From c2fb3094669a3205f16a32f4119d0afe40b1a1fd Mon Sep 17 00:00:00 2001 From: =?utf8?q?Christian=20K=C3=B6nig?= Date: Sun, 20 Apr 2014 13:24:32 +0200 Subject: [PATCH] drm/radeon: improve PLL limit handling in post div calculation MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This improves the PLL parameters when we work at the limits of the allowed ranges. Signed-off-by: Christian König --- drivers/gpu/drm/radeon/radeon_display.c | 77 ++++++++++++++++--------- 1 file changed, 51 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index e6c3c548825..8d99d5ee801 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -839,6 +839,38 @@ static void avivo_reduce_ratio(unsigned *nom, unsigned *den, } } +/** + * avivo_get_fb_ref_div - feedback and ref divider calculation + * + * @nom: nominator + * @den: denominator + * @post_div: post divider + * @fb_div_max: feedback divider maximum + * @ref_div_max: reference divider maximum + * @fb_div: resulting feedback divider + * @ref_div: resulting reference divider + * + * Calculate feedback and reference divider for a given post divider. Makes + * sure we stay within the limits. + */ +static void avivo_get_fb_ref_div(unsigned nom, unsigned den, unsigned post_div, + unsigned fb_div_max, unsigned ref_div_max, + unsigned *fb_div, unsigned *ref_div) +{ + /* limit reference * post divider to a maximum */ + ref_div_max = min(210 / post_div, ref_div_max); + + /* get matching reference and feedback divider */ + *ref_div = min(max(DIV_ROUND_CLOSEST(den, post_div), 1u), ref_div_max); + *fb_div = DIV_ROUND_CLOSEST(nom * *ref_div * post_div, den); + + /* limit fb divider to its maximum */ + if (*fb_div > fb_div_max) { + *ref_div = DIV_ROUND_CLOSEST(*ref_div * fb_div_max, *fb_div); + *fb_div = fb_div_max; + } +} + /** * radeon_compute_pll_avivo - compute PLL paramaters * @@ -860,6 +892,9 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll, u32 *ref_div_p, u32 *post_div_p) { + unsigned target_clock = pll->flags & RADEON_PLL_USE_FRAC_FB_DIV ? + freq : freq / 10; + unsigned fb_div_min, fb_div_max, fb_div; unsigned post_div_min, post_div_max, post_div; unsigned ref_div_min, ref_div_max, ref_div; @@ -892,7 +927,6 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll, post_div_min = pll->post_div; post_div_max = pll->post_div; } else { - unsigned target_clock = freq / 10; unsigned vco_min, vco_max; if (pll->flags & RADEON_PLL_IS_LCD) { @@ -903,6 +937,11 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll, vco_max = pll->pll_out_max; } + if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { + vco_min *= 10; + vco_max *= 10; + } + post_div_min = vco_min / target_clock; if ((target_clock * post_div_min) < vco_min) ++post_div_min; @@ -917,7 +956,7 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll, } /* represent the searched ratio as fractional number */ - nom = pll->flags & RADEON_PLL_USE_FRAC_FB_DIV ? freq : freq / 10; + nom = target_clock; den = pll->reference_freq; /* reduce the numbers to a simpler ratio */ @@ -931,7 +970,12 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll, diff_best = ~0; for (post_div = post_div_min; post_div <= post_div_max; ++post_div) { - unsigned diff = abs(den - den / post_div * post_div); + unsigned diff; + avivo_get_fb_ref_div(nom, den, post_div, fb_div_max, + ref_div_max, &fb_div, &ref_div); + diff = abs(target_clock - (pll->reference_freq * fb_div) / + (ref_div * post_div)); + if (diff < diff_best || (diff == diff_best && !(pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP))) { @@ -941,28 +985,9 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll, } post_div = post_div_best; - /* limit reference * post divider to a maximum */ - ref_div_max = min(210 / post_div, ref_div_max); - - /* get matching reference and feedback divider */ - ref_div = max(DIV_ROUND_CLOSEST(den, post_div), 1u); - fb_div = DIV_ROUND_CLOSEST(nom * ref_div * post_div, den); - - /* we're almost done, but reference and feedback - divider might be to large now */ - - nom = fb_div; - den = ref_div; - - if (fb_div > fb_div_max) { - ref_div = DIV_ROUND_CLOSEST(den * fb_div_max, nom); - fb_div = fb_div_max; - } - - if (ref_div > ref_div_max) { - ref_div = ref_div_max; - fb_div = DIV_ROUND_CLOSEST(nom * ref_div_max, den); - } + /* get the feedback and reference divider for the optimal value */ + avivo_get_fb_ref_div(nom, den, post_div, fb_div_max, ref_div_max, + &fb_div, &ref_div); /* reduce the numbers to a simpler ratio once more */ /* this also makes sure that the reference divider is large enough */ @@ -984,7 +1009,7 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll, *post_div_p = post_div; DRM_DEBUG_KMS("%d - %d, pll dividers - fb: %d.%d ref: %d, post %d\n", - freq, *dot_clock_p, *fb_div_p, *frac_fb_div_p, + freq, *dot_clock_p * 10, *fb_div_p, *frac_fb_div_p, ref_div, post_div); } -- 2.41.0