diff options
author | Christian König <christian.koenig@amd.com> | 2013-04-29 05:55:02 -0400 |
---|---|---|
committer | Alex Deucher <alexander.deucher@amd.com> | 2013-05-02 10:09:48 -0400 |
commit | facd112d1395fb6a0b6e460778aefc32197afcfc (patch) | |
tree | 69cc696021ccfe1eb1d78a7aedfb32f984675118 /drivers/gpu/drm/radeon/si.c | |
parent | 092fbc4ca29a3d78895673479f794ee162a13ac5 (diff) |
drm/radeon: consolidate UVD clock programming
Instead of duplicating the code over and over again, just use a single
function to handle the clock calculations.
Signed-off-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm/radeon/si.c')
-rw-r--r-- | drivers/gpu/drm/radeon/si.c | 104 |
1 files changed, 11 insertions, 93 deletions
diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index fe6b14e0021c..f0b6c2f87c4d 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c | |||
@@ -5415,62 +5415,9 @@ uint64_t si_get_gpu_clock_counter(struct radeon_device *rdev) | |||
5415 | return clock; | 5415 | return clock; |
5416 | } | 5416 | } |
5417 | 5417 | ||
5418 | static int si_uvd_calc_post_div(unsigned target_freq, | ||
5419 | unsigned vco_freq, | ||
5420 | unsigned *div) | ||
5421 | { | ||
5422 | /* target larger than vco frequency ? */ | ||
5423 | if (vco_freq < target_freq) | ||
5424 | return -1; /* forget it */ | ||
5425 | |||
5426 | /* Fclk = Fvco / PDIV */ | ||
5427 | *div = vco_freq / target_freq; | ||
5428 | |||
5429 | /* we alway need a frequency less than or equal the target */ | ||
5430 | if ((vco_freq / *div) > target_freq) | ||
5431 | *div += 1; | ||
5432 | |||
5433 | /* dividers above 5 must be even */ | ||
5434 | if (*div > 5 && *div % 2) | ||
5435 | *div += 1; | ||
5436 | |||
5437 | /* out of range ? */ | ||
5438 | if (*div >= 128) | ||
5439 | return -1; /* forget it */ | ||
5440 | |||
5441 | return vco_freq / *div; | ||
5442 | } | ||
5443 | |||
5444 | static int si_uvd_send_upll_ctlreq(struct radeon_device *rdev) | ||
5445 | { | ||
5446 | unsigned i; | ||
5447 | |||
5448 | /* assert UPLL_CTLREQ */ | ||
5449 | WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_CTLREQ_MASK, ~UPLL_CTLREQ_MASK); | ||
5450 | |||
5451 | /* wait for CTLACK and CTLACK2 to get asserted */ | ||
5452 | for (i = 0; i < 100; ++i) { | ||
5453 | uint32_t mask = UPLL_CTLACK_MASK | UPLL_CTLACK2_MASK; | ||
5454 | if ((RREG32(CG_UPLL_FUNC_CNTL) & mask) == mask) | ||
5455 | break; | ||
5456 | mdelay(10); | ||
5457 | } | ||
5458 | if (i == 100) | ||
5459 | return -ETIMEDOUT; | ||
5460 | |||
5461 | /* deassert UPLL_CTLREQ */ | ||
5462 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_CTLREQ_MASK); | ||
5463 | |||
5464 | return 0; | ||
5465 | } | ||
5466 | |||
5467 | int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | 5418 | int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) |
5468 | { | 5419 | { |
5469 | /* start off with something large */ | 5420 | unsigned fb_div = 0, vclk_div = 0, dclk_div = 0; |
5470 | int optimal_diff_score = 0x7FFFFFF; | ||
5471 | unsigned optimal_fb_div = 0, optimal_vclk_div = 0; | ||
5472 | unsigned optimal_dclk_div = 0, optimal_vco_freq = 0; | ||
5473 | unsigned vco_freq; | ||
5474 | int r; | 5421 | int r; |
5475 | 5422 | ||
5476 | /* bypass vclk and dclk with bclk */ | 5423 | /* bypass vclk and dclk with bclk */ |
@@ -5487,40 +5434,11 @@ int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
5487 | return 0; | 5434 | return 0; |
5488 | } | 5435 | } |
5489 | 5436 | ||
5490 | /* loop through vco from low to high */ | 5437 | r = radeon_uvd_calc_upll_dividers(rdev, vclk, dclk, 125000, 250000, |
5491 | for (vco_freq = 125000; vco_freq <= 250000; vco_freq += 100) { | 5438 | 16384, 0x03FFFFFF, 0, 128, 5, |
5492 | unsigned fb_div = vco_freq / rdev->clock.spll.reference_freq * 16384; | 5439 | &fb_div, &vclk_div, &dclk_div); |
5493 | int calc_clk, diff_score, diff_vclk, diff_dclk; | 5440 | if (r) |
5494 | unsigned vclk_div, dclk_div; | 5441 | return r; |
5495 | |||
5496 | /* fb div out of range ? */ | ||
5497 | if (fb_div > 0x03FFFFFF) | ||
5498 | break; /* it can oly get worse */ | ||
5499 | |||
5500 | /* calc vclk with current vco freq. */ | ||
5501 | calc_clk = si_uvd_calc_post_div(vclk, vco_freq, &vclk_div); | ||
5502 | if (calc_clk == -1) | ||
5503 | break; /* vco is too big, it has to stop. */ | ||
5504 | diff_vclk = vclk - calc_clk; | ||
5505 | |||
5506 | /* calc dclk with current vco freq. */ | ||
5507 | calc_clk = si_uvd_calc_post_div(dclk, vco_freq, &dclk_div); | ||
5508 | if (calc_clk == -1) | ||
5509 | break; /* vco is too big, it has to stop. */ | ||
5510 | diff_dclk = dclk - calc_clk; | ||
5511 | |||
5512 | /* determine if this vco setting is better than current optimal settings */ | ||
5513 | diff_score = abs(diff_vclk) + abs(diff_dclk); | ||
5514 | if (diff_score < optimal_diff_score) { | ||
5515 | optimal_fb_div = fb_div; | ||
5516 | optimal_vclk_div = vclk_div; | ||
5517 | optimal_dclk_div = dclk_div; | ||
5518 | optimal_vco_freq = vco_freq; | ||
5519 | optimal_diff_score = diff_score; | ||
5520 | if (optimal_diff_score == 0) | ||
5521 | break; /* it can't get better than this */ | ||
5522 | } | ||
5523 | } | ||
5524 | 5442 | ||
5525 | /* set RESET_ANTI_MUX to 0 */ | 5443 | /* set RESET_ANTI_MUX to 0 */ |
5526 | WREG32_P(CG_UPLL_FUNC_CNTL_5, 0, ~RESET_ANTI_MUX_MASK); | 5444 | WREG32_P(CG_UPLL_FUNC_CNTL_5, 0, ~RESET_ANTI_MUX_MASK); |
@@ -5537,7 +5455,7 @@ int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
5537 | 5455 | ||
5538 | mdelay(1); | 5456 | mdelay(1); |
5539 | 5457 | ||
5540 | r = si_uvd_send_upll_ctlreq(rdev); | 5458 | r = radeon_uvd_send_upll_ctlreq(rdev, CG_UPLL_FUNC_CNTL); |
5541 | if (r) | 5459 | if (r) |
5542 | return r; | 5460 | return r; |
5543 | 5461 | ||
@@ -5548,19 +5466,19 @@ int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
5548 | WREG32_P(CG_UPLL_SPREAD_SPECTRUM, 0, ~SSEN_MASK); | 5466 | WREG32_P(CG_UPLL_SPREAD_SPECTRUM, 0, ~SSEN_MASK); |
5549 | 5467 | ||
5550 | /* set feedback divider */ | 5468 | /* set feedback divider */ |
5551 | WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(optimal_fb_div), ~UPLL_FB_DIV_MASK); | 5469 | WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(fb_div), ~UPLL_FB_DIV_MASK); |
5552 | 5470 | ||
5553 | /* set ref divider to 0 */ | 5471 | /* set ref divider to 0 */ |
5554 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_REF_DIV_MASK); | 5472 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_REF_DIV_MASK); |
5555 | 5473 | ||
5556 | if (optimal_vco_freq < 187500) | 5474 | if (fb_div < 307200) |
5557 | WREG32_P(CG_UPLL_FUNC_CNTL_4, 0, ~UPLL_SPARE_ISPARE9); | 5475 | WREG32_P(CG_UPLL_FUNC_CNTL_4, 0, ~UPLL_SPARE_ISPARE9); |
5558 | else | 5476 | else |
5559 | WREG32_P(CG_UPLL_FUNC_CNTL_4, UPLL_SPARE_ISPARE9, ~UPLL_SPARE_ISPARE9); | 5477 | WREG32_P(CG_UPLL_FUNC_CNTL_4, UPLL_SPARE_ISPARE9, ~UPLL_SPARE_ISPARE9); |
5560 | 5478 | ||
5561 | /* set PDIV_A and PDIV_B */ | 5479 | /* set PDIV_A and PDIV_B */ |
5562 | WREG32_P(CG_UPLL_FUNC_CNTL_2, | 5480 | WREG32_P(CG_UPLL_FUNC_CNTL_2, |
5563 | UPLL_PDIV_A(optimal_vclk_div) | UPLL_PDIV_B(optimal_dclk_div), | 5481 | UPLL_PDIV_A(vclk_div) | UPLL_PDIV_B(dclk_div), |
5564 | ~(UPLL_PDIV_A_MASK | UPLL_PDIV_B_MASK)); | 5482 | ~(UPLL_PDIV_A_MASK | UPLL_PDIV_B_MASK)); |
5565 | 5483 | ||
5566 | /* give the PLL some time to settle */ | 5484 | /* give the PLL some time to settle */ |
@@ -5574,7 +5492,7 @@ int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
5574 | /* switch from bypass mode to normal mode */ | 5492 | /* switch from bypass mode to normal mode */ |
5575 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_BYPASS_EN_MASK); | 5493 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_BYPASS_EN_MASK); |
5576 | 5494 | ||
5577 | r = si_uvd_send_upll_ctlreq(rdev); | 5495 | r = radeon_uvd_send_upll_ctlreq(rdev, CG_UPLL_FUNC_CNTL); |
5578 | if (r) | 5496 | if (r) |
5579 | return r; | 5497 | return r; |
5580 | 5498 | ||