aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian König <christian.koenig@amd.com>2014-04-20 07:24:32 -0400
committerChristian König <christian.koenig@amd.com>2014-04-20 11:16:12 -0400
commitc2fb3094669a3205f16a32f4119d0afe40b1a1fd (patch)
treeafd86e556d3d300c0302e98925be5a801c275430
parent24315814239a3fdb306244c99bd076bc79db4ade (diff)
drm/radeon: improve PLL limit handling in post div calculation
This improves the PLL parameters when we work at the limits of the allowed ranges. Signed-off-by: Christian König <christian.koenig@amd.com>
-rw-r--r--drivers/gpu/drm/radeon/radeon_display.c77
1 files changed, 51 insertions, 26 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index e6c3c5488259..8d99d5ee8014 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -840,6 +840,38 @@ static void avivo_reduce_ratio(unsigned *nom, unsigned *den,
840} 840}
841 841
842/** 842/**
843 * avivo_get_fb_ref_div - feedback and ref divider calculation
844 *
845 * @nom: nominator
846 * @den: denominator
847 * @post_div: post divider
848 * @fb_div_max: feedback divider maximum
849 * @ref_div_max: reference divider maximum
850 * @fb_div: resulting feedback divider
851 * @ref_div: resulting reference divider
852 *
853 * Calculate feedback and reference divider for a given post divider. Makes
854 * sure we stay within the limits.
855 */
856static void avivo_get_fb_ref_div(unsigned nom, unsigned den, unsigned post_div,
857 unsigned fb_div_max, unsigned ref_div_max,
858 unsigned *fb_div, unsigned *ref_div)
859{
860 /* limit reference * post divider to a maximum */
861 ref_div_max = min(210 / post_div, ref_div_max);
862
863 /* get matching reference and feedback divider */
864 *ref_div = min(max(DIV_ROUND_CLOSEST(den, post_div), 1u), ref_div_max);
865 *fb_div = DIV_ROUND_CLOSEST(nom * *ref_div * post_div, den);
866
867 /* limit fb divider to its maximum */
868 if (*fb_div > fb_div_max) {
869 *ref_div = DIV_ROUND_CLOSEST(*ref_div * fb_div_max, *fb_div);
870 *fb_div = fb_div_max;
871 }
872}
873
874/**
843 * radeon_compute_pll_avivo - compute PLL paramaters 875 * radeon_compute_pll_avivo - compute PLL paramaters
844 * 876 *
845 * @pll: information about the PLL 877 * @pll: information about the PLL
@@ -860,6 +892,9 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll,
860 u32 *ref_div_p, 892 u32 *ref_div_p,
861 u32 *post_div_p) 893 u32 *post_div_p)
862{ 894{
895 unsigned target_clock = pll->flags & RADEON_PLL_USE_FRAC_FB_DIV ?
896 freq : freq / 10;
897
863 unsigned fb_div_min, fb_div_max, fb_div; 898 unsigned fb_div_min, fb_div_max, fb_div;
864 unsigned post_div_min, post_div_max, post_div; 899 unsigned post_div_min, post_div_max, post_div;
865 unsigned ref_div_min, ref_div_max, ref_div; 900 unsigned ref_div_min, ref_div_max, ref_div;
@@ -892,7 +927,6 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll,
892 post_div_min = pll->post_div; 927 post_div_min = pll->post_div;
893 post_div_max = pll->post_div; 928 post_div_max = pll->post_div;
894 } else { 929 } else {
895 unsigned target_clock = freq / 10;
896 unsigned vco_min, vco_max; 930 unsigned vco_min, vco_max;
897 931
898 if (pll->flags & RADEON_PLL_IS_LCD) { 932 if (pll->flags & RADEON_PLL_IS_LCD) {
@@ -903,6 +937,11 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll,
903 vco_max = pll->pll_out_max; 937 vco_max = pll->pll_out_max;
904 } 938 }
905 939
940 if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) {
941 vco_min *= 10;
942 vco_max *= 10;
943 }
944
906 post_div_min = vco_min / target_clock; 945 post_div_min = vco_min / target_clock;
907 if ((target_clock * post_div_min) < vco_min) 946 if ((target_clock * post_div_min) < vco_min)
908 ++post_div_min; 947 ++post_div_min;
@@ -917,7 +956,7 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll,
917 } 956 }
918 957
919 /* represent the searched ratio as fractional number */ 958 /* represent the searched ratio as fractional number */
920 nom = pll->flags & RADEON_PLL_USE_FRAC_FB_DIV ? freq : freq / 10; 959 nom = target_clock;
921 den = pll->reference_freq; 960 den = pll->reference_freq;
922 961
923 /* reduce the numbers to a simpler ratio */ 962 /* reduce the numbers to a simpler ratio */
@@ -931,7 +970,12 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll,
931 diff_best = ~0; 970 diff_best = ~0;
932 971
933 for (post_div = post_div_min; post_div <= post_div_max; ++post_div) { 972 for (post_div = post_div_min; post_div <= post_div_max; ++post_div) {
934 unsigned diff = abs(den - den / post_div * post_div); 973 unsigned diff;
974 avivo_get_fb_ref_div(nom, den, post_div, fb_div_max,
975 ref_div_max, &fb_div, &ref_div);
976 diff = abs(target_clock - (pll->reference_freq * fb_div) /
977 (ref_div * post_div));
978
935 if (diff < diff_best || (diff == diff_best && 979 if (diff < diff_best || (diff == diff_best &&
936 !(pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP))) { 980 !(pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP))) {
937 981
@@ -941,28 +985,9 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll,
941 } 985 }
942 post_div = post_div_best; 986 post_div = post_div_best;
943 987
944 /* limit reference * post divider to a maximum */ 988 /* get the feedback and reference divider for the optimal value */
945 ref_div_max = min(210 / post_div, ref_div_max); 989 avivo_get_fb_ref_div(nom, den, post_div, fb_div_max, ref_div_max,
946 990 &fb_div, &ref_div);
947 /* get matching reference and feedback divider */
948 ref_div = max(DIV_ROUND_CLOSEST(den, post_div), 1u);
949 fb_div = DIV_ROUND_CLOSEST(nom * ref_div * post_div, den);
950
951 /* we're almost done, but reference and feedback
952 divider might be to large now */
953
954 nom = fb_div;
955 den = ref_div;
956
957 if (fb_div > fb_div_max) {
958 ref_div = DIV_ROUND_CLOSEST(den * fb_div_max, nom);
959 fb_div = fb_div_max;
960 }
961
962 if (ref_div > ref_div_max) {
963 ref_div = ref_div_max;
964 fb_div = DIV_ROUND_CLOSEST(nom * ref_div_max, den);
965 }
966 991
967 /* reduce the numbers to a simpler ratio once more */ 992 /* reduce the numbers to a simpler ratio once more */
968 /* this also makes sure that the reference divider is large enough */ 993 /* this also makes sure that the reference divider is large enough */
@@ -984,7 +1009,7 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll,
984 *post_div_p = post_div; 1009 *post_div_p = post_div;
985 1010
986 DRM_DEBUG_KMS("%d - %d, pll dividers - fb: %d.%d ref: %d, post %d\n", 1011 DRM_DEBUG_KMS("%d - %d, pll dividers - fb: %d.%d ref: %d, post %d\n",
987 freq, *dot_clock_p, *fb_div_p, *frac_fb_div_p, 1012 freq, *dot_clock_p * 10, *fb_div_p, *frac_fb_div_p,
988 ref_div, post_div); 1013 ref_div, post_div);
989} 1014}
990 1015