pll_7.diff (8894B)
1 commit 32167016076f714f0e35e287fbead7de0f1fb179 2 Author: Christian König <christian.koenig@amd.com> 3 Date: Fri Mar 28 18:55:10 2014 +0100 4 5 drm/radeon: rework finding display PLL numbers v2 6 7 This completely reworks how the PLL parameters are generated and 8 should result in better matching dot clock frequencies. 9 10 Probably needs quite a bit of testing. 11 12 bugs: https://bugs.freedesktop.org/show_bug.cgi?id=76564 13 14 v2: more cleanup and comments. 15 16 Signed-off-by: Christian König <christian.koenig@amd.com> 17 Reviewed-by: Alex Deucher <alexander.deucher@amd.com> 18 19 diff --git b/drivers/gpu/drm/radeon/radeon_display.c a/drivers/gpu/drm/radeon/radeon_display.c 20 index 63d54ef758fc..5701fbb36b3c 100644 21 --- b/drivers/gpu/drm/radeon/radeon_display.c 22 +++ a/drivers/gpu/drm/radeon/radeon_display.c 23 @@ -34,8 +34,6 @@ 24 #include <drm/drm_crtc_helper.h> 25 #include <drm/drm_edid.h> 26 27 -#include <linux/gcd.h> 28 - 29 static void avivo_crtc_load_lut(struct drm_crtc *crtc) 30 { 31 struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); 32 @@ -801,57 +799,66 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) 33 } 34 35 /* avivo */ 36 +static void avivo_get_fb_div(struct radeon_pll *pll, 37 + u32 target_clock, 38 + u32 post_div, 39 + u32 ref_div, 40 + u32 *fb_div, 41 + u32 *frac_fb_div) 42 +{ 43 + u32 tmp = post_div * ref_div; 44 45 -/** 46 - * avivo_reduce_ratio - fractional number reduction 47 - * 48 - * @nom: nominator 49 - * @den: denominator 50 - * @nom_min: minimum value for nominator 51 - * @den_min: minimum value for denominator 52 - * 53 - * Find the greatest common divisor and apply it on both nominator and 54 - * denominator, but make nominator and denominator are at least as large 55 - * as their minimum values. 56 - */ 57 -static void avivo_reduce_ratio(unsigned *nom, unsigned *den, 58 - unsigned nom_min, unsigned den_min) 59 + tmp *= target_clock; 60 + *fb_div = tmp / pll->reference_freq; 61 + *frac_fb_div = tmp % pll->reference_freq; 62 + 63 + if (*fb_div > pll->max_feedback_div) 64 + *fb_div = pll->max_feedback_div; 65 + else if (*fb_div < pll->min_feedback_div) 66 + *fb_div = pll->min_feedback_div; 67 +} 68 + 69 +static u32 avivo_get_post_div(struct radeon_pll *pll, 70 + u32 target_clock) 71 { 72 - unsigned tmp; 73 - 74 - /* reduce the numbers to a simpler ratio */ 75 - tmp = gcd(*nom, *den); 76 - *nom /= tmp; 77 - *den /= tmp; 78 - 79 - /* make sure nominator is large enough */ 80 - if (*nom < nom_min) { 81 - tmp = (nom_min + *nom - 1) / *nom; 82 - *nom *= tmp; 83 - *den *= tmp; 84 + u32 vco, post_div, tmp; 85 + 86 + if (pll->flags & RADEON_PLL_USE_POST_DIV) 87 + return pll->post_div; 88 + 89 + if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) { 90 + if (pll->flags & RADEON_PLL_IS_LCD) 91 + vco = pll->lcd_pll_out_min; 92 + else 93 + vco = pll->pll_out_min; 94 + } else { 95 + if (pll->flags & RADEON_PLL_IS_LCD) 96 + vco = pll->lcd_pll_out_max; 97 + else 98 + vco = pll->pll_out_max; 99 } 100 101 - /* make sure the denominator is large enough */ 102 - if (*den < den_min) { 103 - tmp = (den_min + *den - 1) / *den; 104 - *nom *= tmp; 105 - *den *= tmp; 106 + post_div = vco / target_clock; 107 + tmp = vco % target_clock; 108 + 109 + if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) { 110 + if (tmp) 111 + post_div++; 112 + } else { 113 + if (!tmp) 114 + post_div--; 115 } 116 + 117 + if (post_div > pll->max_post_div) 118 + post_div = pll->max_post_div; 119 + else if (post_div < pll->min_post_div) 120 + post_div = pll->min_post_div; 121 + 122 + return post_div; 123 } 124 125 -/** 126 - * radeon_compute_pll_avivo - compute PLL paramaters 127 - * 128 - * @pll: information about the PLL 129 - * @dot_clock_p: resulting pixel clock 130 - * fb_div_p: resulting feedback divider 131 - * frac_fb_div_p: fractional part of the feedback divider 132 - * ref_div_p: resulting reference divider 133 - * post_div_p: resulting reference divider 134 - * 135 - * Try to calculate the PLL parameters to generate the given frequency: 136 - * dot_clock = (ref_freq * feedback_div) / (ref_div * post_div) 137 - */ 138 +#define MAX_TOLERANCE 10 139 + 140 void radeon_compute_pll_avivo(struct radeon_pll *pll, 141 u32 freq, 142 u32 *dot_clock_p, 143 @@ -860,123 +867,53 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll, 144 u32 *ref_div_p, 145 u32 *post_div_p) 146 { 147 - unsigned fb_div_min, fb_div_max, fb_div; 148 - unsigned post_div_min, post_div_max, post_div; 149 - unsigned ref_div_min, ref_div_max, ref_div; 150 - unsigned post_div_best, diff_best; 151 - unsigned nom, den, tmp; 152 - 153 - /* determine allowed feedback divider range */ 154 - fb_div_min = pll->min_feedback_div; 155 - fb_div_max = pll->max_feedback_div; 156 - 157 - if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { 158 - fb_div_min *= 10; 159 - fb_div_max *= 10; 160 - } 161 + u32 target_clock = freq / 10; 162 + u32 post_div = avivo_get_post_div(pll, target_clock); 163 + u32 ref_div = pll->min_ref_div; 164 + u32 fb_div = 0, frac_fb_div = 0, tmp; 165 166 - /* determine allowed ref divider range */ 167 if (pll->flags & RADEON_PLL_USE_REF_DIV) 168 - ref_div_min = pll->reference_div; 169 - else 170 - ref_div_min = pll->min_ref_div; 171 - ref_div_max = pll->max_ref_div; 172 + ref_div = pll->reference_div; 173 174 - /* determine allowed post divider range */ 175 - if (pll->flags & RADEON_PLL_USE_POST_DIV) { 176 - post_div_min = pll->post_div; 177 - post_div_max = pll->post_div; 178 - } else { 179 - unsigned target_clock = freq / 10; 180 - unsigned vco_min, vco_max; 181 - 182 - if (pll->flags & RADEON_PLL_IS_LCD) { 183 - vco_min = pll->lcd_pll_out_min; 184 - vco_max = pll->lcd_pll_out_max; 185 - } else { 186 - vco_min = pll->pll_out_min; 187 - vco_max = pll->pll_out_max; 188 + if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { 189 + avivo_get_fb_div(pll, target_clock, post_div, ref_div, &fb_div, &frac_fb_div); 190 + frac_fb_div = (100 * frac_fb_div) / pll->reference_freq; 191 + if (frac_fb_div >= 5) { 192 + frac_fb_div -= 5; 193 + frac_fb_div = frac_fb_div / 10; 194 + frac_fb_div++; 195 } 196 - 197 - post_div_min = vco_min / target_clock; 198 - if ((target_clock * post_div_min) < vco_min) 199 - ++post_div_min; 200 - if (post_div_min < pll->min_post_div) 201 - post_div_min = pll->min_post_div; 202 - 203 - post_div_max = vco_max / target_clock; 204 - if ((target_clock * post_div_max) > vco_max) 205 - --post_div_max; 206 - if (post_div_max > pll->max_post_div) 207 - post_div_max = pll->max_post_div; 208 - } 209 - 210 - /* represent the searched ratio as fractional number */ 211 - nom = pll->flags & RADEON_PLL_USE_FRAC_FB_DIV ? freq : freq / 10; 212 - den = pll->reference_freq; 213 - 214 - /* reduce the numbers to a simpler ratio */ 215 - avivo_reduce_ratio(&nom, &den, fb_div_min, post_div_min); 216 - 217 - /* now search for a post divider */ 218 - if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) 219 - post_div_best = post_div_min; 220 - else 221 - post_div_best = post_div_max; 222 - diff_best = ~0; 223 - 224 - for (post_div = post_div_min; post_div <= post_div_max; ++post_div) { 225 - unsigned diff = abs(den - den / post_div * post_div); 226 - if (diff < diff_best || (diff == diff_best && 227 - !(pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP))) { 228 - 229 - post_div_best = post_div; 230 - diff_best = diff; 231 + if (frac_fb_div >= 10) { 232 + fb_div++; 233 + frac_fb_div = 0; 234 } 235 - } 236 - post_div = post_div_best; 237 - 238 - /* get matching reference and feedback divider */ 239 - ref_div = max(den / post_div, 1u); 240 - fb_div = nom; 241 - 242 - /* we're almost done, but reference and feedback 243 - divider might be to large now */ 244 - 245 - tmp = ref_div; 246 - 247 - if (fb_div > fb_div_max) { 248 - ref_div = ref_div * fb_div_max / fb_div; 249 - fb_div = fb_div_max; 250 - } 251 - 252 - if (ref_div > ref_div_max) { 253 - ref_div = ref_div_max; 254 - fb_div = nom * ref_div_max / tmp; 255 - } 256 - 257 - /* reduce the numbers to a simpler ratio once more */ 258 - /* this also makes sure that the reference divider is large enough */ 259 - avivo_reduce_ratio(&fb_div, &ref_div, fb_div_min, ref_div_min); 260 - 261 - /* and finally save the result */ 262 - if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) { 263 - *fb_div_p = fb_div / 10; 264 - *frac_fb_div_p = fb_div % 10; 265 } else { 266 - *fb_div_p = fb_div; 267 - *frac_fb_div_p = 0; 268 + while (ref_div <= pll->max_ref_div) { 269 + avivo_get_fb_div(pll, target_clock, post_div, ref_div, 270 + &fb_div, &frac_fb_div); 271 + if (frac_fb_div >= (pll->reference_freq / 2)) 272 + fb_div++; 273 + frac_fb_div = 0; 274 + tmp = (pll->reference_freq * fb_div) / (post_div * ref_div); 275 + tmp = (tmp * 10000) / target_clock; 276 + 277 + if (tmp > (10000 + MAX_TOLERANCE)) 278 + ref_div++; 279 + else if (tmp >= (10000 - MAX_TOLERANCE)) 280 + break; 281 + else 282 + ref_div++; 283 + } 284 } 285 286 - *dot_clock_p = ((pll->reference_freq * *fb_div_p * 10) + 287 - (pll->reference_freq * *frac_fb_div_p)) / 288 - (ref_div * post_div * 10); 289 + *dot_clock_p = ((pll->reference_freq * fb_div * 10) + (pll->reference_freq * frac_fb_div)) / 290 + (ref_div * post_div * 10); 291 + *fb_div_p = fb_div; 292 + *frac_fb_div_p = frac_fb_div; 293 *ref_div_p = ref_div; 294 *post_div_p = post_div; 295 - 296 - DRM_DEBUG_KMS("%d - %d, pll dividers - fb: %d.%d ref: %d, post %d\n", 297 - freq, *dot_clock_p, *fb_div_p, *frac_fb_div_p, 298 - ref_div, post_div); 299 + DRM_DEBUG_KMS("%d, pll dividers - fb: %d.%d ref: %d, post %d\n", 300 + *dot_clock_p, fb_div, frac_fb_div, ref_div, post_div); 301 } 302 303 /* pre-avivo */