aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/radeon/si.c
diff options
context:
space:
mode:
authorChristian König <deathsimple@vodafone.de>2013-04-08 06:41:34 -0400
committerAlex Deucher <alexander.deucher@amd.com>2013-04-09 10:31:37 -0400
commit2539eb02de42f2bc60f329e3adb75d41697089df (patch)
tree6359d7d2af6c9974dcd6df3f240c6b5c853d0928 /drivers/gpu/drm/radeon/si.c
parenta8b4925c79c804055e50515177dbc47909396c95 (diff)
drm/radeon: add set_uvd_clocks callback for SI
Signed-off-by: Christian König <christian.koenig@amd.com> Reviewed-by: Jerome Glisse <jglisse@redhat.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.c167
1 files changed, 167 insertions, 0 deletions
diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c
index 3e9782dc35bf..465053d461bb 100644
--- a/drivers/gpu/drm/radeon/si.c
+++ b/drivers/gpu/drm/radeon/si.c
@@ -4627,3 +4627,170 @@ uint64_t si_get_gpu_clock_counter(struct radeon_device *rdev)
4627 mutex_unlock(&rdev->gpu_clock_mutex); 4627 mutex_unlock(&rdev->gpu_clock_mutex);
4628 return clock; 4628 return clock;
4629} 4629}
4630
4631static int si_uvd_calc_post_div(unsigned target_freq,
4632 unsigned vco_freq,
4633 unsigned *div)
4634{
4635 /* target larger than vco frequency ? */
4636 if (vco_freq < target_freq)
4637 return -1; /* forget it */
4638
4639 /* Fclk = Fvco / PDIV */
4640 *div = vco_freq / target_freq;
4641
4642 /* we alway need a frequency less than or equal the target */
4643 if ((vco_freq / *div) > target_freq)
4644 *div += 1;
4645
4646 /* dividers above 5 must be even */
4647 if (*div > 5 && *div % 2)
4648 *div += 1;
4649
4650 /* out of range ? */
4651 if (*div >= 128)
4652 return -1; /* forget it */
4653
4654 return vco_freq / *div;
4655}
4656
4657static int si_uvd_send_upll_ctlreq(struct radeon_device *rdev)
4658{
4659 unsigned i;
4660
4661 /* assert UPLL_CTLREQ */
4662 WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_CTLREQ_MASK, ~UPLL_CTLREQ_MASK);
4663
4664 /* wait for CTLACK and CTLACK2 to get asserted */
4665 for (i = 0; i < 100; ++i) {
4666 uint32_t mask = UPLL_CTLACK_MASK | UPLL_CTLACK2_MASK;
4667 if ((RREG32(CG_UPLL_FUNC_CNTL) & mask) == mask)
4668 break;
4669 mdelay(10);
4670 }
4671 if (i == 100)
4672 return -ETIMEDOUT;
4673
4674 /* deassert UPLL_CTLREQ */
4675 WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_CTLREQ_MASK);
4676
4677 return 0;
4678}
4679
4680int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk)
4681{
4682 /* start off with something large */
4683 int optimal_diff_score = 0x7FFFFFF;
4684 unsigned optimal_fb_div = 0, optimal_vclk_div = 0;
4685 unsigned optimal_dclk_div = 0, optimal_vco_freq = 0;
4686 unsigned vco_freq;
4687 int r;
4688
4689 /* loop through vco from low to high */
4690 for (vco_freq = 125000; vco_freq <= 250000; vco_freq += 100) {
4691 unsigned fb_div = vco_freq / rdev->clock.spll.reference_freq * 16384;
4692 int calc_clk, diff_score, diff_vclk, diff_dclk;
4693 unsigned vclk_div, dclk_div;
4694
4695 /* fb div out of range ? */
4696 if (fb_div > 0x03FFFFFF)
4697 break; /* it can oly get worse */
4698
4699 /* calc vclk with current vco freq. */
4700 calc_clk = si_uvd_calc_post_div(vclk, vco_freq, &vclk_div);
4701 if (calc_clk == -1)
4702 break; /* vco is too big, it has to stop. */
4703 diff_vclk = vclk - calc_clk;
4704
4705 /* calc dclk with current vco freq. */
4706 calc_clk = si_uvd_calc_post_div(dclk, vco_freq, &dclk_div);
4707 if (calc_clk == -1)
4708 break; /* vco is too big, it has to stop. */
4709 diff_dclk = dclk - calc_clk;
4710
4711 /* determine if this vco setting is better than current optimal settings */
4712 diff_score = abs(diff_vclk) + abs(diff_dclk);
4713 if (diff_score < optimal_diff_score) {
4714 optimal_fb_div = fb_div;
4715 optimal_vclk_div = vclk_div;
4716 optimal_dclk_div = dclk_div;
4717 optimal_vco_freq = vco_freq;
4718 optimal_diff_score = diff_score;
4719 if (optimal_diff_score == 0)
4720 break; /* it can't get better than this */
4721 }
4722 }
4723
4724 /* set RESET_ANTI_MUX to 0 */
4725 WREG32_P(CG_UPLL_FUNC_CNTL_5, 0, ~RESET_ANTI_MUX_MASK);
4726
4727 /* set VCO_MODE to 1 */
4728 WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_VCO_MODE_MASK, ~UPLL_VCO_MODE_MASK);
4729
4730 /* toggle UPLL_SLEEP to 1 then back to 0 */
4731 WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK);
4732 WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_SLEEP_MASK);
4733
4734 /* deassert UPLL_RESET */
4735 WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_RESET_MASK);
4736
4737 mdelay(1);
4738
4739 /* bypass vclk and dclk with bclk */
4740 WREG32_P(CG_UPLL_FUNC_CNTL_2,
4741 VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1),
4742 ~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK));
4743
4744 /* put PLL in bypass mode */
4745 WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK);
4746
4747 r = si_uvd_send_upll_ctlreq(rdev);
4748 if (r)
4749 return r;
4750
4751 /* assert UPLL_RESET again */
4752 WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_RESET_MASK, ~UPLL_RESET_MASK);
4753
4754 /* disable spread spectrum. */
4755 WREG32_P(CG_UPLL_SPREAD_SPECTRUM, 0, ~SSEN_MASK);
4756
4757 /* set feedback divider */
4758 WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(optimal_fb_div), ~UPLL_FB_DIV_MASK);
4759
4760 /* set ref divider to 0 */
4761 WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_REF_DIV_MASK);
4762
4763 if (optimal_vco_freq < 187500)
4764 WREG32_P(CG_UPLL_FUNC_CNTL_4, 0, ~UPLL_SPARE_ISPARE9);
4765 else
4766 WREG32_P(CG_UPLL_FUNC_CNTL_4, UPLL_SPARE_ISPARE9, ~UPLL_SPARE_ISPARE9);
4767
4768 /* set PDIV_A and PDIV_B */
4769 WREG32_P(CG_UPLL_FUNC_CNTL_2,
4770 UPLL_PDIV_A(optimal_vclk_div) | UPLL_PDIV_B(optimal_dclk_div),
4771 ~(UPLL_PDIV_A_MASK | UPLL_PDIV_B_MASK));
4772
4773 /* give the PLL some time to settle */
4774 mdelay(15);
4775
4776 /* deassert PLL_RESET */
4777 WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_RESET_MASK);
4778
4779 mdelay(15);
4780
4781 /* switch from bypass mode to normal mode */
4782 WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_BYPASS_EN_MASK);
4783
4784 r = si_uvd_send_upll_ctlreq(rdev);
4785 if (r)
4786 return r;
4787
4788 /* switch VCLK and DCLK selection */
4789 WREG32_P(CG_UPLL_FUNC_CNTL_2,
4790 VCLK_SRC_SEL(2) | DCLK_SRC_SEL(2),
4791 ~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK));
4792
4793 mdelay(100);
4794
4795 return 0;
4796}