diff options
author | Christian König <christian.koenig@amd.com> | 2014-03-28 13:55:10 -0400 |
---|---|---|
committer | Christian König <christian.koenig@amd.com> | 2014-04-03 06:35:42 -0400 |
commit | 32167016076f714f0e35e287fbead7de0f1fb179 (patch) | |
tree | 8a29b19742748c57b001cd43e30158b4f1395acb /drivers/gpu/drm/radeon | |
parent | ec9954fc26719482c041991607f4329d563c4bbb (diff) |
drm/radeon: rework finding display PLL numbers v2
This completely reworks how the PLL parameters are generated and
should result in better matching dot clock frequencies.
Probably needs quite a bit of testing.
bugs: https://bugs.freedesktop.org/show_bug.cgi?id=76564
v2: more cleanup and comments.
Signed-off-by: Christian König <christian.koenig@amd.com>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm/radeon')
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_display.c | 243 |
1 files changed, 153 insertions, 90 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 5701fbb36b3c..63d54ef758fc 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c | |||
@@ -34,6 +34,8 @@ | |||
34 | #include <drm/drm_crtc_helper.h> | 34 | #include <drm/drm_crtc_helper.h> |
35 | #include <drm/drm_edid.h> | 35 | #include <drm/drm_edid.h> |
36 | 36 | ||
37 | #include <linux/gcd.h> | ||
38 | |||
37 | static void avivo_crtc_load_lut(struct drm_crtc *crtc) | 39 | static void avivo_crtc_load_lut(struct drm_crtc *crtc) |
38 | { | 40 | { |
39 | struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); | 41 | struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
@@ -799,66 +801,57 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) | |||
799 | } | 801 | } |
800 | 802 | ||
801 | /* avivo */ | 803 | /* avivo */ |
802 | static void avivo_get_fb_div(struct radeon_pll *pll, | ||
803 | u32 target_clock, | ||
804 | u32 post_div, | ||
805 | u32 ref_div, | ||
806 | u32 *fb_div, | ||
807 | u32 *frac_fb_div) | ||
808 | { | ||
809 | u32 tmp = post_div * ref_div; | ||
810 | 804 | ||
811 | tmp *= target_clock; | 805 | /** |
812 | *fb_div = tmp / pll->reference_freq; | 806 | * avivo_reduce_ratio - fractional number reduction |
813 | *frac_fb_div = tmp % pll->reference_freq; | 807 | * |
814 | 808 | * @nom: nominator | |
815 | if (*fb_div > pll->max_feedback_div) | 809 | * @den: denominator |
816 | *fb_div = pll->max_feedback_div; | 810 | * @nom_min: minimum value for nominator |
817 | else if (*fb_div < pll->min_feedback_div) | 811 | * @den_min: minimum value for denominator |
818 | *fb_div = pll->min_feedback_div; | 812 | * |
819 | } | 813 | * Find the greatest common divisor and apply it on both nominator and |
820 | 814 | * denominator, but make nominator and denominator are at least as large | |
821 | static u32 avivo_get_post_div(struct radeon_pll *pll, | 815 | * as their minimum values. |
822 | u32 target_clock) | 816 | */ |
817 | static void avivo_reduce_ratio(unsigned *nom, unsigned *den, | ||
818 | unsigned nom_min, unsigned den_min) | ||
823 | { | 819 | { |
824 | u32 vco, post_div, tmp; | 820 | unsigned tmp; |
825 | 821 | ||
826 | if (pll->flags & RADEON_PLL_USE_POST_DIV) | 822 | /* reduce the numbers to a simpler ratio */ |
827 | return pll->post_div; | 823 | tmp = gcd(*nom, *den); |
828 | 824 | *nom /= tmp; | |
829 | if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) { | 825 | *den /= tmp; |
830 | if (pll->flags & RADEON_PLL_IS_LCD) | 826 | |
831 | vco = pll->lcd_pll_out_min; | 827 | /* make sure nominator is large enough */ |
832 | else | 828 | if (*nom < nom_min) { |
833 | vco = pll->pll_out_min; | 829 | tmp = (nom_min + *nom - 1) / *nom; |
834 | } else { | 830 | *nom *= tmp; |
835 | if (pll->flags & RADEON_PLL_IS_LCD) | 831 | *den *= tmp; |
836 | vco = pll->lcd_pll_out_max; | ||
837 | else | ||
838 | vco = pll->pll_out_max; | ||
839 | } | 832 | } |
840 | 833 | ||
841 | post_div = vco / target_clock; | 834 | /* make sure the denominator is large enough */ |
842 | tmp = vco % target_clock; | 835 | if (*den < den_min) { |
843 | 836 | tmp = (den_min + *den - 1) / *den; | |
844 | if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) { | 837 | *nom *= tmp; |
845 | if (tmp) | 838 | *den *= tmp; |
846 | post_div++; | ||
847 | } else { | ||
848 | if (!tmp) | ||
849 | post_div--; | ||
850 | } | 839 | } |
851 | |||
852 | if (post_div > pll->max_post_div) | ||
853 | post_div = pll->max_post_div; | ||
854 | else if (post_div < pll->min_post_div) | ||
855 | post_div = pll->min_post_div; | ||
856 | |||
857 | return post_div; | ||
858 | } | 840 | } |
859 | 841 | ||
860 | #define MAX_TOLERANCE 10 | 842 | /** |
861 | 843 | * radeon_compute_pll_avivo - compute PLL paramaters | |
844 | * | ||
845 | * @pll: information about the PLL | ||
846 | * @dot_clock_p: resulting pixel clock | ||
847 | * fb_div_p: resulting feedback divider | ||
848 | * frac_fb_div_p: fractional part of the feedback divider | ||
849 | * ref_div_p: resulting reference divider | ||
850 | * post_div_p: resulting reference divider | ||
851 | * | ||
852 | * Try to calculate the PLL parameters to generate the given frequency: | ||
853 | * dot_clock = (ref_freq * feedback_div) / (ref_div * post_div) | ||
854 | */ | ||
862 | void radeon_compute_pll_avivo(struct radeon_pll *pll, | 855 | void radeon_compute_pll_avivo(struct radeon_pll *pll, |
863 | u32 freq, | 856 | u32 freq, |
864 | u32 *dot_clock_p, | 857 | u32 *dot_clock_p, |
@@ -867,53 +860,123 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll, | |||
867 | u32 *ref_div_p, | 860 | u32 *ref_div_p, |
868 | u32 *post_div_p) | 861 | u32 *post_div_p) |
869 | { | 862 | { |
870 | u32 target_clock = freq / 10; | 863 | unsigned fb_div_min, fb_div_max, fb_div; |
871 | u32 post_div = avivo_get_post_div(pll, target_clock); | 864 | unsigned post_div_min, post_div_max, post_div; |
872 | u32 ref_div = pll->min_ref_div; | 865 | unsigned ref_div_min, ref_div_max, ref_div; |
873 | u32 fb_div = 0, frac_fb_div = 0, tmp; | 866 | unsigned post_div_best, diff_best; |
867 | unsigned nom, den, tmp; | ||
874 | 868 | ||
875 | if (pll->flags & RADEON_PLL_USE_REF_DIV) | 869 | /* determine allowed feedback divider range */ |
876 | ref_div = pll->reference_div; | 870 | fb_div_min = pll->min_feedback_div; |
871 | fb_div_max = pll->max_feedback_div; | ||
877 | 872 | ||
878 | if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { | 873 | if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { |
879 | avivo_get_fb_div(pll, target_clock, post_div, ref_div, &fb_div, &frac_fb_div); | 874 | fb_div_min *= 10; |
880 | frac_fb_div = (100 * frac_fb_div) / pll->reference_freq; | 875 | fb_div_max *= 10; |
881 | if (frac_fb_div >= 5) { | 876 | } |
882 | frac_fb_div -= 5; | 877 | |
883 | frac_fb_div = frac_fb_div / 10; | 878 | /* determine allowed ref divider range */ |
884 | frac_fb_div++; | 879 | if (pll->flags & RADEON_PLL_USE_REF_DIV) |
880 | ref_div_min = pll->reference_div; | ||
881 | else | ||
882 | ref_div_min = pll->min_ref_div; | ||
883 | ref_div_max = pll->max_ref_div; | ||
884 | |||
885 | /* determine allowed post divider range */ | ||
886 | if (pll->flags & RADEON_PLL_USE_POST_DIV) { | ||
887 | post_div_min = pll->post_div; | ||
888 | post_div_max = pll->post_div; | ||
889 | } else { | ||
890 | unsigned target_clock = freq / 10; | ||
891 | unsigned vco_min, vco_max; | ||
892 | |||
893 | if (pll->flags & RADEON_PLL_IS_LCD) { | ||
894 | vco_min = pll->lcd_pll_out_min; | ||
895 | vco_max = pll->lcd_pll_out_max; | ||
896 | } else { | ||
897 | vco_min = pll->pll_out_min; | ||
898 | vco_max = pll->pll_out_max; | ||
885 | } | 899 | } |
886 | if (frac_fb_div >= 10) { | 900 | |
887 | fb_div++; | 901 | post_div_min = vco_min / target_clock; |
888 | frac_fb_div = 0; | 902 | if ((target_clock * post_div_min) < vco_min) |
903 | ++post_div_min; | ||
904 | if (post_div_min < pll->min_post_div) | ||
905 | post_div_min = pll->min_post_div; | ||
906 | |||
907 | post_div_max = vco_max / target_clock; | ||
908 | if ((target_clock * post_div_max) > vco_max) | ||
909 | --post_div_max; | ||
910 | if (post_div_max > pll->max_post_div) | ||
911 | post_div_max = pll->max_post_div; | ||
912 | } | ||
913 | |||
914 | /* represent the searched ratio as fractional number */ | ||
915 | nom = pll->flags & RADEON_PLL_USE_FRAC_FB_DIV ? freq : freq / 10; | ||
916 | den = pll->reference_freq; | ||
917 | |||
918 | /* reduce the numbers to a simpler ratio */ | ||
919 | avivo_reduce_ratio(&nom, &den, fb_div_min, post_div_min); | ||
920 | |||
921 | /* now search for a post divider */ | ||
922 | if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) | ||
923 | post_div_best = post_div_min; | ||
924 | else | ||
925 | post_div_best = post_div_max; | ||
926 | diff_best = ~0; | ||
927 | |||
928 | for (post_div = post_div_min; post_div <= post_div_max; ++post_div) { | ||
929 | unsigned diff = abs(den - den / post_div * post_div); | ||
930 | if (diff < diff_best || (diff == diff_best && | ||
931 | !(pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP))) { | ||
932 | |||
933 | post_div_best = post_div; | ||
934 | diff_best = diff; | ||
889 | } | 935 | } |
936 | } | ||
937 | post_div = post_div_best; | ||
938 | |||
939 | /* get matching reference and feedback divider */ | ||
940 | ref_div = max(den / post_div, 1u); | ||
941 | fb_div = nom; | ||
942 | |||
943 | /* we're almost done, but reference and feedback | ||
944 | divider might be to large now */ | ||
945 | |||
946 | tmp = ref_div; | ||
947 | |||
948 | if (fb_div > fb_div_max) { | ||
949 | ref_div = ref_div * fb_div_max / fb_div; | ||
950 | fb_div = fb_div_max; | ||
951 | } | ||
952 | |||
953 | if (ref_div > ref_div_max) { | ||
954 | ref_div = ref_div_max; | ||
955 | fb_div = nom * ref_div_max / tmp; | ||
956 | } | ||
957 | |||
958 | /* reduce the numbers to a simpler ratio once more */ | ||
959 | /* this also makes sure that the reference divider is large enough */ | ||
960 | avivo_reduce_ratio(&fb_div, &ref_div, fb_div_min, ref_div_min); | ||
961 | |||
962 | /* and finally save the result */ | ||
963 | if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { | ||
964 | *fb_div_p = fb_div / 10; | ||
965 | *frac_fb_div_p = fb_div % 10; | ||
890 | } else { | 966 | } else { |
891 | while (ref_div <= pll->max_ref_div) { | 967 | *fb_div_p = fb_div; |
892 | avivo_get_fb_div(pll, target_clock, post_div, ref_div, | 968 | *frac_fb_div_p = 0; |
893 | &fb_div, &frac_fb_div); | ||
894 | if (frac_fb_div >= (pll->reference_freq / 2)) | ||
895 | fb_div++; | ||
896 | frac_fb_div = 0; | ||
897 | tmp = (pll->reference_freq * fb_div) / (post_div * ref_div); | ||
898 | tmp = (tmp * 10000) / target_clock; | ||
899 | |||
900 | if (tmp > (10000 + MAX_TOLERANCE)) | ||
901 | ref_div++; | ||
902 | else if (tmp >= (10000 - MAX_TOLERANCE)) | ||
903 | break; | ||
904 | else | ||
905 | ref_div++; | ||
906 | } | ||
907 | } | 969 | } |
908 | 970 | ||
909 | *dot_clock_p = ((pll->reference_freq * fb_div * 10) + (pll->reference_freq * frac_fb_div)) / | 971 | *dot_clock_p = ((pll->reference_freq * *fb_div_p * 10) + |
910 | (ref_div * post_div * 10); | 972 | (pll->reference_freq * *frac_fb_div_p)) / |
911 | *fb_div_p = fb_div; | 973 | (ref_div * post_div * 10); |
912 | *frac_fb_div_p = frac_fb_div; | ||
913 | *ref_div_p = ref_div; | 974 | *ref_div_p = ref_div; |
914 | *post_div_p = post_div; | 975 | *post_div_p = post_div; |
915 | DRM_DEBUG_KMS("%d, pll dividers - fb: %d.%d ref: %d, post %d\n", | 976 | |
916 | *dot_clock_p, fb_div, frac_fb_div, ref_div, post_div); | 977 | DRM_DEBUG_KMS("%d - %d, pll dividers - fb: %d.%d ref: %d, post %d\n", |
978 | freq, *dot_clock_p, *fb_div_p, *frac_fb_div_p, | ||
979 | ref_div, post_div); | ||
917 | } | 980 | } |
918 | 981 | ||
919 | /* pre-avivo */ | 982 | /* pre-avivo */ |