aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.c1
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.h1
-rw-r--r--drivers/gpu/drm/radeon/si.c167
-rw-r--r--drivers/gpu/drm/radeon/sid.h29
4 files changed, 198 insertions, 0 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c
index a65312c95bd3..03228cb65518 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.c
+++ b/drivers/gpu/drm/radeon/radeon_asic.c
@@ -1887,6 +1887,7 @@ static struct radeon_asic si_asic = {
1887 .get_pcie_lanes = NULL, 1887 .get_pcie_lanes = NULL,
1888 .set_pcie_lanes = NULL, 1888 .set_pcie_lanes = NULL,
1889 .set_clock_gating = NULL, 1889 .set_clock_gating = NULL,
1890 .set_uvd_clocks = &si_set_uvd_clocks,
1890 }, 1891 },
1891 .pflip = { 1892 .pflip = {
1892 .pre_page_flip = &evergreen_pre_page_flip, 1893 .pre_page_flip = &evergreen_pre_page_flip,
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h
index 54a7ef70805d..365c964399a5 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.h
+++ b/drivers/gpu/drm/radeon/radeon_asic.h
@@ -545,5 +545,6 @@ int si_copy_dma(struct radeon_device *rdev,
545void si_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm); 545void si_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm);
546u32 si_get_xclk(struct radeon_device *rdev); 546u32 si_get_xclk(struct radeon_device *rdev);
547uint64_t si_get_gpu_clock_counter(struct radeon_device *rdev); 547uint64_t si_get_gpu_clock_counter(struct radeon_device *rdev);
548int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk);
548 549
549#endif 550#endif
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}
diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h
index 1fb8ee2c45dc..3a685855c3d8 100644
--- a/drivers/gpu/drm/radeon/sid.h
+++ b/drivers/gpu/drm/radeon/sid.h
@@ -29,6 +29,35 @@
29#define TAHITI_GB_ADDR_CONFIG_GOLDEN 0x12011003 29#define TAHITI_GB_ADDR_CONFIG_GOLDEN 0x12011003
30#define VERDE_GB_ADDR_CONFIG_GOLDEN 0x12010002 30#define VERDE_GB_ADDR_CONFIG_GOLDEN 0x12010002
31 31
32/* discrete uvd clocks */
33#define CG_UPLL_FUNC_CNTL 0x634
34# define UPLL_RESET_MASK 0x00000001
35# define UPLL_SLEEP_MASK 0x00000002
36# define UPLL_BYPASS_EN_MASK 0x00000004
37# define UPLL_CTLREQ_MASK 0x00000008
38# define UPLL_VCO_MODE_MASK 0x00000600
39# define UPLL_REF_DIV_MASK 0x001F0000
40# define UPLL_CTLACK_MASK 0x40000000
41# define UPLL_CTLACK2_MASK 0x80000000
42#define CG_UPLL_FUNC_CNTL_2 0x638
43# define UPLL_PDIV_A(x) ((x) << 0)
44# define UPLL_PDIV_A_MASK 0x0000007F
45# define UPLL_PDIV_B(x) ((x) << 8)
46# define UPLL_PDIV_B_MASK 0x00007F00
47# define VCLK_SRC_SEL(x) ((x) << 20)
48# define VCLK_SRC_SEL_MASK 0x01F00000
49# define DCLK_SRC_SEL(x) ((x) << 25)
50# define DCLK_SRC_SEL_MASK 0x3E000000
51#define CG_UPLL_FUNC_CNTL_3 0x63C
52# define UPLL_FB_DIV(x) ((x) << 0)
53# define UPLL_FB_DIV_MASK 0x01FFFFFF
54#define CG_UPLL_FUNC_CNTL_4 0x644
55# define UPLL_SPARE_ISPARE9 0x00020000
56#define CG_UPLL_FUNC_CNTL_5 0x648
57# define RESET_ANTI_MUX_MASK 0x00000200
58#define CG_UPLL_SPREAD_SPECTRUM 0x650
59# define SSEN_MASK 0x00000001
60
32#define CG_MULT_THERMAL_STATUS 0x714 61#define CG_MULT_THERMAL_STATUS 0x714
33#define ASIC_MAX_TEMP(x) ((x) << 0) 62#define ASIC_MAX_TEMP(x) ((x) << 0)
34#define ASIC_MAX_TEMP_MASK 0x000001ff 63#define ASIC_MAX_TEMP_MASK 0x000001ff