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 | |
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')
-rw-r--r-- | drivers/gpu/drm/radeon/evergreen.c | 103 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/r600d.h | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon.h | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_uvd.c | 137 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/rv770.c | 110 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/si.c | 104 |
6 files changed, 191 insertions, 278 deletions
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 1531f167d152..105bafb6c29d 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c | |||
@@ -989,62 +989,10 @@ done: | |||
989 | return r; | 989 | return r; |
990 | } | 990 | } |
991 | 991 | ||
992 | static int evergreen_uvd_calc_post_div(unsigned target_freq, | ||
993 | unsigned vco_freq, | ||
994 | unsigned *div) | ||
995 | { | ||
996 | /* target larger than vco frequency ? */ | ||
997 | if (vco_freq < target_freq) | ||
998 | return -1; /* forget it */ | ||
999 | |||
1000 | /* Fclk = Fvco / PDIV */ | ||
1001 | *div = vco_freq / target_freq; | ||
1002 | |||
1003 | /* we alway need a frequency less than or equal the target */ | ||
1004 | if ((vco_freq / *div) > target_freq) | ||
1005 | *div += 1; | ||
1006 | |||
1007 | /* dividers above 5 must be even */ | ||
1008 | if (*div > 5 && *div % 2) | ||
1009 | *div += 1; | ||
1010 | |||
1011 | /* out of range ? */ | ||
1012 | if (*div >= 128) | ||
1013 | return -1; /* forget it */ | ||
1014 | |||
1015 | return vco_freq / *div; | ||
1016 | } | ||
1017 | |||
1018 | static int evergreen_uvd_send_upll_ctlreq(struct radeon_device *rdev) | ||
1019 | { | ||
1020 | unsigned i; | ||
1021 | |||
1022 | /* assert UPLL_CTLREQ */ | ||
1023 | WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_CTLREQ_MASK, ~UPLL_CTLREQ_MASK); | ||
1024 | |||
1025 | /* wait for CTLACK and CTLACK2 to get asserted */ | ||
1026 | for (i = 0; i < 100; ++i) { | ||
1027 | uint32_t mask = UPLL_CTLACK_MASK | UPLL_CTLACK2_MASK; | ||
1028 | if ((RREG32(CG_UPLL_FUNC_CNTL) & mask) == mask) | ||
1029 | break; | ||
1030 | mdelay(10); | ||
1031 | } | ||
1032 | if (i == 100) | ||
1033 | return -ETIMEDOUT; | ||
1034 | |||
1035 | /* deassert UPLL_CTLREQ */ | ||
1036 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_CTLREQ_MASK); | ||
1037 | |||
1038 | return 0; | ||
1039 | } | ||
1040 | |||
1041 | int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | 992 | int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) |
1042 | { | 993 | { |
1043 | /* start off with something large */ | 994 | /* start off with something large */ |
1044 | int optimal_diff_score = 0x7FFFFFF; | 995 | unsigned fb_div = 0, vclk_div = 0, dclk_div = 0; |
1045 | unsigned optimal_fb_div = 0, optimal_vclk_div = 0; | ||
1046 | unsigned optimal_dclk_div = 0, optimal_vco_freq = 0; | ||
1047 | unsigned vco_freq; | ||
1048 | int r; | 996 | int r; |
1049 | 997 | ||
1050 | /* bypass vclk and dclk with bclk */ | 998 | /* bypass vclk and dclk with bclk */ |
@@ -1061,40 +1009,11 @@ int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
1061 | return 0; | 1009 | return 0; |
1062 | } | 1010 | } |
1063 | 1011 | ||
1064 | /* loop through vco from low to high */ | 1012 | r = radeon_uvd_calc_upll_dividers(rdev, vclk, dclk, 125000, 250000, |
1065 | for (vco_freq = 125000; vco_freq <= 250000; vco_freq += 100) { | 1013 | 16384, 0x03FFFFFF, 0, 128, 5, |
1066 | unsigned fb_div = vco_freq / rdev->clock.spll.reference_freq * 16384; | 1014 | &fb_div, &vclk_div, &dclk_div); |
1067 | int calc_clk, diff_score, diff_vclk, diff_dclk; | 1015 | if (r) |
1068 | unsigned vclk_div, dclk_div; | 1016 | return r; |
1069 | |||
1070 | /* fb div out of range ? */ | ||
1071 | if (fb_div > 0x03FFFFFF) | ||
1072 | break; /* it can oly get worse */ | ||
1073 | |||
1074 | /* calc vclk with current vco freq. */ | ||
1075 | calc_clk = evergreen_uvd_calc_post_div(vclk, vco_freq, &vclk_div); | ||
1076 | if (calc_clk == -1) | ||
1077 | break; /* vco is too big, it has to stop. */ | ||
1078 | diff_vclk = vclk - calc_clk; | ||
1079 | |||
1080 | /* calc dclk with current vco freq. */ | ||
1081 | calc_clk = evergreen_uvd_calc_post_div(dclk, vco_freq, &dclk_div); | ||
1082 | if (calc_clk == -1) | ||
1083 | break; /* vco is too big, it has to stop. */ | ||
1084 | diff_dclk = dclk - calc_clk; | ||
1085 | |||
1086 | /* determine if this vco setting is better than current optimal settings */ | ||
1087 | diff_score = abs(diff_vclk) + abs(diff_dclk); | ||
1088 | if (diff_score < optimal_diff_score) { | ||
1089 | optimal_fb_div = fb_div; | ||
1090 | optimal_vclk_div = vclk_div; | ||
1091 | optimal_dclk_div = dclk_div; | ||
1092 | optimal_vco_freq = vco_freq; | ||
1093 | optimal_diff_score = diff_score; | ||
1094 | if (optimal_diff_score == 0) | ||
1095 | break; /* it can't get better than this */ | ||
1096 | } | ||
1097 | } | ||
1098 | 1017 | ||
1099 | /* set VCO_MODE to 1 */ | 1018 | /* set VCO_MODE to 1 */ |
1100 | WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_VCO_MODE_MASK, ~UPLL_VCO_MODE_MASK); | 1019 | WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_VCO_MODE_MASK, ~UPLL_VCO_MODE_MASK); |
@@ -1108,7 +1027,7 @@ int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
1108 | 1027 | ||
1109 | mdelay(1); | 1028 | mdelay(1); |
1110 | 1029 | ||
1111 | r = evergreen_uvd_send_upll_ctlreq(rdev); | 1030 | r = radeon_uvd_send_upll_ctlreq(rdev, CG_UPLL_FUNC_CNTL); |
1112 | if (r) | 1031 | if (r) |
1113 | return r; | 1032 | return r; |
1114 | 1033 | ||
@@ -1119,19 +1038,19 @@ int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
1119 | WREG32_P(CG_UPLL_SPREAD_SPECTRUM, 0, ~SSEN_MASK); | 1038 | WREG32_P(CG_UPLL_SPREAD_SPECTRUM, 0, ~SSEN_MASK); |
1120 | 1039 | ||
1121 | /* set feedback divider */ | 1040 | /* set feedback divider */ |
1122 | WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(optimal_fb_div), ~UPLL_FB_DIV_MASK); | 1041 | WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(fb_div), ~UPLL_FB_DIV_MASK); |
1123 | 1042 | ||
1124 | /* set ref divider to 0 */ | 1043 | /* set ref divider to 0 */ |
1125 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_REF_DIV_MASK); | 1044 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_REF_DIV_MASK); |
1126 | 1045 | ||
1127 | if (optimal_vco_freq < 187500) | 1046 | if (fb_div < 307200) |
1128 | WREG32_P(CG_UPLL_FUNC_CNTL_4, 0, ~UPLL_SPARE_ISPARE9); | 1047 | WREG32_P(CG_UPLL_FUNC_CNTL_4, 0, ~UPLL_SPARE_ISPARE9); |
1129 | else | 1048 | else |
1130 | WREG32_P(CG_UPLL_FUNC_CNTL_4, UPLL_SPARE_ISPARE9, ~UPLL_SPARE_ISPARE9); | 1049 | WREG32_P(CG_UPLL_FUNC_CNTL_4, UPLL_SPARE_ISPARE9, ~UPLL_SPARE_ISPARE9); |
1131 | 1050 | ||
1132 | /* set PDIV_A and PDIV_B */ | 1051 | /* set PDIV_A and PDIV_B */ |
1133 | WREG32_P(CG_UPLL_FUNC_CNTL_2, | 1052 | WREG32_P(CG_UPLL_FUNC_CNTL_2, |
1134 | UPLL_PDIV_A(optimal_vclk_div) | UPLL_PDIV_B(optimal_dclk_div), | 1053 | UPLL_PDIV_A(vclk_div) | UPLL_PDIV_B(dclk_div), |
1135 | ~(UPLL_PDIV_A_MASK | UPLL_PDIV_B_MASK)); | 1054 | ~(UPLL_PDIV_A_MASK | UPLL_PDIV_B_MASK)); |
1136 | 1055 | ||
1137 | /* give the PLL some time to settle */ | 1056 | /* give the PLL some time to settle */ |
@@ -1145,7 +1064,7 @@ int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
1145 | /* switch from bypass mode to normal mode */ | 1064 | /* switch from bypass mode to normal mode */ |
1146 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_BYPASS_EN_MASK); | 1065 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_BYPASS_EN_MASK); |
1147 | 1066 | ||
1148 | r = evergreen_uvd_send_upll_ctlreq(rdev); | 1067 | r = radeon_uvd_send_upll_ctlreq(rdev, CG_UPLL_FUNC_CNTL); |
1149 | if (r) | 1068 | if (r) |
1150 | return r; | 1069 | return r; |
1151 | 1070 | ||
diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h index 6105b25b18c3..acb146c06973 100644 --- a/drivers/gpu/drm/radeon/r600d.h +++ b/drivers/gpu/drm/radeon/r600d.h | |||
@@ -1208,6 +1208,10 @@ | |||
1208 | 1208 | ||
1209 | #define UVD_CONTEXT_ID 0xf6f4 | 1209 | #define UVD_CONTEXT_ID 0xf6f4 |
1210 | 1210 | ||
1211 | # define UPLL_CTLREQ_MASK 0x00000008 | ||
1212 | # define UPLL_CTLACK_MASK 0x40000000 | ||
1213 | # define UPLL_CTLACK2_MASK 0x80000000 | ||
1214 | |||
1211 | /* | 1215 | /* |
1212 | * PM4 | 1216 | * PM4 |
1213 | */ | 1217 | */ |
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 3ef7543a2986..1442ce765d48 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h | |||
@@ -1162,6 +1162,17 @@ void radeon_uvd_free_handles(struct radeon_device *rdev, | |||
1162 | struct drm_file *filp); | 1162 | struct drm_file *filp); |
1163 | int radeon_uvd_cs_parse(struct radeon_cs_parser *parser); | 1163 | int radeon_uvd_cs_parse(struct radeon_cs_parser *parser); |
1164 | void radeon_uvd_note_usage(struct radeon_device *rdev); | 1164 | void radeon_uvd_note_usage(struct radeon_device *rdev); |
1165 | int radeon_uvd_calc_upll_dividers(struct radeon_device *rdev, | ||
1166 | unsigned vclk, unsigned dclk, | ||
1167 | unsigned vco_min, unsigned vco_max, | ||
1168 | unsigned fb_factor, unsigned fb_mask, | ||
1169 | unsigned pd_min, unsigned pd_max, | ||
1170 | unsigned pd_even, | ||
1171 | unsigned *optimal_fb_div, | ||
1172 | unsigned *optimal_vclk_div, | ||
1173 | unsigned *optimal_dclk_div); | ||
1174 | int radeon_uvd_send_upll_ctlreq(struct radeon_device *rdev, | ||
1175 | unsigned cg_upll_func_cntl); | ||
1165 | 1176 | ||
1166 | struct r600_audio { | 1177 | struct r600_audio { |
1167 | int channels; | 1178 | int channels; |
diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c index 0312a7f4d768..906e5c0ca3b9 100644 --- a/drivers/gpu/drm/radeon/radeon_uvd.c +++ b/drivers/gpu/drm/radeon/radeon_uvd.c | |||
@@ -692,3 +692,140 @@ void radeon_uvd_note_usage(struct radeon_device *rdev) | |||
692 | if (set_clocks) | 692 | if (set_clocks) |
693 | radeon_set_uvd_clocks(rdev, 53300, 40000); | 693 | radeon_set_uvd_clocks(rdev, 53300, 40000); |
694 | } | 694 | } |
695 | |||
696 | static unsigned radeon_uvd_calc_upll_post_div(unsigned vco_freq, | ||
697 | unsigned target_freq, | ||
698 | unsigned pd_min, | ||
699 | unsigned pd_even) | ||
700 | { | ||
701 | unsigned post_div = vco_freq / target_freq; | ||
702 | |||
703 | /* adjust to post divider minimum value */ | ||
704 | if (post_div < pd_min) | ||
705 | post_div = pd_min; | ||
706 | |||
707 | /* we alway need a frequency less than or equal the target */ | ||
708 | if ((vco_freq / post_div) > target_freq) | ||
709 | post_div += 1; | ||
710 | |||
711 | /* post dividers above a certain value must be even */ | ||
712 | if (post_div > pd_even && post_div % 2) | ||
713 | post_div += 1; | ||
714 | |||
715 | return post_div; | ||
716 | } | ||
717 | |||
718 | /** | ||
719 | * radeon_uvd_calc_upll_dividers - calc UPLL clock dividers | ||
720 | * | ||
721 | * @rdev: radeon_device pointer | ||
722 | * @vclk: wanted VCLK | ||
723 | * @dclk: wanted DCLK | ||
724 | * @vco_min: minimum VCO frequency | ||
725 | * @vco_max: maximum VCO frequency | ||
726 | * @fb_factor: factor to multiply vco freq with | ||
727 | * @fb_mask: limit and bitmask for feedback divider | ||
728 | * @pd_min: post divider minimum | ||
729 | * @pd_max: post divider maximum | ||
730 | * @pd_even: post divider must be even above this value | ||
731 | * @optimal_fb_div: resulting feedback divider | ||
732 | * @optimal_vclk_div: resulting vclk post divider | ||
733 | * @optimal_dclk_div: resulting dclk post divider | ||
734 | * | ||
735 | * Calculate dividers for UVDs UPLL (R6xx-SI, except APUs). | ||
736 | * Returns zero on success -EINVAL on error. | ||
737 | */ | ||
738 | int radeon_uvd_calc_upll_dividers(struct radeon_device *rdev, | ||
739 | unsigned vclk, unsigned dclk, | ||
740 | unsigned vco_min, unsigned vco_max, | ||
741 | unsigned fb_factor, unsigned fb_mask, | ||
742 | unsigned pd_min, unsigned pd_max, | ||
743 | unsigned pd_even, | ||
744 | unsigned *optimal_fb_div, | ||
745 | unsigned *optimal_vclk_div, | ||
746 | unsigned *optimal_dclk_div) | ||
747 | { | ||
748 | unsigned vco_freq, ref_freq = rdev->clock.spll.reference_freq; | ||
749 | |||
750 | /* start off with something large */ | ||
751 | unsigned optimal_score = ~0; | ||
752 | |||
753 | /* loop through vco from low to high */ | ||
754 | vco_min = max(max(vco_min, vclk), dclk); | ||
755 | for (vco_freq = vco_min; vco_freq <= vco_max; vco_freq += 100) { | ||
756 | |||
757 | uint64_t fb_div = (uint64_t)vco_freq * fb_factor; | ||
758 | unsigned vclk_div, dclk_div, score; | ||
759 | |||
760 | do_div(fb_div, ref_freq); | ||
761 | |||
762 | /* fb div out of range ? */ | ||
763 | if (fb_div > fb_mask) | ||
764 | break; /* it can oly get worse */ | ||
765 | |||
766 | fb_div &= fb_mask; | ||
767 | |||
768 | /* calc vclk divider with current vco freq */ | ||
769 | vclk_div = radeon_uvd_calc_upll_post_div(vco_freq, vclk, | ||
770 | pd_min, pd_even); | ||
771 | if (vclk_div > pd_max) | ||
772 | break; /* vco is too big, it has to stop */ | ||
773 | |||
774 | /* calc dclk divider with current vco freq */ | ||
775 | dclk_div = radeon_uvd_calc_upll_post_div(vco_freq, dclk, | ||
776 | pd_min, pd_even); | ||
777 | if (vclk_div > pd_max) | ||
778 | break; /* vco is too big, it has to stop */ | ||
779 | |||
780 | /* calc score with current vco freq */ | ||
781 | score = vclk - (vco_freq / vclk_div) + dclk - (vco_freq / dclk_div); | ||
782 | |||
783 | /* determine if this vco setting is better than current optimal settings */ | ||
784 | if (score < optimal_score) { | ||
785 | *optimal_fb_div = fb_div; | ||
786 | *optimal_vclk_div = vclk_div; | ||
787 | *optimal_dclk_div = dclk_div; | ||
788 | optimal_score = score; | ||
789 | if (optimal_score == 0) | ||
790 | break; /* it can't get better than this */ | ||
791 | } | ||
792 | } | ||
793 | |||
794 | /* did we found a valid setup ? */ | ||
795 | if (optimal_score == ~0) | ||
796 | return -EINVAL; | ||
797 | |||
798 | return 0; | ||
799 | } | ||
800 | |||
801 | int radeon_uvd_send_upll_ctlreq(struct radeon_device *rdev, | ||
802 | unsigned cg_upll_func_cntl) | ||
803 | { | ||
804 | unsigned i; | ||
805 | |||
806 | /* make sure UPLL_CTLREQ is deasserted */ | ||
807 | WREG32_P(cg_upll_func_cntl, 0, ~UPLL_CTLREQ_MASK); | ||
808 | |||
809 | mdelay(10); | ||
810 | |||
811 | /* assert UPLL_CTLREQ */ | ||
812 | WREG32_P(cg_upll_func_cntl, UPLL_CTLREQ_MASK, ~UPLL_CTLREQ_MASK); | ||
813 | |||
814 | /* wait for CTLACK and CTLACK2 to get asserted */ | ||
815 | for (i = 0; i < 100; ++i) { | ||
816 | uint32_t mask = UPLL_CTLACK_MASK | UPLL_CTLACK2_MASK; | ||
817 | if ((RREG32(cg_upll_func_cntl) & mask) == mask) | ||
818 | break; | ||
819 | mdelay(10); | ||
820 | } | ||
821 | |||
822 | /* deassert UPLL_CTLREQ */ | ||
823 | WREG32_P(cg_upll_func_cntl, 0, ~UPLL_CTLREQ_MASK); | ||
824 | |||
825 | if (i == 100) { | ||
826 | DRM_ERROR("Timeout setting UVD clocks!\n"); | ||
827 | return -ETIMEDOUT; | ||
828 | } | ||
829 | |||
830 | return 0; | ||
831 | } | ||
diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index 91530d4c11c4..83f612a9500b 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c | |||
@@ -44,56 +44,9 @@ void rv770_fini(struct radeon_device *rdev); | |||
44 | static void rv770_pcie_gen2_enable(struct radeon_device *rdev); | 44 | static void rv770_pcie_gen2_enable(struct radeon_device *rdev); |
45 | int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk); | 45 | int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk); |
46 | 46 | ||
47 | static int rv770_uvd_calc_post_div(unsigned target_freq, | ||
48 | unsigned vco_freq, | ||
49 | unsigned *div) | ||
50 | { | ||
51 | /* Fclk = Fvco / PDIV */ | ||
52 | *div = vco_freq / target_freq; | ||
53 | |||
54 | /* we alway need a frequency less than or equal the target */ | ||
55 | if ((vco_freq / *div) > target_freq) | ||
56 | *div += 1; | ||
57 | |||
58 | /* out of range ? */ | ||
59 | if (*div > 30) | ||
60 | return -1; /* forget it */ | ||
61 | |||
62 | *div -= 1; | ||
63 | return vco_freq / (*div + 1); | ||
64 | } | ||
65 | |||
66 | static int rv770_uvd_send_upll_ctlreq(struct radeon_device *rdev) | ||
67 | { | ||
68 | unsigned i; | ||
69 | |||
70 | /* assert UPLL_CTLREQ */ | ||
71 | WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_CTLREQ_MASK, ~UPLL_CTLREQ_MASK); | ||
72 | |||
73 | /* wait for CTLACK and CTLACK2 to get asserted */ | ||
74 | for (i = 0; i < 100; ++i) { | ||
75 | uint32_t mask = UPLL_CTLACK_MASK | UPLL_CTLACK2_MASK; | ||
76 | if ((RREG32(CG_UPLL_FUNC_CNTL) & mask) == mask) | ||
77 | break; | ||
78 | mdelay(10); | ||
79 | } | ||
80 | if (i == 100) | ||
81 | return -ETIMEDOUT; | ||
82 | |||
83 | /* deassert UPLL_CTLREQ */ | ||
84 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_CTLREQ_MASK); | ||
85 | |||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | 47 | int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) |
90 | { | 48 | { |
91 | /* start off with something large */ | 49 | unsigned fb_div = 0, vclk_div = 0, dclk_div = 0; |
92 | int optimal_diff_score = 0x7FFFFFF; | ||
93 | unsigned optimal_fb_div = 0, optimal_vclk_div = 0; | ||
94 | unsigned optimal_dclk_div = 0, optimal_vco_freq = 0; | ||
95 | unsigned vco_freq, vco_min = 50000, vco_max = 160000; | ||
96 | unsigned ref_freq = rdev->clock.spll.reference_freq; | ||
97 | int r; | 50 | int r; |
98 | 51 | ||
99 | /* RV740 uses evergreen uvd clk programming */ | 52 | /* RV740 uses evergreen uvd clk programming */ |
@@ -111,44 +64,15 @@ int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
111 | return 0; | 64 | return 0; |
112 | } | 65 | } |
113 | 66 | ||
114 | /* loop through vco from low to high */ | 67 | r = radeon_uvd_calc_upll_dividers(rdev, vclk, dclk, 50000, 160000, |
115 | vco_min = max(max(vco_min, vclk), dclk); | 68 | 43663, 0x03FFFFFE, 1, 30, ~0, |
116 | for (vco_freq = vco_min; vco_freq <= vco_max; vco_freq += 500) { | 69 | &fb_div, &vclk_div, &dclk_div); |
117 | uint64_t fb_div = (uint64_t)vco_freq * 43663; | 70 | if (r) |
118 | int calc_clk, diff_score, diff_vclk, diff_dclk; | 71 | return r; |
119 | unsigned vclk_div, dclk_div; | 72 | |
120 | 73 | fb_div |= 1; | |
121 | do_div(fb_div, ref_freq); | 74 | vclk_div -= 1; |
122 | fb_div |= 1; | 75 | dclk_div -= 1; |
123 | |||
124 | /* fb div out of range ? */ | ||
125 | if (fb_div > 0x03FFFFFF) | ||
126 | break; /* it can oly get worse */ | ||
127 | |||
128 | /* calc vclk with current vco freq. */ | ||
129 | calc_clk = rv770_uvd_calc_post_div(vclk, vco_freq, &vclk_div); | ||
130 | if (calc_clk == -1) | ||
131 | break; /* vco is too big, it has to stop. */ | ||
132 | diff_vclk = vclk - calc_clk; | ||
133 | |||
134 | /* calc dclk with current vco freq. */ | ||
135 | calc_clk = rv770_uvd_calc_post_div(dclk, vco_freq, &dclk_div); | ||
136 | if (calc_clk == -1) | ||
137 | break; /* vco is too big, it has to stop. */ | ||
138 | diff_dclk = dclk - calc_clk; | ||
139 | |||
140 | /* determine if this vco setting is better than current optimal settings */ | ||
141 | diff_score = abs(diff_vclk) + abs(diff_dclk); | ||
142 | if (diff_score < optimal_diff_score) { | ||
143 | optimal_fb_div = fb_div; | ||
144 | optimal_vclk_div = vclk_div; | ||
145 | optimal_dclk_div = dclk_div; | ||
146 | optimal_vco_freq = vco_freq; | ||
147 | optimal_diff_score = diff_score; | ||
148 | if (optimal_diff_score == 0) | ||
149 | break; /* it can't get better than this */ | ||
150 | } | ||
151 | } | ||
152 | 76 | ||
153 | /* set UPLL_FB_DIV to 0x50000 */ | 77 | /* set UPLL_FB_DIV to 0x50000 */ |
154 | WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(0x50000), ~UPLL_FB_DIV_MASK); | 78 | WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(0x50000), ~UPLL_FB_DIV_MASK); |
@@ -160,7 +84,7 @@ int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
160 | WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK); | 84 | WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK); |
161 | WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(1), ~UPLL_FB_DIV(1)); | 85 | WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(1), ~UPLL_FB_DIV(1)); |
162 | 86 | ||
163 | r = rv770_uvd_send_upll_ctlreq(rdev); | 87 | r = radeon_uvd_send_upll_ctlreq(rdev, CG_UPLL_FUNC_CNTL); |
164 | if (r) | 88 | if (r) |
165 | return r; | 89 | return r; |
166 | 90 | ||
@@ -170,13 +94,13 @@ int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
170 | /* set the required FB_DIV, REF_DIV, Post divder values */ | 94 | /* set the required FB_DIV, REF_DIV, Post divder values */ |
171 | WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_REF_DIV(1), ~UPLL_REF_DIV_MASK); | 95 | WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_REF_DIV(1), ~UPLL_REF_DIV_MASK); |
172 | WREG32_P(CG_UPLL_FUNC_CNTL_2, | 96 | WREG32_P(CG_UPLL_FUNC_CNTL_2, |
173 | UPLL_SW_HILEN(optimal_vclk_div >> 1) | | 97 | UPLL_SW_HILEN(vclk_div >> 1) | |
174 | UPLL_SW_LOLEN((optimal_vclk_div >> 1) + (optimal_vclk_div & 1)) | | 98 | UPLL_SW_LOLEN((vclk_div >> 1) + (vclk_div & 1)) | |
175 | UPLL_SW_HILEN2(optimal_dclk_div >> 1) | | 99 | UPLL_SW_HILEN2(dclk_div >> 1) | |
176 | UPLL_SW_LOLEN2((optimal_dclk_div >> 1) + (optimal_dclk_div & 1)), | 100 | UPLL_SW_LOLEN2((dclk_div >> 1) + (dclk_div & 1)), |
177 | ~UPLL_SW_MASK); | 101 | ~UPLL_SW_MASK); |
178 | 102 | ||
179 | WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(optimal_fb_div), | 103 | WREG32_P(CG_UPLL_FUNC_CNTL_3, UPLL_FB_DIV(fb_div), |
180 | ~UPLL_FB_DIV_MASK); | 104 | ~UPLL_FB_DIV_MASK); |
181 | 105 | ||
182 | /* give the PLL some time to settle */ | 106 | /* give the PLL some time to settle */ |
@@ -191,7 +115,7 @@ int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) | |||
191 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_BYPASS_EN_MASK); | 115 | WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_BYPASS_EN_MASK); |
192 | WREG32_P(CG_UPLL_FUNC_CNTL_3, 0, ~UPLL_FB_DIV(1)); | 116 | WREG32_P(CG_UPLL_FUNC_CNTL_3, 0, ~UPLL_FB_DIV(1)); |
193 | 117 | ||
194 | r = rv770_uvd_send_upll_ctlreq(rdev); | 118 | r = radeon_uvd_send_upll_ctlreq(rdev, CG_UPLL_FUNC_CNTL); |
195 | if (r) | 119 | if (r) |
196 | return r; | 120 | return r; |
197 | 121 | ||
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 | ||