Skip to content

Commit

Permalink
drm/radeon: improve PLL limit handling in post div calculation
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Christian König committed Apr 20, 2014
1 parent 2431581 commit c2fb309
Showing 1 changed file with 51 additions and 26 deletions.
77 changes: 51 additions & 26 deletions drivers/gpu/drm/radeon/radeon_display.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
Expand All @@ -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 */
Expand All @@ -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))) {

Expand All @@ -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 */
Expand All @@ -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);
}

Expand Down

0 comments on commit c2fb309

Please sign in to comment.