diff options
author | Christian König <christian.koenig@amd.com> | 2014-04-20 07:24:32 -0400 |
---|---|---|
committer | Christian König <christian.koenig@amd.com> | 2014-04-20 11:16:12 -0400 |
commit | c2fb3094669a3205f16a32f4119d0afe40b1a1fd (patch) | |
tree | afd86e556d3d300c0302e98925be5a801c275430 | |
parent | 24315814239a3fdb306244c99bd076bc79db4ade (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.c | 77 |
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 | */ | ||
856 | static 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 | ||