drm/amdgpu: SI support for UVD clock control
Port functionality from the Radeon driver to support UVD clock control. Signed-off-by: Alex Jivin <alex.jivin@amd.com> Reviewed-by: Alex Deucher <alexander.deucher@amd.com> Acked-by: Christian König <christian.koenig@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Родитель
9555152beb
Коммит
3b0627a4b6
|
@ -1267,12 +1267,6 @@ static u32 si_get_xclk(struct amdgpu_device *adev)
|
|||
return reference_clock;
|
||||
}
|
||||
|
||||
//xxx:not implemented
|
||||
static int si_set_uvd_clocks(struct amdgpu_device *adev, u32 vclk, u32 dclk)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void si_flush_hdp(struct amdgpu_device *adev, struct amdgpu_ring *ring)
|
||||
{
|
||||
if (!ring || !ring->funcs->emit_wreg) {
|
||||
|
@ -1428,6 +1422,234 @@ static uint64_t si_get_pcie_replay_count(struct amdgpu_device *adev)
|
|||
return (nak_r + nak_g);
|
||||
}
|
||||
|
||||
static int si_uvd_send_upll_ctlreq(struct amdgpu_device *adev,
|
||||
unsigned cg_upll_func_cntl)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
/* Make sure UPLL_CTLREQ is deasserted */
|
||||
WREG32_P(cg_upll_func_cntl, 0, ~UPLL_CTLREQ_MASK);
|
||||
|
||||
mdelay(10);
|
||||
|
||||
/* Assert UPLL_CTLREQ */
|
||||
WREG32_P(cg_upll_func_cntl, UPLL_CTLREQ_MASK, ~UPLL_CTLREQ_MASK);
|
||||
|
||||
/* Wait for CTLACK and CTLACK2 to get asserted */
|
||||
for (i = 0; i < SI_MAX_CTLACKS_ASSERTION_WAIT; ++i) {
|
||||
uint32_t mask = UPLL_CTLACK_MASK | UPLL_CTLACK2_MASK;
|
||||
|
||||
if ((RREG32(cg_upll_func_cntl) & mask) == mask)
|
||||
break;
|
||||
mdelay(10);
|
||||
}
|
||||
|
||||
/* Deassert UPLL_CTLREQ */
|
||||
WREG32_P(cg_upll_func_cntl, 0, ~UPLL_CTLREQ_MASK);
|
||||
|
||||
if (i == SI_MAX_CTLACKS_ASSERTION_WAIT) {
|
||||
DRM_ERROR("Timeout setting UVD clocks!\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned si_uvd_calc_upll_post_div(unsigned vco_freq,
|
||||
unsigned target_freq,
|
||||
unsigned pd_min,
|
||||
unsigned pd_even)
|
||||
{
|
||||
unsigned post_div = vco_freq / target_freq;
|
||||
|
||||
/* Adjust to post divider minimum value */
|
||||
if (post_div < pd_min)
|
||||
post_div = pd_min;
|
||||
|
||||
/* We alway need a frequency less than or equal the target */
|
||||
if ((vco_freq / post_div) > target_freq)
|
||||
post_div += 1;
|
||||
|
||||
/* Post dividers above a certain value must be even */
|
||||
if (post_div > pd_even && post_div % 2)
|
||||
post_div += 1;
|
||||
|
||||
return post_div;
|
||||
}
|
||||
|
||||
/**
|
||||
* si_calc_upll_dividers - calc UPLL clock dividers
|
||||
*
|
||||
* @adev: amdgpu_device pointer
|
||||
* @vclk: wanted VCLK
|
||||
* @dclk: wanted DCLK
|
||||
* @vco_min: minimum VCO frequency
|
||||
* @vco_max: maximum VCO frequency
|
||||
* @fb_factor: factor to multiply vco freq with
|
||||
* @fb_mask: limit and bitmask for feedback divider
|
||||
* @pd_min: post divider minimum
|
||||
* @pd_max: post divider maximum
|
||||
* @pd_even: post divider must be even above this value
|
||||
* @optimal_fb_div: resulting feedback divider
|
||||
* @optimal_vclk_div: resulting vclk post divider
|
||||
* @optimal_dclk_div: resulting dclk post divider
|
||||
*
|
||||
* Calculate dividers for UVDs UPLL (except APUs).
|
||||
* Returns zero on success; -EINVAL on error.
|
||||
*/
|
||||
static int si_calc_upll_dividers(struct amdgpu_device *adev,
|
||||
unsigned vclk, unsigned dclk,
|
||||
unsigned vco_min, unsigned vco_max,
|
||||
unsigned fb_factor, unsigned fb_mask,
|
||||
unsigned pd_min, unsigned pd_max,
|
||||
unsigned pd_even,
|
||||
unsigned *optimal_fb_div,
|
||||
unsigned *optimal_vclk_div,
|
||||
unsigned *optimal_dclk_div)
|
||||
{
|
||||
unsigned vco_freq, ref_freq = adev->clock.spll.reference_freq;
|
||||
|
||||
/* Start off with something large */
|
||||
unsigned optimal_score = ~0;
|
||||
|
||||
/* Loop through vco from low to high */
|
||||
vco_min = max(max(vco_min, vclk), dclk);
|
||||
for (vco_freq = vco_min; vco_freq <= vco_max; vco_freq += 100) {
|
||||
uint64_t fb_div = (uint64_t)vco_freq * fb_factor;
|
||||
unsigned vclk_div, dclk_div, score;
|
||||
|
||||
do_div(fb_div, ref_freq);
|
||||
|
||||
/* fb div out of range ? */
|
||||
if (fb_div > fb_mask)
|
||||
break; /* It can oly get worse */
|
||||
|
||||
fb_div &= fb_mask;
|
||||
|
||||
/* Calc vclk divider with current vco freq */
|
||||
vclk_div = si_uvd_calc_upll_post_div(vco_freq, vclk,
|
||||
pd_min, pd_even);
|
||||
if (vclk_div > pd_max)
|
||||
break; /* vco is too big, it has to stop */
|
||||
|
||||
/* Calc dclk divider with current vco freq */
|
||||
dclk_div = si_uvd_calc_upll_post_div(vco_freq, dclk,
|
||||
pd_min, pd_even);
|
||||
if (dclk_div > pd_max)
|
||||
break; /* vco is too big, it has to stop */
|
||||
|
||||
/* Calc score with current vco freq */
|
||||
score = vclk - (vco_freq / vclk_div) + dclk - (vco_freq / dclk_div);
|
||||
|
||||
/* Determine if this vco setting is better than current optimal settings */
|
||||
if (score < optimal_score) {
|
||||
*optimal_fb_div = fb_div;
|
||||
*optimal_vclk_div = vclk_div;
|
||||
*optimal_dclk_div = dclk_div;
|
||||
optimal_score = score;
|
||||
if (optimal_score == 0)
|
||||
break; /* It can't get better than this */
|
||||
}
|
||||
}
|
||||
|
||||
/* Did we found a valid setup ? */
|
||||
if (optimal_score == ~0)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int si_set_uvd_clocks(struct amdgpu_device *adev, u32 vclk, u32 dclk)
|
||||
{
|
||||
unsigned fb_div = 0, vclk_div = 0, dclk_div = 0;
|
||||
int r;
|
||||
|
||||
/* Bypass vclk and dclk with bclk */
|
||||
WREG32_P(CG_UPLL_FUNC_CNTL_2,
|
||||
VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1),
|
||||
~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK));
|
||||
|
||||
/* Put PLL in bypass mode */
|
||||
WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK);
|
||||
|
||||
if (!vclk || !dclk) {
|
||||
/* Keep the Bypass mode */
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = si_calc_upll_dividers(adev, vclk, dclk, 125000, 250000,
|
||||
16384, 0x03FFFFFF, 0, 128, 5,
|
||||
&fb_div, &vclk_div, &dclk_div);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/* Set RESET_ANTI_MUX to 0 */
|
||||
WREG32_P(CG_UPLL_FUNC_CNTL_5, 0, ~RESET_ANTI_MUX_MASK);
|
||||
|
||||
/* Set VCO_MODE to 1 */
|
||||
WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_VCO_MODE_MASK, ~UPLL_VCO_MODE_MASK);
|
||||
|
||||
/* Disable sleep mode */
|
||||
WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_SLEEP_MASK);
|
||||
|
||||
/* Deassert UPLL_RESET */
|
||||
WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_RESET_MASK);
|
||||
|
||||
mdelay(1);
|
||||
|
||||
r = si_uvd_send_upll_ctlreq(adev, CG_UPLL_FUNC_CNTL);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/* Assert UPLL_RESET again */
|
||||
WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_RESET_MASK, ~UPLL_RESET_MASK);
|
||||
|
||||
/* Disable spread spectrum. */
|
||||
WREG32_P(CG_UPLL_SPREAD_SPECTRUM, 0, ~SSEN_MASK);
|
||||
|
||||
/* Set feedback divider */
|
||||
WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(fb_div), ~UPLL_FB_DIV_MASK);
|
||||
|
||||
/* Set ref divider to 0 */
|
||||
WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_REF_DIV_MASK);
|
||||
|
||||
if (fb_div < 307200)
|
||||
WREG32_P(CG_UPLL_FUNC_CNTL_4, 0, ~UPLL_SPARE_ISPARE9);
|
||||
else
|
||||
WREG32_P(CG_UPLL_FUNC_CNTL_4,
|
||||
UPLL_SPARE_ISPARE9,
|
||||
~UPLL_SPARE_ISPARE9);
|
||||
|
||||
/* Set PDIV_A and PDIV_B */
|
||||
WREG32_P(CG_UPLL_FUNC_CNTL_2,
|
||||
UPLL_PDIV_A(vclk_div) | UPLL_PDIV_B(dclk_div),
|
||||
~(UPLL_PDIV_A_MASK | UPLL_PDIV_B_MASK));
|
||||
|
||||
/* Give the PLL some time to settle */
|
||||
mdelay(15);
|
||||
|
||||
/* Deassert PLL_RESET */
|
||||
WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_RESET_MASK);
|
||||
|
||||
mdelay(15);
|
||||
|
||||
/* Switch from bypass mode to normal mode */
|
||||
WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_BYPASS_EN_MASK);
|
||||
|
||||
r = si_uvd_send_upll_ctlreq(adev, CG_UPLL_FUNC_CNTL);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/* Switch VCLK and DCLK selection */
|
||||
WREG32_P(CG_UPLL_FUNC_CNTL_2,
|
||||
VCLK_SRC_SEL(2) | DCLK_SRC_SEL(2),
|
||||
~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK));
|
||||
|
||||
mdelay(100);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct amdgpu_asic_funcs si_asic_funcs =
|
||||
{
|
||||
.read_disabled_bios = &si_read_disabled_bios,
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#define SI_MAX_LDS_NUM 0xFFFF
|
||||
#define SI_MAX_TCC 16
|
||||
#define SI_MAX_TCC_MASK 0xFFFF
|
||||
#define SI_MAX_CTLACKS_ASSERTION_WAIT 100
|
||||
|
||||
#define AMDGPU_NUM_OF_VMIDS 8
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче