/* * Copyright 2009 Advanced Micro Devices, Inc. * * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation on the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) OR * AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Author: Alex Deucher * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* Driver data structures */ #include "radeon.h" #include "radeon_reg.h" #include "radeon_macros.h" #include "radeon_atombios.h" #include "ati_pciids_gen.h" /* 10 khz */ static uint32_t calc_eng_mem_clock(ScrnInfoPtr pScrn, uint32_t req_clock, int ref_div, int *fb_div, int *post_div) { RADEONInfoPtr info = RADEONPTR(pScrn); RADEONPLLPtr pll = &info->pll; if (req_clock < 15000) { *post_div = 8; req_clock *= 8; } else if (req_clock < 30000) { *post_div = 4; req_clock *= 4; } else if (req_clock < 60000) { *post_div = 2; req_clock *= 2; } else *post_div = 1; req_clock *= ref_div; req_clock += pll->reference_freq; req_clock /= (2 * pll->reference_freq); *fb_div = req_clock & 0xff; req_clock = (req_clock & 0xffff) << 1; req_clock *= pll->reference_freq; req_clock /= ref_div; req_clock /= *post_div; return req_clock; } static void RADEONSetEngineClock(ScrnInfoPtr pScrn, uint32_t eng_clock) { uint32_t tmp; int ref_div, fb_div, post_div; RADEONWaitForIdleMMIO(pScrn); tmp = INPLL(pScrn, RADEON_M_SPLL_REF_FB_DIV); ref_div = tmp & RADEON_M_SPLL_REF_DIV_MASK; eng_clock = calc_eng_mem_clock(pScrn, eng_clock, ref_div, &fb_div, &post_div); tmp = INPLL(pScrn, RADEON_CLK_PIN_CNTL); tmp &= ~RADEON_DONT_USE_XTALIN; OUTPLL(pScrn, RADEON_CLK_PIN_CNTL, tmp); tmp = INPLL(pScrn, RADEON_SCLK_CNTL); tmp &= ~RADEON_SCLK_SRC_SEL_MASK; OUTPLL(pScrn, RADEON_SCLK_CNTL, tmp); usleep(10); tmp = INPLL(pScrn, RADEON_SPLL_CNTL); tmp |= RADEON_SPLL_SLEEP; OUTPLL(pScrn, RADEON_SPLL_CNTL, tmp); usleep(2); tmp = INPLL(pScrn, RADEON_SPLL_CNTL); tmp |= RADEON_SPLL_RESET; OUTPLL(pScrn, RADEON_SPLL_CNTL, tmp); usleep(200); tmp = INPLL(pScrn, RADEON_M_SPLL_REF_FB_DIV); tmp &= ~(RADEON_SPLL_FB_DIV_MASK << RADEON_SPLL_FB_DIV_SHIFT); tmp |= (fb_div & RADEON_SPLL_FB_DIV_MASK) << RADEON_SPLL_FB_DIV_SHIFT; OUTPLL(pScrn, RADEON_M_SPLL_REF_FB_DIV, tmp); /* XXX: verify on different asics */ tmp = INPLL(pScrn, RADEON_SPLL_CNTL); tmp &= ~RADEON_SPLL_PVG_MASK; if ((eng_clock * post_div) >= 90000) tmp |= (0x7 << RADEON_SPLL_PVG_SHIFT); else tmp |= (0x4 << RADEON_SPLL_PVG_SHIFT); OUTPLL(pScrn, RADEON_SPLL_CNTL, tmp); tmp = INPLL(pScrn, RADEON_SPLL_CNTL); tmp &= ~RADEON_SPLL_SLEEP; OUTPLL(pScrn, RADEON_SPLL_CNTL, tmp); usleep(2); tmp = INPLL(pScrn, RADEON_SPLL_CNTL); tmp &= ~RADEON_SPLL_RESET; OUTPLL(pScrn, RADEON_SPLL_CNTL, tmp); usleep(200); tmp = INPLL(pScrn, RADEON_SCLK_CNTL); tmp &= ~RADEON_SCLK_SRC_SEL_MASK; switch (post_div) { case 1: default: tmp |= 1; break; case 2: tmp |= 2; break; case 4: tmp |= 3; break; case 8: tmp |= 4; break; } OUTPLL(pScrn, RADEON_SCLK_CNTL, tmp); usleep(20); tmp = INPLL(pScrn, RADEON_CLK_PIN_CNTL); tmp |= RADEON_DONT_USE_XTALIN; OUTPLL(pScrn, RADEON_CLK_PIN_CNTL, tmp); usleep(10); } static void LegacySetClockGating(ScrnInfoPtr pScrn, Bool enable) { RADEONInfoPtr info = RADEONPTR(pScrn); RADEONEntPtr pRADEONEnt = RADEONEntPriv(pScrn); unsigned char *RADEONMMIO = info->MMIO; uint32_t tmp; if (enable) { if (!pRADEONEnt->HasCRTC2) { tmp = INPLL(pScrn, RADEON_SCLK_CNTL); if ((INREG(RADEON_CONFIG_CNTL) & RADEON_CFG_ATI_REV_ID_MASK) > RADEON_CFG_ATI_REV_A13) { tmp &= ~(RADEON_SCLK_FORCE_CP | RADEON_SCLK_FORCE_RB); } tmp &= ~(RADEON_SCLK_FORCE_HDP | RADEON_SCLK_FORCE_DISP1 | RADEON_SCLK_FORCE_TOP | RADEON_SCLK_FORCE_SE | RADEON_SCLK_FORCE_IDCT | RADEON_SCLK_FORCE_RE | RADEON_SCLK_FORCE_PB | RADEON_SCLK_FORCE_TAM | RADEON_SCLK_FORCE_TDM); OUTPLL(pScrn, RADEON_SCLK_CNTL, tmp); } else if (IS_R300_VARIANT) { if ((info->ChipFamily == CHIP_FAMILY_RS400) || (info->ChipFamily == CHIP_FAMILY_RS480)) { tmp = INPLL(pScrn, RADEON_SCLK_CNTL); tmp &= ~(RADEON_SCLK_FORCE_DISP2 | RADEON_SCLK_FORCE_CP | RADEON_SCLK_FORCE_HDP | RADEON_SCLK_FORCE_DISP1 | RADEON_SCLK_FORCE_TOP | RADEON_SCLK_FORCE_E2 | R300_SCLK_FORCE_VAP | RADEON_SCLK_FORCE_IDCT | RADEON_SCLK_FORCE_VIP | R300_SCLK_FORCE_SR | R300_SCLK_FORCE_PX | R300_SCLK_FORCE_TX | R300_SCLK_FORCE_US | RADEON_SCLK_FORCE_TV_SCLK | R300_SCLK_FORCE_SU | RADEON_SCLK_FORCE_OV0); tmp |= RADEON_DYN_STOP_LAT_MASK; tmp |= RADEON_SCLK_FORCE_TOP | RADEON_SCLK_FORCE_VIP; OUTPLL(pScrn, RADEON_SCLK_CNTL, tmp); tmp = INPLL(pScrn, RADEON_SCLK_MORE_CNTL); tmp &= ~RADEON_SCLK_MORE_FORCEON; tmp |= RADEON_SCLK_MORE_MAX_DYN_STOP_LAT; OUTPLL(pScrn, RADEON_SCLK_MORE_CNTL, tmp); tmp = INPLL(pScrn, RADEON_VCLK_ECP_CNTL); tmp |= (RADEON_PIXCLK_ALWAYS_ONb | RADEON_PIXCLK_DAC_ALWAYS_ONb); OUTPLL(pScrn, RADEON_VCLK_ECP_CNTL, tmp); tmp = INPLL(pScrn, RADEON_PIXCLKS_CNTL); tmp |= (RADEON_PIX2CLK_ALWAYS_ONb | RADEON_PIX2CLK_DAC_ALWAYS_ONb | RADEON_DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb | R300_DVOCLK_ALWAYS_ONb | RADEON_PIXCLK_BLEND_ALWAYS_ONb | RADEON_PIXCLK_GV_ALWAYS_ONb | R300_PIXCLK_DVO_ALWAYS_ONb | RADEON_PIXCLK_LVDS_ALWAYS_ONb | RADEON_PIXCLK_TMDS_ALWAYS_ONb | R300_PIXCLK_TRANS_ALWAYS_ONb | R300_PIXCLK_TVO_ALWAYS_ONb | R300_P2G2CLK_ALWAYS_ONb | R300_P2G2CLK_ALWAYS_ONb); OUTPLL(pScrn, RADEON_PIXCLKS_CNTL, tmp); } else if (info->ChipFamily >= CHIP_FAMILY_RV350) { tmp = INPLL(pScrn, R300_SCLK_CNTL2); tmp &= ~(R300_SCLK_FORCE_TCL | R300_SCLK_FORCE_GA | R300_SCLK_FORCE_CBA); tmp |= (R300_SCLK_TCL_MAX_DYN_STOP_LAT | R300_SCLK_GA_MAX_DYN_STOP_LAT | R300_SCLK_CBA_MAX_DYN_STOP_LAT); OUTPLL(pScrn, R300_SCLK_CNTL2, tmp); tmp = INPLL(pScrn, RADEON_SCLK_CNTL); tmp &= ~(RADEON_SCLK_FORCE_DISP2 | RADEON_SCLK_FORCE_CP | RADEON_SCLK_FORCE_HDP | RADEON_SCLK_FORCE_DISP1 | RADEON_SCLK_FORCE_TOP | RADEON_SCLK_FORCE_E2 | R300_SCLK_FORCE_VAP | RADEON_SCLK_FORCE_IDCT | RADEON_SCLK_FORCE_VIP | R300_SCLK_FORCE_SR | R300_SCLK_FORCE_PX | R300_SCLK_FORCE_TX | R300_SCLK_FORCE_US | RADEON_SCLK_FORCE_TV_SCLK | R300_SCLK_FORCE_SU | RADEON_SCLK_FORCE_OV0); tmp |= RADEON_DYN_STOP_LAT_MASK; OUTPLL(pScrn, RADEON_SCLK_CNTL, tmp); tmp = INPLL(pScrn, RADEON_SCLK_MORE_CNTL); tmp &= ~RADEON_SCLK_MORE_FORCEON; tmp |= RADEON_SCLK_MORE_MAX_DYN_STOP_LAT; OUTPLL(pScrn, RADEON_SCLK_MORE_CNTL, tmp); tmp = INPLL(pScrn, RADEON_VCLK_ECP_CNTL); tmp |= (RADEON_PIXCLK_ALWAYS_ONb | RADEON_PIXCLK_DAC_ALWAYS_ONb); OUTPLL(pScrn, RADEON_VCLK_ECP_CNTL, tmp); tmp = INPLL(pScrn, RADEON_PIXCLKS_CNTL); tmp |= (RADEON_PIX2CLK_ALWAYS_ONb | RADEON_PIX2CLK_DAC_ALWAYS_ONb | RADEON_DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb | R300_DVOCLK_ALWAYS_ONb | RADEON_PIXCLK_BLEND_ALWAYS_ONb | RADEON_PIXCLK_GV_ALWAYS_ONb | R300_PIXCLK_DVO_ALWAYS_ONb | RADEON_PIXCLK_LVDS_ALWAYS_ONb | RADEON_PIXCLK_TMDS_ALWAYS_ONb | R300_PIXCLK_TRANS_ALWAYS_ONb | R300_PIXCLK_TVO_ALWAYS_ONb | R300_P2G2CLK_ALWAYS_ONb | R300_P2G2CLK_ALWAYS_ONb); OUTPLL(pScrn, RADEON_PIXCLKS_CNTL, tmp); tmp = INPLL(pScrn, RADEON_MCLK_MISC); tmp |= (RADEON_MC_MCLK_DYN_ENABLE | RADEON_IO_MCLK_DYN_ENABLE); OUTPLL(pScrn, RADEON_MCLK_MISC, tmp); tmp = INPLL(pScrn, RADEON_MCLK_CNTL); tmp |= (RADEON_FORCEON_MCLKA | RADEON_FORCEON_MCLKB); tmp &= ~(RADEON_FORCEON_YCLKA | RADEON_FORCEON_YCLKB | RADEON_FORCEON_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 & R300_DISABLE_MC_MCLKA) && (tmp & R300_DISABLE_MC_MCLKB)) { /* If both bits are set, then check the active channels */ tmp = INPLL(pScrn, RADEON_MCLK_CNTL); if (info->RamWidth == 64) { if (INREG(RADEON_MEM_CNTL) & R300_MEM_USE_CD_CH_ONLY) tmp &= ~R300_DISABLE_MC_MCLKB; else tmp &= ~R300_DISABLE_MC_MCLKA; } else { tmp &= ~(R300_DISABLE_MC_MCLKA | R300_DISABLE_MC_MCLKB); } } OUTPLL(pScrn, RADEON_MCLK_CNTL, tmp); } else { tmp = INPLL(pScrn, RADEON_SCLK_CNTL); tmp &= ~(R300_SCLK_FORCE_VAP); tmp |= RADEON_SCLK_FORCE_CP; OUTPLL(pScrn, RADEON_SCLK_CNTL, tmp); usleep(15000); tmp = INPLL(pScrn, R300_SCLK_CNTL2); tmp &= ~(R300_SCLK_FORCE_TCL | R300_SCLK_FORCE_GA | R300_SCLK_FORCE_CBA); OUTPLL(pScrn, R300_SCLK_CNTL2, tmp); } } else { tmp = INPLL(pScrn, RADEON_CLK_PWRMGT_CNTL); tmp &= ~(RADEON_ACTIVE_HILO_LAT_MASK | RADEON_DISP_DYN_STOP_LAT_MASK | RADEON_DYN_STOP_MODE_MASK); tmp |= (RADEON_ENGIN_DYNCLK_MODE | (0x01 << RADEON_ACTIVE_HILO_LAT_SHIFT)); OUTPLL(pScrn, RADEON_CLK_PWRMGT_CNTL, tmp); usleep(15000); tmp = INPLL(pScrn, RADEON_CLK_PIN_CNTL); tmp |= RADEON_SCLK_DYN_START_CNTL; OUTPLL(pScrn, RADEON_CLK_PIN_CNTL, tmp); usleep(15000); /* 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(pScrn, RADEON_SCLK_CNTL); /*tmp &= RADEON_SCLK_SRC_SEL_MASK;*/ tmp &= ~RADEON_SCLK_FORCEON_MASK; /*RAGE_6::A11 A12 A12N1 A13, RV250::A11 A12, R300*/ if (((info->ChipFamily == CHIP_FAMILY_RV250) && ((INREG(RADEON_CONFIG_CNTL) & RADEON_CFG_ATI_REV_ID_MASK) < RADEON_CFG_ATI_REV_A13)) || ((info->ChipFamily == CHIP_FAMILY_RV100) && ((INREG(RADEON_CONFIG_CNTL) & RADEON_CFG_ATI_REV_ID_MASK) <= RADEON_CFG_ATI_REV_A13))) { tmp |= RADEON_SCLK_FORCE_CP; tmp |= RADEON_SCLK_FORCE_VIP; } OUTPLL(pScrn, RADEON_SCLK_CNTL, tmp); if ((info->ChipFamily == CHIP_FAMILY_RV200) || (info->ChipFamily == CHIP_FAMILY_RV250) || (info->ChipFamily == CHIP_FAMILY_RV280)) { tmp = INPLL(pScrn, RADEON_SCLK_MORE_CNTL); tmp &= ~RADEON_SCLK_MORE_FORCEON; /* RV200::A11 A12 RV250::A11 A12 */ if (((info->ChipFamily == CHIP_FAMILY_RV200) || (info->ChipFamily == CHIP_FAMILY_RV250)) && ((INREG(RADEON_CONFIG_CNTL) & RADEON_CFG_ATI_REV_ID_MASK) < RADEON_CFG_ATI_REV_A13)) { tmp |= RADEON_SCLK_MORE_FORCEON; } OUTPLL(pScrn, RADEON_SCLK_MORE_CNTL, tmp); usleep(15000); } /* RV200::A11 A12, RV250::A11 A12 */ if (((info->ChipFamily == CHIP_FAMILY_RV200) || (info->ChipFamily == CHIP_FAMILY_RV250)) && ((INREG(RADEON_CONFIG_CNTL) & RADEON_CFG_ATI_REV_ID_MASK) < RADEON_CFG_ATI_REV_A13)) { tmp = INPLL(pScrn, RADEON_PLL_PWRMGT_CNTL); tmp |= RADEON_TCL_BYPASS_DISABLE; OUTPLL(pScrn, RADEON_PLL_PWRMGT_CNTL, tmp); } usleep(15000); /*enable dynamic mode for display clocks (PIXCLK and PIX2CLK)*/ tmp = INPLL(pScrn, RADEON_PIXCLKS_CNTL); tmp |= (RADEON_PIX2CLK_ALWAYS_ONb | RADEON_PIX2CLK_DAC_ALWAYS_ONb | RADEON_PIXCLK_BLEND_ALWAYS_ONb | RADEON_PIXCLK_GV_ALWAYS_ONb | RADEON_PIXCLK_DIG_TMDS_ALWAYS_ONb | RADEON_PIXCLK_LVDS_ALWAYS_ONb | RADEON_PIXCLK_TMDS_ALWAYS_ONb); OUTPLL(pScrn, RADEON_PIXCLKS_CNTL, tmp); usleep(15000); tmp = INPLL(pScrn, RADEON_VCLK_ECP_CNTL); tmp |= (RADEON_PIXCLK_ALWAYS_ONb | RADEON_PIXCLK_DAC_ALWAYS_ONb); OUTPLL(pScrn, RADEON_VCLK_ECP_CNTL, tmp); usleep(15000); } } else { /* Turn everything OFF (ForceON to everything)*/ if ( !pRADEONEnt->HasCRTC2 ) { tmp = INPLL(pScrn, RADEON_SCLK_CNTL); tmp |= (RADEON_SCLK_FORCE_CP | RADEON_SCLK_FORCE_HDP | RADEON_SCLK_FORCE_DISP1 | RADEON_SCLK_FORCE_TOP | RADEON_SCLK_FORCE_E2 | RADEON_SCLK_FORCE_SE | RADEON_SCLK_FORCE_IDCT | RADEON_SCLK_FORCE_VIP | RADEON_SCLK_FORCE_RE | RADEON_SCLK_FORCE_PB | RADEON_SCLK_FORCE_TAM | RADEON_SCLK_FORCE_TDM | RADEON_SCLK_FORCE_RB); OUTPLL(pScrn, RADEON_SCLK_CNTL, tmp); } else if ((info->ChipFamily == CHIP_FAMILY_RS400) || (info->ChipFamily == CHIP_FAMILY_RS480)) { tmp = INPLL(pScrn, RADEON_SCLK_CNTL); tmp |= (RADEON_SCLK_FORCE_DISP2 | RADEON_SCLK_FORCE_CP | RADEON_SCLK_FORCE_HDP | RADEON_SCLK_FORCE_DISP1 | RADEON_SCLK_FORCE_TOP | RADEON_SCLK_FORCE_E2 | R300_SCLK_FORCE_VAP | RADEON_SCLK_FORCE_IDCT | RADEON_SCLK_FORCE_VIP | R300_SCLK_FORCE_SR | R300_SCLK_FORCE_PX | R300_SCLK_FORCE_TX | R300_SCLK_FORCE_US | RADEON_SCLK_FORCE_TV_SCLK | R300_SCLK_FORCE_SU | RADEON_SCLK_FORCE_OV0); OUTPLL(pScrn, RADEON_SCLK_CNTL, tmp); tmp = INPLL(pScrn, RADEON_SCLK_MORE_CNTL); tmp |= RADEON_SCLK_MORE_FORCEON; OUTPLL(pScrn, RADEON_SCLK_MORE_CNTL, tmp); tmp = INPLL(pScrn, RADEON_VCLK_ECP_CNTL); tmp &= ~(RADEON_PIXCLK_ALWAYS_ONb | RADEON_PIXCLK_DAC_ALWAYS_ONb | R300_DISP_DAC_PIXCLK_DAC_BLANK_OFF); OUTPLL(pScrn, RADEON_VCLK_ECP_CNTL, tmp); tmp = INPLL(pScrn, RADEON_PIXCLKS_CNTL); tmp &= ~(RADEON_PIX2CLK_ALWAYS_ONb | RADEON_PIX2CLK_DAC_ALWAYS_ONb | RADEON_DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb | R300_DVOCLK_ALWAYS_ONb | RADEON_PIXCLK_BLEND_ALWAYS_ONb | RADEON_PIXCLK_GV_ALWAYS_ONb | R300_PIXCLK_DVO_ALWAYS_ONb | RADEON_PIXCLK_LVDS_ALWAYS_ONb | RADEON_PIXCLK_TMDS_ALWAYS_ONb | R300_PIXCLK_TRANS_ALWAYS_ONb | R300_PIXCLK_TVO_ALWAYS_ONb | R300_P2G2CLK_ALWAYS_ONb | R300_P2G2CLK_ALWAYS_ONb | R300_DISP_DAC_PIXCLK_DAC2_BLANK_OFF); OUTPLL(pScrn, RADEON_PIXCLKS_CNTL, tmp); } else if (info->ChipFamily >= CHIP_FAMILY_RV350) { /* for RV350/M10, no delays are required. */ tmp = INPLL(pScrn, R300_SCLK_CNTL2); tmp |= (R300_SCLK_FORCE_TCL | R300_SCLK_FORCE_GA | R300_SCLK_FORCE_CBA); OUTPLL(pScrn, R300_SCLK_CNTL2, tmp); tmp = INPLL(pScrn, RADEON_SCLK_CNTL); tmp |= (RADEON_SCLK_FORCE_DISP2 | RADEON_SCLK_FORCE_CP | RADEON_SCLK_FORCE_HDP | RADEON_SCLK_FORCE_DISP1 | RADEON_SCLK_FORCE_TOP | RADEON_SCLK_FORCE_E2 | R300_SCLK_FORCE_VAP | RADEON_SCLK_FORCE_IDCT | RADEON_SCLK_FORCE_VIP | R300_SCLK_FORCE_SR | R300_SCLK_FORCE_PX | R300_SCLK_FORCE_TX | R300_SCLK_FORCE_US | RADEON_SCLK_FORCE_TV_SCLK | R300_SCLK_FORCE_SU | RADEON_SCLK_FORCE_OV0); OUTPLL(pScrn, RADEON_SCLK_CNTL, tmp); tmp = INPLL(pScrn, RADEON_SCLK_MORE_CNTL); tmp |= RADEON_SCLK_MORE_FORCEON; OUTPLL(pScrn, RADEON_SCLK_MORE_CNTL, tmp); tmp = INPLL(pScrn, RADEON_MCLK_CNTL); tmp |= (RADEON_FORCEON_MCLKA | RADEON_FORCEON_MCLKB | RADEON_FORCEON_YCLKA | RADEON_FORCEON_YCLKB | RADEON_FORCEON_MC); OUTPLL(pScrn, RADEON_MCLK_CNTL, tmp); tmp = INPLL(pScrn, RADEON_VCLK_ECP_CNTL); tmp &= ~(RADEON_PIXCLK_ALWAYS_ONb | RADEON_PIXCLK_DAC_ALWAYS_ONb | R300_DISP_DAC_PIXCLK_DAC_BLANK_OFF); OUTPLL(pScrn, RADEON_VCLK_ECP_CNTL, tmp); tmp = INPLL(pScrn, RADEON_PIXCLKS_CNTL); tmp &= ~(RADEON_PIX2CLK_ALWAYS_ONb | RADEON_PIX2CLK_DAC_ALWAYS_ONb | RADEON_DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb | R300_DVOCLK_ALWAYS_ONb | RADEON_PIXCLK_BLEND_ALWAYS_ONb | RADEON_PIXCLK_GV_ALWAYS_ONb | R300_PIXCLK_DVO_ALWAYS_ONb | RADEON_PIXCLK_LVDS_ALWAYS_ONb | RADEON_PIXCLK_TMDS_ALWAYS_ONb | R300_PIXCLK_TRANS_ALWAYS_ONb | R300_PIXCLK_TVO_ALWAYS_ONb | R300_P2G2CLK_ALWAYS_ONb | R300_P2G2CLK_ALWAYS_ONb | R300_DISP_DAC_PIXCLK_DAC2_BLANK_OFF); OUTPLL(pScrn, RADEON_PIXCLKS_CNTL, tmp); } else { tmp = INPLL(pScrn, RADEON_SCLK_CNTL); tmp |= (RADEON_SCLK_FORCE_CP | RADEON_SCLK_FORCE_E2); tmp |= RADEON_SCLK_FORCE_SE; if ( !pRADEONEnt->HasCRTC2 ) { tmp |= ( RADEON_SCLK_FORCE_RB | RADEON_SCLK_FORCE_TDM | RADEON_SCLK_FORCE_TAM | RADEON_SCLK_FORCE_PB | RADEON_SCLK_FORCE_RE | RADEON_SCLK_FORCE_VIP | RADEON_SCLK_FORCE_IDCT | RADEON_SCLK_FORCE_TOP | RADEON_SCLK_FORCE_DISP1 | RADEON_SCLK_FORCE_DISP2 | RADEON_SCLK_FORCE_HDP ); } else if ((info->ChipFamily == CHIP_FAMILY_R300) || (info->ChipFamily == CHIP_FAMILY_R350)) { tmp |= ( RADEON_SCLK_FORCE_HDP | RADEON_SCLK_FORCE_DISP1 | RADEON_SCLK_FORCE_DISP2 | RADEON_SCLK_FORCE_TOP | RADEON_SCLK_FORCE_IDCT | RADEON_SCLK_FORCE_VIP); } OUTPLL(pScrn, RADEON_SCLK_CNTL, tmp); usleep(16000); if ((info->ChipFamily == CHIP_FAMILY_R300) || (info->ChipFamily == CHIP_FAMILY_R350)) { tmp = INPLL(pScrn, R300_SCLK_CNTL2); tmp |= ( R300_SCLK_FORCE_TCL | R300_SCLK_FORCE_GA | R300_SCLK_FORCE_CBA); OUTPLL(pScrn, R300_SCLK_CNTL2, tmp); usleep(16000); } if (info->IsIGP) { tmp = INPLL(pScrn, RADEON_MCLK_CNTL); tmp &= ~(RADEON_FORCEON_MCLKA | RADEON_FORCEON_YCLKA); OUTPLL(pScrn, RADEON_MCLK_CNTL, tmp); usleep(16000); } if ((info->ChipFamily == CHIP_FAMILY_RV200) || (info->ChipFamily == CHIP_FAMILY_RV250) || (info->ChipFamily == CHIP_FAMILY_RV280)) { tmp = INPLL(pScrn, RADEON_SCLK_MORE_CNTL); tmp |= RADEON_SCLK_MORE_FORCEON; OUTPLL(pScrn, RADEON_SCLK_MORE_CNTL, tmp); usleep(16000); } tmp = INPLL(pScrn, RADEON_PIXCLKS_CNTL); tmp &= ~(RADEON_PIX2CLK_ALWAYS_ONb | RADEON_PIX2CLK_DAC_ALWAYS_ONb | RADEON_PIXCLK_BLEND_ALWAYS_ONb | RADEON_PIXCLK_GV_ALWAYS_ONb | RADEON_PIXCLK_DIG_TMDS_ALWAYS_ONb | RADEON_PIXCLK_LVDS_ALWAYS_ONb | RADEON_PIXCLK_TMDS_ALWAYS_ONb); OUTPLL(pScrn, RADEON_PIXCLKS_CNTL, tmp); usleep(16000); tmp = INPLL(pScrn, RADEON_VCLK_ECP_CNTL); tmp &= ~(RADEON_PIXCLK_ALWAYS_ONb | RADEON_PIXCLK_DAC_ALWAYS_ONb); OUTPLL(pScrn, RADEON_VCLK_ECP_CNTL, tmp); } } } static void RADEONPMQuirks(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); uint32_t tmp; RADEONWaitForIdleMMIO(pScrn); if (info->ChipFamily < CHIP_FAMILY_RV515) { tmp = INPLL(pScrn, RADEON_SCLK_CNTL); if (IS_R300_VARIANT || IS_RV100_VARIANT) tmp |= RADEON_SCLK_FORCE_CP | RADEON_SCLK_FORCE_VIP; if ((info->ChipFamily == CHIP_FAMILY_RV250) || (info->ChipFamily == CHIP_FAMILY_RV280)) tmp |= RADEON_SCLK_FORCE_DISP1 | RADEON_SCLK_FORCE_DISP2; if ((info->ChipFamily == CHIP_FAMILY_RV350) || (info->ChipFamily == CHIP_FAMILY_RV380)) tmp |= R300_SCLK_FORCE_VAP; if (info->ChipFamily == CHIP_FAMILY_R420) tmp |= R300_SCLK_FORCE_PX | R300_SCLK_FORCE_TX; OUTPLL(pScrn, RADEON_SCLK_CNTL, tmp); } else if (info->ChipFamily < CHIP_FAMILY_R600) { tmp = INPLL(pScrn, AVIVO_CP_DYN_CNTL); tmp |= AVIVO_CP_FORCEON; OUTPLL(pScrn, AVIVO_CP_DYN_CNTL, tmp); tmp = INPLL(pScrn, AVIVO_E2_DYN_CNTL); tmp |= AVIVO_E2_FORCEON; OUTPLL(pScrn, AVIVO_E2_DYN_CNTL, tmp); tmp = INPLL(pScrn, AVIVO_IDCT_DYN_CNTL); tmp |= AVIVO_IDCT_FORCEON; OUTPLL(pScrn, AVIVO_IDCT_DYN_CNTL, tmp); } } static void RADEONSetPCIELanes(ScrnInfoPtr pScrn, int lanes) { RADEONInfoPtr info = RADEONPTR(pScrn); unsigned char *RADEONMMIO = info->MMIO; uint32_t link_width_cntl, mask, target_reg; if (info->IsIGP) return; /* don't change lanes on multi-gpu cards for now */ if ((info->Chipset == PCI_CHIP_RV770_9441) || (info->Chipset == PCI_CHIP_RV770_9443) || (info->Chipset == PCI_CHIP_RV770_944B) || (info->Chipset == PCI_CHIP_RV670_9506) || (info->Chipset == PCI_CHIP_RV670_9509) || (info->Chipset == PCI_CHIP_RV670_950F)) return; RADEONWaitForIdleMMIO(pScrn); switch (lanes) { case 0: mask = RADEON_PCIE_LC_LINK_WIDTH_X0; break; case 1: mask = RADEON_PCIE_LC_LINK_WIDTH_X1; break; case 2: mask = RADEON_PCIE_LC_LINK_WIDTH_X2; break; case 4: mask = RADEON_PCIE_LC_LINK_WIDTH_X4; break; case 8: mask = RADEON_PCIE_LC_LINK_WIDTH_X8; break; case 12: mask = RADEON_PCIE_LC_LINK_WIDTH_X12; break; case 16: default: mask = RADEON_PCIE_LC_LINK_WIDTH_X16; break; } if (info->ChipFamily >= CHIP_FAMILY_R600) { link_width_cntl = INPCIE_P(pScrn, RADEON_PCIE_LC_LINK_WIDTH_CNTL); if ((link_width_cntl & RADEON_PCIE_LC_LINK_WIDTH_RD_MASK) == (mask << RADEON_PCIE_LC_LINK_WIDTH_RD_SHIFT)) return; link_width_cntl &= ~(RADEON_PCIE_LC_LINK_WIDTH_MASK | RADEON_PCIE_LC_RECONFIG_NOW | R600_PCIE_LC_RECONFIG_ARC_MISSING_ESCAPE | R600_PCIE_LC_SHORT_RECONFIG_EN | R600_PCIE_LC_RENEGOTIATE_EN); link_width_cntl |= mask; #if 0 /* some northbridges can renegotiate the link rather than requiring * a complete re-config. * e.g., AMD 780/790 northbridges (pci ids: 0x5956, 0x5957, 0x5958, etc.) */ if (northbridge can renegotiate) link_width_cntl |= R600_PCIE_LC_RENEGOTIATE_EN; else #endif link_width_cntl |= R600_PCIE_LC_RECONFIG_ARC_MISSING_ESCAPE; OUTPCIE_P(pScrn, RADEON_PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); OUTPCIE_P(pScrn, RADEON_PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl | RADEON_PCIE_LC_RECONFIG_NOW); if (info->ChipFamily >= CHIP_FAMILY_RV770) target_reg = R700_TARGET_AND_CURRENT_PROFILE_INDEX; else target_reg = R600_TARGET_AND_CURRENT_PROFILE_INDEX; /* wait for lane set to complete */ link_width_cntl = INREG(target_reg); while (link_width_cntl == 0xffffffff) link_width_cntl = INREG(target_reg); } else { link_width_cntl = INPCIE(pScrn, RADEON_PCIE_LC_LINK_WIDTH_CNTL); if ((link_width_cntl & RADEON_PCIE_LC_LINK_WIDTH_RD_MASK) == (mask << RADEON_PCIE_LC_LINK_WIDTH_RD_SHIFT)) return; link_width_cntl &= ~(RADEON_PCIE_LC_LINK_WIDTH_MASK | RADEON_PCIE_LC_RECONFIG_NOW | RADEON_PCIE_LC_RECONFIG_LATER | RADEON_PCIE_LC_SHORT_RECONFIG_EN); link_width_cntl |= mask; OUTPCIE(pScrn, RADEON_PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl); OUTPCIE(pScrn, RADEON_PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl | RADEON_PCIE_LC_RECONFIG_NOW); /* wait for lane set to complete */ link_width_cntl = INPCIE(pScrn, RADEON_PCIE_LC_LINK_WIDTH_CNTL); while (link_width_cntl == 0xffffffff) link_width_cntl = INPCIE(pScrn, RADEON_PCIE_LC_LINK_WIDTH_CNTL); } } static void RADEONSetClockGating(ScrnInfoPtr pScrn, Bool enable) { RADEONInfoPtr info = RADEONPTR(pScrn); RADEONWaitForIdleMMIO(pScrn); if (info->ChipFamily >= CHIP_FAMILY_R600) atombios_static_pwrmgt_setup(pScrn, enable); else { if (info->IsAtomBios) { atombios_static_pwrmgt_setup(pScrn, enable); atombios_clk_gating_setup(pScrn, enable); } else if (info->IsMobility) LegacySetClockGating(pScrn, enable); } xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Dynamic Clock Gating %sabled\n", enable ? "En" : "Dis"); } static void RADEONSetStaticPowerMode(ScrnInfoPtr pScrn, RADEONPMType type) { RADEONInfoPtr info = RADEONPTR(pScrn); int i; for (i = 0; i < info->pm.num_modes; i++) { if (info->pm.mode[i].type == type) break; } if (i == info->pm.num_modes) return; if (i == info->pm.current_mode) return; RADEONWaitForIdleMMIO(pScrn); if (info->IsAtomBios) atombios_set_engine_clock(pScrn, info->pm.mode[i].sclk); else RADEONSetEngineClock(pScrn, info->pm.mode[i].sclk); if (info->cardType == CARD_PCIE) RADEONSetPCIELanes(pScrn, info->pm.mode[i].pcie_lanes); info->pm.current_mode = i; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Power Mode Switch\n"); } void RADEONPMInit(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); if (xf86ReturnOptValBool(info->Options, OPTION_CLOCK_GATING, FALSE)) { info->pm.clock_gating_enabled = TRUE; RADEONSetClockGating(pScrn, info->pm.clock_gating_enabled); } else info->pm.clock_gating_enabled = FALSE; info->pm.mode[0].type = POWER_DEFAULT; info->pm.mode[0].sclk = (uint32_t)info->sclk * 100; /* 10 khz */ info->pm.mode[0].mclk = (uint32_t)info->mclk * 100; /* 10 khz */ info->pm.mode[0].pcie_lanes = 16; /* XXX: read back current lane config */ info->pm.current_mode = 0; info->pm.num_modes = 1; if (xf86ReturnOptValBool(info->Options, OPTION_DYNAMIC_PM, FALSE)) { info->pm.dynamic_mode_enabled = TRUE; info->pm.mode[1].type = POWER_LOW; info->pm.mode[1].sclk = info->pm.mode[0].sclk / 4; info->pm.mode[1].mclk = info->pm.mode[0].mclk / 4; info->pm.mode[1].pcie_lanes = 1; info->pm.mode[2].type = POWER_HIGH; info->pm.mode[2].sclk = info->pm.mode[0].sclk; info->pm.mode[2].mclk = info->pm.mode[0].mclk; info->pm.mode[2].pcie_lanes = 16; info->pm.num_modes += 2; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Dynamic Power Management Enabled\n"); } else { info->pm.dynamic_mode_enabled = FALSE; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Dynamic Power Management Disabled\n"); } if (xf86ReturnOptValBool(info->Options, OPTION_FORCE_LOW_POWER, FALSE)) { xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Force Low Power Mode Enabled\n"); info->pm.force_low_power_enabled = TRUE; if (info->pm.dynamic_mode_enabled) { info->pm.mode[2].type = POWER_HIGH; info->pm.mode[2].sclk = info->pm.mode[0].sclk / 2; info->pm.mode[2].mclk = info->pm.mode[0].mclk / 2; info->pm.mode[2].pcie_lanes = 4; } else { info->pm.mode[1].type = POWER_HIGH; info->pm.mode[1].sclk = info->pm.mode[0].sclk / 2; info->pm.mode[1].mclk = info->pm.mode[0].mclk / 2; info->pm.mode[1].pcie_lanes = 4; info->pm.num_modes += 1; } RADEONSetStaticPowerMode(pScrn, POWER_HIGH); } else info->pm.force_low_power_enabled = FALSE; RADEONPMQuirks(pScrn); } void RADEONPMEnterVT(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); if (info->pm.clock_gating_enabled) RADEONSetClockGating(pScrn, info->pm.clock_gating_enabled); RADEONPMQuirks(pScrn); if (info->pm.force_low_power_enabled || info->pm.dynamic_mode_enabled) RADEONSetStaticPowerMode(pScrn, POWER_HIGH); } void RADEONPMLeaveVT(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); if (info->pm.clock_gating_enabled) RADEONSetClockGating(pScrn, FALSE); if (info->pm.force_low_power_enabled || info->pm.dynamic_mode_enabled) RADEONSetStaticPowerMode(pScrn, POWER_DEFAULT); } void RADEONPMFini(ScrnInfoPtr pScrn) { RADEONInfoPtr info = RADEONPTR(pScrn); if (info->pm.clock_gating_enabled) RADEONSetClockGating(pScrn, FALSE); if (info->pm.force_low_power_enabled || info->pm.dynamic_mode_enabled) RADEONSetStaticPowerMode(pScrn, POWER_DEFAULT); } void RADEONPMBlockHandler(ScrnInfoPtr pScrn) { RADEONEntPtr pRADEONEnt = RADEONEntPriv(pScrn); if ((!pRADEONEnt->Controller[0]->enabled) && (!pRADEONEnt->Controller[1]->enabled)) RADEONSetStaticPowerMode(pScrn, POWER_LOW); else RADEONSetStaticPowerMode(pScrn, POWER_HIGH); }