radeon_ms: fix pll computation to follow hw constraint

main
Jerome Glisse 2007-12-08 00:45:33 +01:00
parent a693e8ab12
commit 9d064966d8
2 changed files with 82 additions and 51 deletions

View File

@ -379,13 +379,26 @@ static void radeon_ms_crtc_mode_prepare(struct drm_crtc *crtc)
crtc->funcs->dpms(crtc, DPMSModeOff); crtc->funcs->dpms(crtc, DPMSModeOff);
} }
/* Compute n/d with rounding */ /* compute PLL registers values for requested video mode */
static int radeon_div(int n, int d) static int radeon_pll1_constraint(int clock, int rdiv,
int fdiv, int pdiv,
int rfrq, int pfrq)
{ {
return (n + (d / 2)) / d; int dfrq;
if (rdiv < 2 || fdiv < 4) {
return 0;
}
dfrq = rfrq / rdiv;
if (dfrq < 2000 || dfrq > 3300) {
return 0;
}
if (pfrq < 125000 || pfrq > 250000) {
return 0;
}
return 1;
} }
/* compute PLL registers values for requested video mode */
static void radeon_pll1_compute(struct drm_crtc *crtc, static void radeon_pll1_compute(struct drm_crtc *crtc,
struct drm_display_mode *mode) struct drm_display_mode *mode)
{ {
@ -410,65 +423,73 @@ static void radeon_pll1_compute(struct drm_crtc *crtc,
}; };
struct drm_radeon_private *dev_priv = crtc->dev->dev_private; struct drm_radeon_private *dev_priv = crtc->dev->dev_private;
struct radeon_state *state = &dev_priv->driver_state; struct radeon_state *state = &dev_priv->driver_state;
unsigned long freq = mode->clock / 10; int clock = mode->clock;
int pll_output_freq; int rfrq = dev_priv->properties->pll_reference_freq;
int min_rcenter_dist; int pdiv = 1;
int rcenter_dist; int pdiv_id = 0;
int post_divider; int rdiv_best = 2;
int post_divider_id; int fdiv_best = 4;
int ref_div; int tfrq_best = 0;
int fb_div; int pfrq_best = 0;
int diff_cpfrq_best = 350000;
int vco_freq; int vco_freq;
int vco_gain; int vco_gain;
int rdiv = 0;
int fdiv = 0;
int tfrq = 35000;
int pfrq = 35000;
int diff_cpfrq = 350000;
/* clamp frequency into pll [min; max] frequency range */ /* clamp frequency into pll [min; max] frequency range */
if (freq > dev_priv->properties->pll_max_pll_freq) { if (clock > dev_priv->properties->pll_max_pll_freq) {
freq = dev_priv->properties->pll_max_pll_freq; clock = dev_priv->properties->pll_max_pll_freq;
} }
if (freq < dev_priv->properties->pll_min_pll_freq) { if ((clock * 12) < dev_priv->properties->pll_min_pll_freq) {
freq = dev_priv->properties->pll_min_pll_freq; clock = dev_priv->properties->pll_min_pll_freq / 12;
} }
/* select divider so that pll output frequency is the nearest to
* the center of [350; 125]Mhz range */ /* maximize pll_ref_div while staying in boundary and minimizing
min_rcenter_dist = 350 * 100; * the difference btw target frequency and programmed frequency */
post_divider = post_divs[0].divider;
post_divider_id = post_divs[0].divider_id;
for (post_div = &post_divs[0]; post_div->divider; ++post_div) { for (post_div = &post_divs[0]; post_div->divider; ++post_div) {
if (post_div->divider == 0) if (post_div->divider == 0) {
break; break;
/* pll output frequency (before post divider) */ }
pll_output_freq = post_div->divider * freq; tfrq = clock * post_div->divider;
/* compute distance to [350; 125] range center*/ for (fdiv = 1023; fdiv >= 4; fdiv--) {
rcenter_dist = abs(pll_output_freq - 11250); rdiv = (fdiv * rfrq) / tfrq;
if (rcenter_dist < min_rcenter_dist) { if (radeon_pll1_constraint(clock, rdiv, fdiv,
min_rcenter_dist = rcenter_dist; pdiv, rfrq, tfrq)) {
post_divider = post_div->divider; pfrq = (fdiv * rfrq) / rdiv;
post_divider_id = post_div->divider_id; diff_cpfrq = pfrq - tfrq;
if ((diff_cpfrq >= 0 &&
diff_cpfrq < diff_cpfrq_best) ||
(diff_cpfrq == diff_cpfrq_best &&
rdiv > rdiv_best)) {
rdiv_best = rdiv;
fdiv_best = fdiv;
tfrq_best = tfrq;
pfrq_best = pfrq;
pdiv = post_div->divider;
pdiv_id = post_div->divider_id;
diff_cpfrq_best = diff_cpfrq;
}
}
} }
} }
pll_output_freq = post_divider * freq; state->ppll_ref_div =
/* select first feedback */ REG_S(PPLL_REF_DIV, PPLL_REF_DIV, rdiv_best) |
state->clock_cntl_index = REG_S(CLOCK_CNTL_INDEX, PPLL_DIV_SEL, 0); REG_S(PPLL_REF_DIV, PPLL_REF_DIV_ACC, rdiv_best);
/* set ref div so that ref_freq/ref_div is in middle of [2; 3.3]Mhz */ state->ppll_div_0 = REG_S(PPLL_DIV_0, PPLL_FB0_DIV, fdiv_best) |
ref_div = dev_priv->properties->pll_reference_freq / 265; REG_S(PPLL_DIV_0, PPLL_POST0_DIV, pdiv_id);
state->ppll_ref_div = REG_S(PPLL_REF_DIV, PPLL_REF_DIV, ref_div) |
REG_S(PPLL_REF_DIV, PPLL_REF_DIV_ACC, ref_div); vco_freq = (fdiv_best * rfrq) / rdiv_best;
fb_div = radeon_div(pll_output_freq * ref_div,
dev_priv->properties->pll_reference_freq);
state->ppll_div_0 = REG_S(PPLL_DIV_0, PPLL_FB0_DIV, fb_div) |
REG_S(PPLL_DIV_0, PPLL_POST0_DIV, post_divider_id);
/* configure vco gain */
state->ppll_cntl = PPLL_R(PPLL_CNTL);
vco_gain = REG_G(PPLL_CNTL, PPLL_PVG, state->ppll_cntl);
vco_freq = (fb_div * dev_priv->properties->pll_reference_freq) /
ref_div;
/* This is horribly crude: the VCO frequency range is divided into /* This is horribly crude: the VCO frequency range is divided into
* 3 parts, each part having a fixed PLL gain value. * 3 parts, each part having a fixed PLL gain value.
*/ */
if (vco_freq >= 30000) { if (vco_freq >= 300000) {
/* [300..max] MHz : 7 */ /* [300..max] MHz : 7 */
vco_gain = 7; vco_gain = 7;
} else if (vco_freq >= 18000) { } else if (vco_freq >= 180000) {
/* [180..300) MHz : 4 */ /* [180..300) MHz : 4 */
vco_gain = 4; vco_gain = 4;
} else { } else {
@ -479,6 +500,16 @@ static void radeon_pll1_compute(struct drm_crtc *crtc,
state->vclk_ecp_cntl |= REG_S(VCLK_ECP_CNTL, VCLK_SRC_SEL, state->vclk_ecp_cntl |= REG_S(VCLK_ECP_CNTL, VCLK_SRC_SEL,
VCLK_SRC_SEL__PPLLCLK); VCLK_SRC_SEL__PPLLCLK);
state->htotal_cntl = 0; state->htotal_cntl = 0;
DRM_INFO("rdiv: %d\n", rdiv_best);
DRM_INFO("fdiv: %d\n", fdiv_best);
DRM_INFO("pdiv: %d\n", pdiv);
DRM_INFO("pdiv: %d\n", pdiv_id);
DRM_INFO("tfrq: %d\n", tfrq_best);
DRM_INFO("pfrq: %d\n", pfrq_best);
DRM_INFO("PPLL_REF_DIV: 0x%08X\n", state->ppll_ref_div);
DRM_INFO("PPLL_DIV_0: 0x%08X\n", state->ppll_div_0);
DRM_INFO("PPLL_CNTL: 0x%08X\n", state->ppll_cntl);
DRM_INFO("VCLK_ECP_CNTL: 0x%08X\n", state->vclk_ecp_cntl);
} }
static void radeon_ms_crtc1_mode_set(struct drm_crtc *crtc, static void radeon_ms_crtc1_mode_set(struct drm_crtc *crtc,

View File

@ -76,7 +76,7 @@ static struct radeon_ms_connector radeon_ms_dvi_i_2 = {
static struct radeon_ms_properties properties[] = { static struct radeon_ms_properties properties[] = {
/* default only one VGA connector */ /* default only one VGA connector */
{ {
0, 0, 2700, 2500, 20000, 1, 1, 1, 1, 0, 0, 27000, 25000, 200000, 1, 1, 1, 1,
{ {
&radeon_ms_dac1, NULL, NULL, NULL, NULL, NULL, NULL, &radeon_ms_dac1, NULL, NULL, NULL, NULL, NULL, NULL,
NULL NULL
@ -87,7 +87,7 @@ static struct radeon_ms_properties properties[] = {
} }
}, },
{ {
0x1043, 0x176, 2700, 2500, 20000, 1, 1, 1, 1, 0x1043, 0x176, 27000, 25000, 200000, 1, 1, 1, 1,
{ {
&radeon_ms_dac1, &radeon_ms_dac2, NULL, NULL, NULL, &radeon_ms_dac1, &radeon_ms_dac2, NULL, NULL, NULL,
NULL, NULL, NULL NULL, NULL, NULL
@ -98,7 +98,7 @@ static struct radeon_ms_properties properties[] = {
} }
}, },
{ {
0x1002, 0x4150, 2700, 2500, 20000, 1, 1, 1, 1, 0x1002, 0x4150, 27000, 25000, 200000, 1, 1, 1, 1,
{ {
&radeon_ms_dac1, &radeon_ms_dac2, NULL, NULL, NULL, &radeon_ms_dac1, &radeon_ms_dac2, NULL, NULL, NULL,
NULL, NULL, NULL NULL, NULL, NULL