diff options
Diffstat (limited to 'drivers/gpu/drm/radeon')
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_asic.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_asic.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/si.c | 167 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/sid.h | 29 |
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, | |||
545 | void si_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm); | 545 | void si_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm); |
546 | u32 si_get_xclk(struct radeon_device *rdev); | 546 | u32 si_get_xclk(struct radeon_device *rdev); |
547 | uint64_t si_get_gpu_clock_counter(struct radeon_device *rdev); | 547 | uint64_t si_get_gpu_clock_counter(struct radeon_device *rdev); |
548 | int 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 | |||
4631 | static 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 | |||
4657 | static 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 | |||
4680 | int 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 |