radeondrm

make radeondrm(1) work with iMac 11,1
Log | Files | Refs | README

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 */