@@ -34,8 +34,6 @@
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
-#include <linux/gcd.h>
-
static void avivo_crtc_load_lut(struct drm_crtc *crtc)
{
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
@@ -802,57 +800,66 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector)
}
/* avivo */
+static void avivo_get_fb_div(struct radeon_pll *pll,
+ u32 target_clock,
+ u32 post_div,
+ u32 ref_div,
+ u32 *fb_div,
+ u32 *frac_fb_div)
+{
+ u32 tmp = post_div * ref_div;
-/**
- * avivo_reduce_ratio - fractional number reduction
- *
- * @nom: nominator
- * @den: denominator
- * @nom_min: minimum value for nominator
- * @den_min: minimum value for denominator
- *
- * Find the greatest common divisor and apply it on both nominator and
- * denominator, but make nominator and denominator are at least as large
- * as their minimum values.
- */
-static void avivo_reduce_ratio(unsigned *nom, unsigned *den,
- unsigned nom_min, unsigned den_min)
+ tmp *= target_clock;
+ *fb_div = tmp / pll->reference_freq;
+ *frac_fb_div = tmp % pll->reference_freq;
+
+ if (*fb_div > pll->max_feedback_div)
+ *fb_div = pll->max_feedback_div;
+ else if (*fb_div < pll->min_feedback_div)
+ *fb_div = pll->min_feedback_div;
+}
+
+static u32 avivo_get_post_div(struct radeon_pll *pll,
+ u32 target_clock)
{
- unsigned tmp;
-
- /* reduce the numbers to a simpler ratio */
- tmp = gcd(*nom, *den);
- *nom /= tmp;
- *den /= tmp;
-
- /* make sure nominator is large enough */
- if (*nom < nom_min) {
- tmp = (nom_min + *nom - 1) / *nom;
- *nom *= tmp;
- *den *= tmp;
+ u32 vco, post_div, tmp;
+
+ if (pll->flags & RADEON_PLL_USE_POST_DIV)
+ return pll->post_div;
+
+ if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) {
+ if (pll->flags & RADEON_PLL_IS_LCD)
+ vco = pll->lcd_pll_out_min;
+ else
+ vco = pll->pll_out_min;
+ } else {
+ if (pll->flags & RADEON_PLL_IS_LCD)
+ vco = pll->lcd_pll_out_max;
+ else
+ vco = pll->pll_out_max;
}
- /* make sure the denominator is large enough */
- if (*den < den_min) {
- tmp = (den_min + *den - 1) / *den;
- *nom *= tmp;
- *den *= tmp;
+ post_div = vco / target_clock;
+ tmp = vco % target_clock;
+
+ if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) {
+ if (tmp)
+ post_div++;
+ } else {
+ if (!tmp)
+ post_div--;
}
+
+ if (post_div > pll->max_post_div)
+ post_div = pll->max_post_div;
+ else if (post_div < pll->min_post_div)
+ post_div = pll->min_post_div;
+
+ return post_div;
}
-/**
- * radeon_compute_pll_avivo - compute PLL paramaters
- *
- * @pll: information about the PLL
- * @dot_clock_p: resulting pixel clock
- * fb_div_p: resulting feedback divider
- * frac_fb_div_p: fractional part of the feedback divider
- * ref_div_p: resulting reference divider
- * post_div_p: resulting reference divider
- *
- * Try to calculate the PLL parameters to generate the given frequency:
- * dot_clock = (ref_freq * feedback_div) / (ref_div * post_div)
- */
+#define MAX_TOLERANCE 10
+
void radeon_compute_pll_avivo(struct radeon_pll *pll,
u32 freq,
u32 *dot_clock_p,
@@ -861,126 +868,53 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll,
u32 *ref_div_p,
u32 *post_div_p)
{
- 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;
- unsigned post_div_best, diff_best;
- unsigned nom, den, tmp;
-
- /* determine allowed feedback divider range */
- fb_div_min = pll->min_feedback_div;
- fb_div_max = pll->max_feedback_div;
+ u32 target_clock = freq / 10;
+ u32 post_div = avivo_get_post_div(pll, target_clock);
+ u32 ref_div = pll->min_ref_div;
+ u32 fb_div = 0, frac_fb_div = 0, tmp;
- if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) {
- fb_div_min *= 10;
- fb_div_max *= 10;
- }
-
- /* determine allowed ref divider range */
if (pll->flags & RADEON_PLL_USE_REF_DIV)
- ref_div_min = pll->reference_div;
- else
- ref_div_min = pll->min_ref_div;
- ref_div_max = pll->max_ref_div;
+ ref_div = pll->reference_div;
- /* determine allowed post divider range */
- if (pll->flags & RADEON_PLL_USE_POST_DIV) {
- 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) {
- vco_min = pll->lcd_pll_out_min;
- vco_max = pll->lcd_pll_out_max;
- } else {
- vco_min = pll->pll_out_min;
- vco_max = pll->pll_out_max;
+ if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) {
+ avivo_get_fb_div(pll, target_clock, post_div, ref_div, &fb_div, &frac_fb_div);
+ frac_fb_div = (100 * frac_fb_div) / pll->reference_freq;
+ if (frac_fb_div >= 5) {
+ frac_fb_div -= 5;
+ frac_fb_div = frac_fb_div / 10;
+ frac_fb_div++;
}
-
- post_div_min = vco_min / target_clock;
- if ((target_clock * post_div_min) < vco_min)
- ++post_div_min;
- if (post_div_min < pll->min_post_div)
- post_div_min = pll->min_post_div;
-
- post_div_max = vco_max / target_clock;
- if ((target_clock * post_div_max) > vco_max)
- --post_div_max;
- if (post_div_max > pll->max_post_div)
- post_div_max = pll->max_post_div;
- }
-
- /* represent the searched ratio as fractional number */
- nom = pll->flags & RADEON_PLL_USE_FRAC_FB_DIV ? freq : freq / 10;
- den = pll->reference_freq;
-
- /* reduce the numbers to a simpler ratio */
- avivo_reduce_ratio(&nom, &den, fb_div_min, post_div_min);
-
- /* now search for a post divider */
- if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP)
- post_div_best = post_div_min;
- else
- post_div_best = post_div_max;
- 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);
- if (diff < diff_best || (diff == diff_best &&
- !(pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP))) {
-
- post_div_best = post_div;
- diff_best = diff;
+ if (frac_fb_div >= 10) {
+ fb_div++;
+ frac_fb_div = 0;
}
- }
- 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(den / post_div, 1u);
- fb_div = nom;
-
- /* we're almost done, but reference and feedback
- divider might be to large now */
-
- tmp = ref_div;
-
- if (fb_div > fb_div_max) {
- ref_div = ref_div * fb_div_max / fb_div;
- fb_div = fb_div_max;
- }
-
- if (ref_div > ref_div_max) {
- ref_div = ref_div_max;
- fb_div = nom * ref_div_max / tmp;
- }
-
- /* reduce the numbers to a simpler ratio once more */
- /* this also makes sure that the reference divider is large enough */
- avivo_reduce_ratio(&fb_div, &ref_div, fb_div_min, ref_div_min);
-
- /* and finally save the result */
- if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) {
- *fb_div_p = fb_div / 10;
- *frac_fb_div_p = fb_div % 10;
} else {
- *fb_div_p = fb_div;
- *frac_fb_div_p = 0;
+ while (ref_div <= pll->max_ref_div) {
+ avivo_get_fb_div(pll, target_clock, post_div, ref_div,
+ &fb_div, &frac_fb_div);
+ if (frac_fb_div >= (pll->reference_freq / 2))
+ fb_div++;
+ frac_fb_div = 0;
+ tmp = (pll->reference_freq * fb_div) / (post_div * ref_div);
+ tmp = (tmp * 10000) / target_clock;
+
+ if (tmp > (10000 + MAX_TOLERANCE))
+ ref_div++;
+ else if (tmp >= (10000 - MAX_TOLERANCE))
+ break;
+ else
+ ref_div++;
+ }
}
- *dot_clock_p = ((pll->reference_freq * *fb_div_p * 10) +
- (pll->reference_freq * *frac_fb_div_p)) /
- (ref_div * post_div * 10);
+ *dot_clock_p = ((pll->reference_freq * fb_div * 10) + (pll->reference_freq * frac_fb_div)) /
+ (ref_div * post_div * 10);
+ *fb_div_p = fb_div;
+ *frac_fb_div_p = frac_fb_div;
*ref_div_p = ref_div;
*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,
- ref_div, post_div);
+ DRM_DEBUG_KMS("%d, pll dividers - fb: %d.%d ref: %d, post %d\n",
+ *dot_clock_p, fb_div, frac_fb_div, ref_div, post_div);
}
/* pre-avivo */