@ -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
*
@ -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 ;
@ -880,14 +915,18 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll,
ref_div_min = pll - > reference_div ;
else
ref_div_min = pll - > min_ref_div ;
ref_div_max = pll - > max_ref_div ;
if ( pll - > flags & RADEON_PLL_USE_FRAC_FB_DIV & &
pll - > flags & RADEON_PLL_USE_REF_DIV )
ref_div_max = pll - > reference_div ;
else
ref_div_max = pll - > max_ref_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 ) {
@ -898,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 ;
@ -912,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 */
@ -926,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 ) ) ) {
@ -936,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 */
@ -979,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 ) ;
}