diff options
Diffstat (limited to 'drivers/gpu/drm/radeon/radeon_clocks.c')
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_clocks.c | 833 |
1 files changed, 833 insertions, 0 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_clocks.c b/drivers/gpu/drm/radeon/radeon_clocks.c new file mode 100644 index 00000000000..a37cbce5318 --- /dev/null +++ b/drivers/gpu/drm/radeon/radeon_clocks.c @@ -0,0 +1,833 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Jerome Glisse. + * + * 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 + * 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 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 NONINFRINGEMENT. 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. + * + * Authors: Dave Airlie + * Alex Deucher + * Jerome Glisse + */ +#include "drmP.h" +#include "radeon_drm.h" +#include "radeon_reg.h" +#include "radeon.h" +#include "atom.h" + +/* 10 khz */ +static uint32_t radeon_legacy_get_engine_clock(struct radeon_device *rdev) +{ + struct radeon_pll *spll = &rdev->clock.spll; + uint32_t fb_div, ref_div, post_div, sclk; + + fb_div = RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV); + fb_div = (fb_div >> RADEON_SPLL_FB_DIV_SHIFT) & RADEON_SPLL_FB_DIV_MASK; + fb_div <<= 1; + fb_div *= spll->reference_freq; + + ref_div = + RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV) & RADEON_M_SPLL_REF_DIV_MASK; + sclk = fb_div / ref_div; + + post_div = RREG32_PLL(RADEON_SCLK_CNTL) & RADEON_SCLK_SRC_SEL_MASK; + if (post_div == 2) + sclk >>= 1; + else if (post_div == 3) + sclk >>= 2; + else if (post_div == 4) + sclk >>= 4; + + return sclk; +} + +/* 10 khz */ +static uint32_t radeon_legacy_get_memory_clock(struct radeon_device *rdev) +{ + struct radeon_pll *mpll = &rdev->clock.mpll; + uint32_t fb_div, ref_div, post_div, mclk; + + fb_div = RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV); + fb_div = (fb_div >> RADEON_MPLL_FB_DIV_SHIFT) & RADEON_MPLL_FB_DIV_MASK; + fb_div <<= 1; + fb_div *= mpll->reference_freq; + + ref_div = + RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV) & RADEON_M_SPLL_REF_DIV_MASK; + mclk = fb_div / ref_div; + + post_div = RREG32_PLL(RADEON_MCLK_CNTL) & 0x7; + if (post_div == 2) + mclk >>= 1; + else if (post_div == 3) + mclk >>= 2; + else if (post_div == 4) + mclk >>= 4; + + return mclk; +} + +void radeon_get_clock_info(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + struct radeon_pll *p1pll = &rdev->clock.p1pll; + struct radeon_pll *p2pll = &rdev->clock.p2pll; + struct radeon_pll *spll = &rdev->clock.spll; + struct radeon_pll *mpll = &rdev->clock.mpll; + int ret; + + if (rdev->is_atom_bios) + ret = radeon_atom_get_clock_info(dev); + else + ret = radeon_combios_get_clock_info(dev); + + if (ret) { + if (p1pll->reference_div < 2) + p1pll->reference_div = 12; + if (p2pll->reference_div < 2) + p2pll->reference_div = 12; + if (spll->reference_div < 2) + spll->reference_div = + RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV) & + RADEON_M_SPLL_REF_DIV_MASK; + if (mpll->reference_div < 2) + mpll->reference_div = spll->reference_div; + } else { + if (ASIC_IS_AVIVO(rdev)) { + /* TODO FALLBACK */ + } else { + DRM_INFO("Using generic clock info\n"); + + if (rdev->flags & RADEON_IS_IGP) { + p1pll->reference_freq = 1432; + p2pll->reference_freq = 1432; + spll->reference_freq = 1432; + mpll->reference_freq = 1432; + } else { + p1pll->reference_freq = 2700; + p2pll->reference_freq = 2700; + spll->reference_freq = 2700; + mpll->reference_freq = 2700; + } + p1pll->reference_div = + RREG32_PLL(RADEON_PPLL_REF_DIV) & 0x3ff; + if (p1pll->reference_div < 2) + p1pll->reference_div = 12; + p2pll->reference_div = p1pll->reference_div; + + if (rdev->family >= CHIP_R420) { + p1pll->pll_in_min = 100; + p1pll->pll_in_max = 1350; + p1pll->pll_out_min = 20000; + p1pll->pll_out_max = 50000; + p2pll->pll_in_min = 100; + p2pll->pll_in_max = 1350; + p2pll->pll_out_min = 20000; + p2pll->pll_out_max = 50000; + } else { + p1pll->pll_in_min = 40; + p1pll->pll_in_max = 500; + p1pll->pll_out_min = 12500; + p1pll->pll_out_max = 35000; + p2pll->pll_in_min = 40; + p2pll->pll_in_max = 500; + p2pll->pll_out_min = 12500; + p2pll->pll_out_max = 35000; + } + + spll->reference_div = + RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV) & + RADEON_M_SPLL_REF_DIV_MASK; + mpll->reference_div = spll->reference_div; + rdev->clock.default_sclk = + radeon_legacy_get_engine_clock(rdev); + rdev->clock.default_mclk = + radeon_legacy_get_memory_clock(rdev); + } + } + + /* pixel clocks */ + if (ASIC_IS_AVIVO(rdev)) { + p1pll->min_post_div = 2; + p1pll->max_post_div = 0x7f; + p1pll->min_frac_feedback_div = 0; + p1pll->max_frac_feedback_div = 9; + p2pll->min_post_div = 2; + p2pll->max_post_div = 0x7f; + p2pll->min_frac_feedback_div = 0; + p2pll->max_frac_feedback_div = 9; + } else { + p1pll->min_post_div = 1; + p1pll->max_post_div = 16; + p1pll->min_frac_feedback_div = 0; + p1pll->max_frac_feedback_div = 0; + p2pll->min_post_div = 1; + p2pll->max_post_div = 12; + p2pll->min_frac_feedback_div = 0; + p2pll->max_frac_feedback_div = 0; + } + + p1pll->min_ref_div = 2; + p1pll->max_ref_div = 0x3ff; + p1pll->min_feedback_div = 4; + p1pll->max_feedback_div = 0x7ff; + p1pll->best_vco = 0; + + p2pll->min_ref_div = 2; + p2pll->max_ref_div = 0x3ff; + p2pll->min_feedback_div = 4; + p2pll->max_feedback_div = 0x7ff; + p2pll->best_vco = 0; + + /* system clock */ + spll->min_post_div = 1; + spll->max_post_div = 1; + spll->min_ref_div = 2; + spll->max_ref_div = 0xff; + spll->min_feedback_div = 4; + spll->max_feedback_div = 0xff; + spll->best_vco = 0; + + /* memory clock */ + mpll->min_post_div = 1; + mpll->max_post_div = 1; + mpll->min_ref_div = 2; + mpll->max_ref_div = 0xff; + mpll->min_feedback_div = 4; + mpll->max_feedback_div = 0xff; + mpll->best_vco = 0; + +} + +/* 10 khz */ +static uint32_t calc_eng_mem_clock(struct radeon_device *rdev, + uint32_t req_clock, + int *fb_div, int *post_div) +{ + struct radeon_pll *spll = &rdev->clock.spll; + int ref_div = spll->reference_div; + + if (!ref_div) + ref_div = + RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV) & + RADEON_M_SPLL_REF_DIV_MASK; + + 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 += spll->reference_freq; + req_clock /= (2 * spll->reference_freq); + + *fb_div = req_clock & 0xff; + + req_clock = (req_clock & 0xffff) << 1; + req_clock *= spll->reference_freq; + req_clock /= ref_div; + req_clock /= *post_div; + + return req_clock; +} + +/* 10 khz */ +void radeon_legacy_set_engine_clock(struct radeon_device *rdev, + uint32_t eng_clock) +{ + uint32_t tmp; + int fb_div, post_div; + + /* XXX: wait for idle */ + + eng_clock = calc_eng_mem_clock(rdev, eng_clock, &fb_div, &post_div); + + tmp = RREG32_PLL(RADEON_CLK_PIN_CNTL); + tmp &= ~RADEON_DONT_USE_XTALIN; + WREG32_PLL(RADEON_CLK_PIN_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_SCLK_CNTL); + tmp &= ~RADEON_SCLK_SRC_SEL_MASK; + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + + udelay(10); + + tmp = RREG32_PLL(RADEON_SPLL_CNTL); + tmp |= RADEON_SPLL_SLEEP; + WREG32_PLL(RADEON_SPLL_CNTL, tmp); + + udelay(2); + + tmp = RREG32_PLL(RADEON_SPLL_CNTL); + tmp |= RADEON_SPLL_RESET; + WREG32_PLL(RADEON_SPLL_CNTL, tmp); + + udelay(200); + + tmp = RREG32_PLL(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; + WREG32_PLL(RADEON_M_SPLL_REF_FB_DIV, tmp); + + /* XXX: verify on different asics */ + tmp = RREG32_PLL(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); + WREG32_PLL(RADEON_SPLL_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_SPLL_CNTL); + tmp &= ~RADEON_SPLL_SLEEP; + WREG32_PLL(RADEON_SPLL_CNTL, tmp); + + udelay(2); + + tmp = RREG32_PLL(RADEON_SPLL_CNTL); + tmp &= ~RADEON_SPLL_RESET; + WREG32_PLL(RADEON_SPLL_CNTL, tmp); + + udelay(200); + + tmp = RREG32_PLL(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; + } + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + + udelay(20); + + tmp = RREG32_PLL(RADEON_CLK_PIN_CNTL); + tmp |= RADEON_DONT_USE_XTALIN; + WREG32_PLL(RADEON_CLK_PIN_CNTL, tmp); + + udelay(10); +} + +void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable) +{ + uint32_t tmp; + + if (enable) { + if (rdev->flags & RADEON_SINGLE_CRTC) { + tmp = RREG32_PLL(RADEON_SCLK_CNTL); + if ((RREG32(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); + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + } else if (ASIC_IS_R300(rdev)) { + if ((rdev->family == CHIP_RS400) || + (rdev->family == CHIP_RS480)) { + tmp = RREG32_PLL(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; + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_SCLK_MORE_CNTL); + tmp &= ~RADEON_SCLK_MORE_FORCEON; + tmp |= RADEON_SCLK_MORE_MAX_DYN_STOP_LAT; + WREG32_PLL(RADEON_SCLK_MORE_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_VCLK_ECP_CNTL); + tmp |= (RADEON_PIXCLK_ALWAYS_ONb | + RADEON_PIXCLK_DAC_ALWAYS_ONb); + WREG32_PLL(RADEON_VCLK_ECP_CNTL, tmp); + + tmp = RREG32_PLL(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); + WREG32_PLL(RADEON_PIXCLKS_CNTL, tmp); + } else if (rdev->family >= CHIP_RV350) { + tmp = RREG32_PLL(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); + WREG32_PLL(R300_SCLK_CNTL2, tmp); + + tmp = RREG32_PLL(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; + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_SCLK_MORE_CNTL); + tmp &= ~RADEON_SCLK_MORE_FORCEON; + tmp |= RADEON_SCLK_MORE_MAX_DYN_STOP_LAT; + WREG32_PLL(RADEON_SCLK_MORE_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_VCLK_ECP_CNTL); + tmp |= (RADEON_PIXCLK_ALWAYS_ONb | + RADEON_PIXCLK_DAC_ALWAYS_ONb); + WREG32_PLL(RADEON_VCLK_ECP_CNTL, tmp); + + tmp = RREG32_PLL(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); + WREG32_PLL(RADEON_PIXCLKS_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_MCLK_MISC); + tmp |= (RADEON_MC_MCLK_DYN_ENABLE | + RADEON_IO_MCLK_DYN_ENABLE); + WREG32_PLL(RADEON_MCLK_MISC, tmp); + + tmp = RREG32_PLL(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 = RREG32_PLL(RADEON_MCLK_CNTL); + if (rdev->mc.vram_width == 64) { + if (RREG32(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); + } + } + + WREG32_PLL(RADEON_MCLK_CNTL, tmp); + } else { + tmp = RREG32_PLL(RADEON_SCLK_CNTL); + tmp &= ~(R300_SCLK_FORCE_VAP); + tmp |= RADEON_SCLK_FORCE_CP; + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + udelay(15000); + + tmp = RREG32_PLL(R300_SCLK_CNTL2); + tmp &= ~(R300_SCLK_FORCE_TCL | + R300_SCLK_FORCE_GA | + R300_SCLK_FORCE_CBA); + WREG32_PLL(R300_SCLK_CNTL2, tmp); + } + } else { + tmp = RREG32_PLL(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)); + WREG32_PLL(RADEON_CLK_PWRMGT_CNTL, tmp); + udelay(15000); + + tmp = RREG32_PLL(RADEON_CLK_PIN_CNTL); + tmp |= RADEON_SCLK_DYN_START_CNTL; + WREG32_PLL(RADEON_CLK_PIN_CNTL, tmp); + udelay(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 = RREG32_PLL(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 (((rdev->family == CHIP_RV250) && + ((RREG32(RADEON_CONFIG_CNTL) & + RADEON_CFG_ATI_REV_ID_MASK) < + RADEON_CFG_ATI_REV_A13)) + || ((rdev->family == CHIP_RV100) + && + ((RREG32(RADEON_CONFIG_CNTL) & + RADEON_CFG_ATI_REV_ID_MASK) <= + RADEON_CFG_ATI_REV_A13))) { + tmp |= RADEON_SCLK_FORCE_CP; + tmp |= RADEON_SCLK_FORCE_VIP; + } + + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + + if ((rdev->family == CHIP_RV200) || + (rdev->family == CHIP_RV250) || + (rdev->family == CHIP_RV280)) { + tmp = RREG32_PLL(RADEON_SCLK_MORE_CNTL); + tmp &= ~RADEON_SCLK_MORE_FORCEON; + + /* RV200::A11 A12 RV250::A11 A12 */ + if (((rdev->family == CHIP_RV200) || + (rdev->family == CHIP_RV250)) && + ((RREG32(RADEON_CONFIG_CNTL) & + RADEON_CFG_ATI_REV_ID_MASK) < + RADEON_CFG_ATI_REV_A13)) { + tmp |= RADEON_SCLK_MORE_FORCEON; + } + WREG32_PLL(RADEON_SCLK_MORE_CNTL, tmp); + udelay(15000); + } + + /* RV200::A11 A12, RV250::A11 A12 */ + if (((rdev->family == CHIP_RV200) || + (rdev->family == CHIP_RV250)) && + ((RREG32(RADEON_CONFIG_CNTL) & + RADEON_CFG_ATI_REV_ID_MASK) < + RADEON_CFG_ATI_REV_A13)) { + tmp = RREG32_PLL(RADEON_PLL_PWRMGT_CNTL); + tmp |= RADEON_TCL_BYPASS_DISABLE; + WREG32_PLL(RADEON_PLL_PWRMGT_CNTL, tmp); + } + udelay(15000); + + /*enable dynamic mode for display clocks (PIXCLK and PIX2CLK) */ + tmp = RREG32_PLL(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); + + WREG32_PLL(RADEON_PIXCLKS_CNTL, tmp); + udelay(15000); + + tmp = RREG32_PLL(RADEON_VCLK_ECP_CNTL); + tmp |= (RADEON_PIXCLK_ALWAYS_ONb | + RADEON_PIXCLK_DAC_ALWAYS_ONb); + + WREG32_PLL(RADEON_VCLK_ECP_CNTL, tmp); + udelay(15000); + } + } else { + /* Turn everything OFF (ForceON to everything) */ + if (rdev->flags & RADEON_SINGLE_CRTC) { + tmp = RREG32_PLL(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); + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + } else if ((rdev->family == CHIP_RS400) || + (rdev->family == CHIP_RS480)) { + tmp = RREG32_PLL(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); + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_SCLK_MORE_CNTL); + tmp |= RADEON_SCLK_MORE_FORCEON; + WREG32_PLL(RADEON_SCLK_MORE_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_VCLK_ECP_CNTL); + tmp &= ~(RADEON_PIXCLK_ALWAYS_ONb | + RADEON_PIXCLK_DAC_ALWAYS_ONb | + R300_DISP_DAC_PIXCLK_DAC_BLANK_OFF); + WREG32_PLL(RADEON_VCLK_ECP_CNTL, tmp); + + tmp = RREG32_PLL(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); + WREG32_PLL(RADEON_PIXCLKS_CNTL, tmp); + } else if (rdev->family >= CHIP_RV350) { + /* for RV350/M10, no delays are required. */ + tmp = RREG32_PLL(R300_SCLK_CNTL2); + tmp |= (R300_SCLK_FORCE_TCL | + R300_SCLK_FORCE_GA | R300_SCLK_FORCE_CBA); + WREG32_PLL(R300_SCLK_CNTL2, tmp); + + tmp = RREG32_PLL(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); + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_SCLK_MORE_CNTL); + tmp |= RADEON_SCLK_MORE_FORCEON; + WREG32_PLL(RADEON_SCLK_MORE_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_MCLK_CNTL); + tmp |= (RADEON_FORCEON_MCLKA | + RADEON_FORCEON_MCLKB | + RADEON_FORCEON_YCLKA | + RADEON_FORCEON_YCLKB | RADEON_FORCEON_MC); + WREG32_PLL(RADEON_MCLK_CNTL, tmp); + + tmp = RREG32_PLL(RADEON_VCLK_ECP_CNTL); + tmp &= ~(RADEON_PIXCLK_ALWAYS_ONb | + RADEON_PIXCLK_DAC_ALWAYS_ONb | + R300_DISP_DAC_PIXCLK_DAC_BLANK_OFF); + WREG32_PLL(RADEON_VCLK_ECP_CNTL, tmp); + + tmp = RREG32_PLL(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); + WREG32_PLL(RADEON_PIXCLKS_CNTL, tmp); + } else { + tmp = RREG32_PLL(RADEON_SCLK_CNTL); + tmp |= (RADEON_SCLK_FORCE_CP | RADEON_SCLK_FORCE_E2); + tmp |= RADEON_SCLK_FORCE_SE; + + if (rdev->flags & RADEON_SINGLE_CRTC) { + 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 ((rdev->family == CHIP_R300) || + (rdev->family == CHIP_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); + } + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + + udelay(16000); + + if ((rdev->family == CHIP_R300) || + (rdev->family == CHIP_R350)) { + tmp = RREG32_PLL(R300_SCLK_CNTL2); + tmp |= (R300_SCLK_FORCE_TCL | + R300_SCLK_FORCE_GA | + R300_SCLK_FORCE_CBA); + WREG32_PLL(R300_SCLK_CNTL2, tmp); + udelay(16000); + } + + if (rdev->flags & RADEON_IS_IGP) { + tmp = RREG32_PLL(RADEON_MCLK_CNTL); + tmp &= ~(RADEON_FORCEON_MCLKA | + RADEON_FORCEON_YCLKA); + WREG32_PLL(RADEON_MCLK_CNTL, tmp); + udelay(16000); + } + + if ((rdev->family == CHIP_RV200) || + (rdev->family == CHIP_RV250) || + (rdev->family == CHIP_RV280)) { + tmp = RREG32_PLL(RADEON_SCLK_MORE_CNTL); + tmp |= RADEON_SCLK_MORE_FORCEON; + WREG32_PLL(RADEON_SCLK_MORE_CNTL, tmp); + udelay(16000); + } + + tmp = RREG32_PLL(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); + + WREG32_PLL(RADEON_PIXCLKS_CNTL, tmp); + udelay(16000); + + tmp = RREG32_PLL(RADEON_VCLK_ECP_CNTL); + tmp &= ~(RADEON_PIXCLK_ALWAYS_ONb | + RADEON_PIXCLK_DAC_ALWAYS_ONb); + WREG32_PLL(RADEON_VCLK_ECP_CNTL, tmp); + } + } +} + +static void radeon_apply_clock_quirks(struct radeon_device *rdev) +{ + uint32_t tmp; + + /* XXX make sure engine is idle */ + + if (rdev->family < CHIP_RS600) { + tmp = RREG32_PLL(RADEON_SCLK_CNTL); + if (ASIC_IS_R300(rdev) || ASIC_IS_RV100(rdev)) + tmp |= RADEON_SCLK_FORCE_CP | RADEON_SCLK_FORCE_VIP; + if ((rdev->family == CHIP_RV250) + || (rdev->family == CHIP_RV280)) + tmp |= + RADEON_SCLK_FORCE_DISP1 | RADEON_SCLK_FORCE_DISP2; + if ((rdev->family == CHIP_RV350) + || (rdev->family == CHIP_RV380)) + tmp |= R300_SCLK_FORCE_VAP; + if (rdev->family == CHIP_R420) + tmp |= R300_SCLK_FORCE_PX | R300_SCLK_FORCE_TX; + WREG32_PLL(RADEON_SCLK_CNTL, tmp); + } else if (rdev->family < CHIP_R600) { + tmp = RREG32_PLL(AVIVO_CP_DYN_CNTL); + tmp |= AVIVO_CP_FORCEON; + WREG32_PLL(AVIVO_CP_DYN_CNTL, tmp); + + tmp = RREG32_PLL(AVIVO_E2_DYN_CNTL); + tmp |= AVIVO_E2_FORCEON; + WREG32_PLL(AVIVO_E2_DYN_CNTL, tmp); + + tmp = RREG32_PLL(AVIVO_IDCT_DYN_CNTL); + tmp |= AVIVO_IDCT_FORCEON; + WREG32_PLL(AVIVO_IDCT_DYN_CNTL, tmp); + } +} + +int radeon_static_clocks_init(struct drm_device *dev) +{ + struct radeon_device *rdev = dev->dev_private; + + /* XXX make sure engine is idle */ + + if (radeon_dynclks != -1) { + if (radeon_dynclks) + radeon_set_clock_gating(rdev, 1); + } + radeon_apply_clock_quirks(rdev); + return 0; +} |