836 lines
24 KiB
C
836 lines
24 KiB
C
/*
|
|
* 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 (rdev->family < CHIP_RS600) {
|
|
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_DAC_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_DAC_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_DAC_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_DAC_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;
|
|
}
|