From patchwork Thu Apr 8 12:13:06 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andy Whitcroft X-Patchwork-Id: 49725 X-Patchwork-Delegate: apw@canonical.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from chlorine.canonical.com (chlorine.canonical.com [91.189.94.204]) by ozlabs.org (Postfix) with ESMTP id 39904B7D41 for ; Thu, 8 Apr 2010 22:13:19 +1000 (EST) Received: from localhost ([127.0.0.1] helo=chlorine.canonical.com) by chlorine.canonical.com with esmtp (Exim 4.69) (envelope-from ) id 1Nzqbd-0003b0-W3; Thu, 08 Apr 2010 13:13:10 +0100 Received: from adelie.canonical.com ([91.189.90.139]) by chlorine.canonical.com with esmtp (Exim 4.69) (envelope-from ) id 1Nzqbc-0003aX-Ao for kernel-team@lists.ubuntu.com; Thu, 08 Apr 2010 13:13:08 +0100 Received: from hutte.canonical.com ([91.189.90.181]) by adelie.canonical.com with esmtp (Exim 4.69 #1 (Debian)) id 1Nzqbc-0003iz-9X for ; Thu, 08 Apr 2010 13:13:08 +0100 Received: from 79-66-242-230.dynamic.dsl.as9105.com ([79.66.242.230] helo=localhost.localdomain) by hutte.canonical.com with esmtpsa (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.69) (envelope-from ) id 1Nzqbb-0005by-Ub for kernel-team@lists.ubuntu.com; Thu, 08 Apr 2010 13:13:08 +0100 From: Andy Whitcroft To: kernel-team@lists.ubuntu.com Subject: [PATCH 2/2] drm/radeon/kms: update new pll algo Date: Thu, 8 Apr 2010 13:13:06 +0100 Message-Id: <1270728786-25734-3-git-send-email-apw@canonical.com> X-Mailer: git-send-email 1.7.0 In-Reply-To: <1270728786-25734-1-git-send-email-apw@canonical.com> References: <1270728786-25734-1-git-send-email-apw@canonical.com> X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.9 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: kernel-team-bounces@lists.ubuntu.com Errors-To: kernel-team-bounces@lists.ubuntu.com From: Alex Deucher - add support for pre-avivo chips - add support for fixed post/ref dividers - add support for non-fractional fb dividers By default avivo chips use the new algo and pre-avivo chips use the old algo. Use the "new_pll" module option to toggle between them. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie (cherry picked from 267364ac17f6474c69b03034340f769b22f46105 upsteam) BugLink: http://bugs.launchpad.net/bugs/538377 Signed-off-by: Andy Whitcroft --- drivers/gpu/drm/radeon/atombios_crtc.c | 12 +- drivers/gpu/drm/radeon/radeon_atombios.c | 12 +- drivers/gpu/drm/radeon/radeon_display.c | 228 ++++++++++++++++++--------- drivers/gpu/drm/radeon/radeon_drv.c | 4 +- drivers/gpu/drm/radeon/radeon_legacy_crtc.c | 5 +- drivers/gpu/drm/radeon/radeon_mode.h | 2 +- 6 files changed, 176 insertions(+), 87 deletions(-) diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 4bae551..fbb82a4 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -426,12 +426,16 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, /* select the PLL algo */ if (ASIC_IS_AVIVO(rdev)) { - if (radeon_new_pll) - pll->algo = PLL_ALGO_AVIVO; + if (radeon_new_pll == 0) + pll->algo = PLL_ALGO_LEGACY; + else + pll->algo = PLL_ALGO_NEW; + } else { + if (radeon_new_pll == 1) + pll->algo = PLL_ALGO_NEW; else pll->algo = PLL_ALGO_LEGACY; - } else - pll->algo = PLL_ALGO_LEGACY; + } if (ASIC_IS_AVIVO(rdev)) { if ((rdev->family == CHIP_RS600) || diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 9b1c927..572c643 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -1162,12 +1162,16 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct lvds->ss = radeon_atombios_get_ss_info(encoder, lvds_info->info.ucSS_Id); if (ASIC_IS_AVIVO(rdev)) { - if (radeon_new_pll) - lvds->pll_algo = PLL_ALGO_AVIVO; + if (radeon_new_pll == 0) + lvds->pll_algo = PLL_ALGO_LEGACY; + else + lvds->pll_algo = PLL_ALGO_NEW; + } else { + if (radeon_new_pll == 1) + lvds->pll_algo = PLL_ALGO_NEW; else lvds->pll_algo = PLL_ALGO_LEGACY; - } else - lvds->pll_algo = PLL_ALGO_LEGACY; + } encoder->native_mode = lvds->native_mode; } diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index d2e0b6b..8f5d736 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -580,95 +580,173 @@ static void radeon_compute_pll_legacy(struct radeon_pll *pll, *post_div_p = best_post_div; } -static void radeon_compute_pll_avivo(struct radeon_pll *pll, - uint64_t freq, - uint32_t *dot_clock_p, - uint32_t *fb_div_p, - uint32_t *frac_fb_div_p, - uint32_t *ref_div_p, - uint32_t *post_div_p) +static bool +calc_fb_div(struct radeon_pll *pll, + uint32_t freq, + uint32_t post_div, + uint32_t ref_div, + uint32_t *fb_div, + uint32_t *fb_div_frac) { - fixed20_12 m, n, frac_n, p, f_vco, f_pclk, best_freq; - fixed20_12 pll_out_max, pll_out_min; - fixed20_12 pll_in_max, pll_in_min; - fixed20_12 reference_freq; - fixed20_12 error, ffreq, a, b; - - pll_out_max.full = rfixed_const(pll->pll_out_max); - pll_out_min.full = rfixed_const(pll->pll_out_min); - pll_in_max.full = rfixed_const(pll->pll_in_max); - pll_in_min.full = rfixed_const(pll->pll_in_min); - reference_freq.full = rfixed_const(pll->reference_freq); - do_div(freq, 10); - ffreq.full = rfixed_const(freq); - error.full = rfixed_const(100 * 100); + fixed20_12 feedback_divider, a, b; + u32 vco_freq; - /* max p */ - p.full = rfixed_div(pll_out_max, ffreq); - p.full = rfixed_floor(p); + vco_freq = freq * post_div; + /* feedback_divider = vco_freq * ref_div / pll->reference_freq; */ + a.full = rfixed_const(pll->reference_freq); + feedback_divider.full = rfixed_const(vco_freq); + feedback_divider.full = rfixed_div(feedback_divider, a); + a.full = rfixed_const(ref_div); + feedback_divider.full = rfixed_mul(feedback_divider, a); - /* min m */ - m.full = rfixed_div(reference_freq, pll_in_max); - m.full = rfixed_ceil(m); + if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { + /* feedback_divider = floor((feedback_divider * 10.0) + 0.5) * 0.1; */ + a.full = rfixed_const(10); + feedback_divider.full = rfixed_mul(feedback_divider, a); + feedback_divider.full += rfixed_const_half(0); + feedback_divider.full = rfixed_floor(feedback_divider); + feedback_divider.full = rfixed_div(feedback_divider, a); + + /* *fb_div = floor(feedback_divider); */ + a.full = rfixed_floor(feedback_divider); + *fb_div = rfixed_trunc(a); + /* *fb_div_frac = fmod(feedback_divider, 1.0) * 10.0; */ + a.full = rfixed_const(10); + b.full = rfixed_mul(feedback_divider, a); + + feedback_divider.full = rfixed_floor(feedback_divider); + feedback_divider.full = rfixed_mul(feedback_divider, a); + feedback_divider.full = b.full - feedback_divider.full; + *fb_div_frac = rfixed_trunc(feedback_divider); + } else { + /* *fb_div = floor(feedback_divider + 0.5); */ + feedback_divider.full += rfixed_const_half(0); + feedback_divider.full = rfixed_floor(feedback_divider); - while (1) { - n.full = rfixed_div(ffreq, reference_freq); - n.full = rfixed_mul(n, m); - n.full = rfixed_mul(n, p); + *fb_div = rfixed_trunc(feedback_divider); + *fb_div_frac = 0; + } - f_vco.full = rfixed_div(n, m); - f_vco.full = rfixed_mul(f_vco, reference_freq); + if (((*fb_div) < pll->min_feedback_div) || ((*fb_div) > pll->max_feedback_div)) + return false; + else + return true; +} - f_pclk.full = rfixed_div(f_vco, p); +static bool +calc_fb_ref_div(struct radeon_pll *pll, + uint32_t freq, + uint32_t post_div, + uint32_t *fb_div, + uint32_t *fb_div_frac, + uint32_t *ref_div) +{ + fixed20_12 ffreq, max_error, error, pll_out, a; + u32 vco; - if (f_pclk.full > ffreq.full) - error.full = f_pclk.full - ffreq.full; - else - error.full = ffreq.full - f_pclk.full; - error.full = rfixed_div(error, f_pclk); - a.full = rfixed_const(100 * 100); - error.full = rfixed_mul(error, a); + ffreq.full = rfixed_const(freq); + /* max_error = ffreq * 0.0025; */ + a.full = rfixed_const(400); + max_error.full = rfixed_div(ffreq, a); - a.full = rfixed_mul(m, p); - a.full = rfixed_div(n, a); - best_freq.full = rfixed_mul(reference_freq, a); + for ((*ref_div) = pll->min_ref_div; (*ref_div) < pll->max_ref_div; ++(*ref_div)) { + if (calc_fb_div(pll, freq, post_div, (*ref_div), fb_div, fb_div_frac)) { + vco = pll->reference_freq * (((*fb_div) * 10) + (*fb_div_frac)); + vco = vco / ((*ref_div) * 10); - if (rfixed_trunc(error) < 25) - break; + if ((vco < pll->pll_out_min) || (vco > pll->pll_out_max)) + continue; - a.full = rfixed_const(1); - m.full = m.full + a.full; - a.full = rfixed_div(reference_freq, m); - if (a.full >= pll_in_min.full) - continue; + /* pll_out = vco / post_div; */ + a.full = rfixed_const(post_div); + pll_out.full = rfixed_const(vco); + pll_out.full = rfixed_div(pll_out, a); - m.full = rfixed_div(reference_freq, pll_in_max); - m.full = rfixed_ceil(m); - a.full= rfixed_const(1); - p.full = p.full - a.full; - a.full = rfixed_mul(p, ffreq); - if (a.full >= pll_out_min.full) - continue; - else { - DRM_ERROR("Unable to find pll dividers\n"); - break; + if (pll_out.full >= ffreq.full) { + error.full = pll_out.full - ffreq.full; + if (error.full <= max_error.full) + return true; + } + } + } + return false; +} + +static void radeon_compute_pll_new(struct radeon_pll *pll, + uint64_t freq, + uint32_t *dot_clock_p, + uint32_t *fb_div_p, + uint32_t *frac_fb_div_p, + uint32_t *ref_div_p, + uint32_t *post_div_p) +{ + u32 fb_div = 0, fb_div_frac = 0, post_div = 0, ref_div = 0; + u32 best_freq = 0, vco_frequency; + + /* freq = freq / 10; */ + do_div(freq, 10); + + if (pll->flags & RADEON_PLL_USE_POST_DIV) { + post_div = pll->post_div; + if ((post_div < pll->min_post_div) || (post_div > pll->max_post_div)) + goto done; + + vco_frequency = freq * post_div; + if ((vco_frequency < pll->pll_out_min) || (vco_frequency > pll->pll_out_max)) + goto done; + + if (pll->flags & RADEON_PLL_USE_REF_DIV) { + ref_div = pll->reference_div; + if ((ref_div < pll->min_ref_div) || (ref_div > pll->max_ref_div)) + goto done; + if (!calc_fb_div(pll, freq, post_div, ref_div, &fb_div, &fb_div_frac)) + goto done; + } + } else { + for (post_div = pll->max_post_div; post_div >= pll->min_post_div; --post_div) { + if (pll->flags & RADEON_PLL_LEGACY) { + if ((post_div == 5) || + (post_div == 7) || + (post_div == 9) || + (post_div == 10) || + (post_div == 11)) + continue; + } + + if ((pll->flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1)) + continue; + + vco_frequency = freq * post_div; + if ((vco_frequency < pll->pll_out_min) || (vco_frequency > pll->pll_out_max)) + continue; + if (pll->flags & RADEON_PLL_USE_REF_DIV) { + ref_div = pll->reference_div; + if ((ref_div < pll->min_ref_div) || (ref_div > pll->max_ref_div)) + goto done; + if (calc_fb_div(pll, freq, post_div, ref_div, &fb_div, &fb_div_frac)) + break; + } else { + if (calc_fb_ref_div(pll, freq, post_div, &fb_div, &fb_div_frac, &ref_div)) + break; + } } } - a.full = rfixed_const(10); - b.full = rfixed_mul(n, a); + best_freq = pll->reference_freq * 10 * fb_div; + best_freq += pll->reference_freq * fb_div_frac; + best_freq = best_freq / (ref_div * post_div); - frac_n.full = rfixed_floor(n); - frac_n.full = rfixed_mul(frac_n, a); - frac_n.full = b.full - frac_n.full; +done: + if (best_freq == 0) + DRM_ERROR("Couldn't find valid PLL dividers\n"); - *dot_clock_p = rfixed_trunc(best_freq); - *fb_div_p = rfixed_trunc(n); - *frac_fb_div_p = rfixed_trunc(frac_n); - *ref_div_p = rfixed_trunc(m); - *post_div_p = rfixed_trunc(p); + *dot_clock_p = best_freq / 10; + *fb_div_p = fb_div; + *frac_fb_div_p = fb_div_frac; + *ref_div_p = ref_div; + *post_div_p = post_div; - DRM_DEBUG("%u %d.%d, %d, %d\n", *dot_clock_p * 10, *fb_div_p, *frac_fb_div_p, *ref_div_p, *post_div_p); + DRM_DEBUG("%u %d.%d, %d, %d\n", *dot_clock_p, *fb_div_p, *frac_fb_div_p, *ref_div_p, *post_div_p); } void radeon_compute_pll(struct radeon_pll *pll, @@ -680,9 +758,9 @@ void radeon_compute_pll(struct radeon_pll *pll, uint32_t *post_div_p) { switch (pll->algo) { - case PLL_ALGO_AVIVO: - radeon_compute_pll_avivo(pll, freq, dot_clock_p, fb_div_p, - frac_fb_div_p, ref_div_p, post_div_p); + case PLL_ALGO_NEW: + radeon_compute_pll_new(pll, freq, dot_clock_p, fb_div_p, + frac_fb_div_p, ref_div_p, post_div_p); break; case PLL_ALGO_LEGACY: default: diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 8ba3de7..f537433 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -86,7 +86,7 @@ int radeon_benchmarking = 0; int radeon_testing = 0; int radeon_connector_table = 0; int radeon_tv = 1; -int radeon_new_pll = 1; +int radeon_new_pll = -1; int radeon_audio = 1; MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers"); @@ -122,7 +122,7 @@ module_param_named(connector_table, radeon_connector_table, int, 0444); MODULE_PARM_DESC(tv, "TV enable (0 = disable)"); module_param_named(tv, radeon_tv, int, 0444); -MODULE_PARM_DESC(new_pll, "Select new PLL code for AVIVO chips"); +MODULE_PARM_DESC(new_pll, "Select new PLL code"); module_param_named(new_pll, radeon_new_pll, int, 0444); MODULE_PARM_DESC(audio, "Audio enable (0 = disable)"); diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c index 376e697..6b40f1d 100644 --- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c +++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c @@ -726,7 +726,10 @@ static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode) pll = &rdev->clock.p1pll; pll->flags = RADEON_PLL_LEGACY; - pll->algo = PLL_ALGO_LEGACY; + if (radeon_new_pll == 1) + pll->algo = PLL_ALGO_NEW; + else + pll->algo = PLL_ALGO_LEGACY; if (mode->clock > 200000) /* range limits??? */ pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV; diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 410cb94..2661fbe 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -131,7 +131,7 @@ struct radeon_tmds_pll { /* pll algo */ enum radeon_pll_algo { PLL_ALGO_LEGACY, - PLL_ALGO_AVIVO + PLL_ALGO_NEW }; struct radeon_pll {