aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorChristian König <christian.koenig@amd.com>2013-04-29 05:55:02 -0400
committerAlex Deucher <alexander.deucher@amd.com>2013-05-02 10:09:48 -0400
commitfacd112d1395fb6a0b6e460778aefc32197afcfc (patch)
tree69cc696021ccfe1eb1d78a7aedfb32f984675118 /drivers/gpu
parent092fbc4ca29a3d78895673479f794ee162a13ac5 (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')
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c103
-rw-r--r--drivers/gpu/drm/radeon/r600d.h4
-rw-r--r--drivers/gpu/drm/radeon/radeon.h11
-rw-r--r--drivers/gpu/drm/radeon/radeon_uvd.c137
-rw-r--r--drivers/gpu/drm/radeon/rv770.c110
-rw-r--r--drivers/gpu/drm/radeon/si.c104
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
992static 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
1018static 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
1041int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) 992int 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);
1163int radeon_uvd_cs_parse(struct radeon_cs_parser *parser); 1163int radeon_uvd_cs_parse(struct radeon_cs_parser *parser);
1164void radeon_uvd_note_usage(struct radeon_device *rdev); 1164void radeon_uvd_note_usage(struct radeon_device *rdev);
1165int 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);
1174int radeon_uvd_send_upll_ctlreq(struct radeon_device *rdev,
1175 unsigned cg_upll_func_cntl);
1165 1176
1166struct r600_audio { 1177struct 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
696static 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 */
738int 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
801int 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);
44static void rv770_pcie_gen2_enable(struct radeon_device *rdev); 44static void rv770_pcie_gen2_enable(struct radeon_device *rdev);
45int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk); 45int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk);
46 46
47static 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
66static 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
89int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) 47int 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
5418static 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
5444static 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
5467int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) 5418int 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