2 Copyright (c) 2002-2004, Thomas Kurschel
5 Part of Radeon accelerant
11 #include "radeon_accelerant.h"
14 #include "pll_access.h"
20 static void Radeon_PLLWaitForReadUpdateComplete(
21 accelerator_info
*ai
, int crtc_idx
)
25 // we should wait forever, but
27 // 2. some r300 loop forever (reported by XFree86)
28 for( i
= 0; i
< 10000; ++i
) {
29 if( (Radeon_INPLL( ai
->regs
, ai
->si
->asic
, crtc_idx
== 0 ? RADEON_PPLL_REF_DIV
: RADEON_P2PLL_REF_DIV
)
30 & RADEON_PPLL_ATOMIC_UPDATE_R
) == 0 )
35 static void Radeon_PLLWriteUpdate(
36 accelerator_info
*ai
, int crtc_idx
)
38 Radeon_PLLWaitForReadUpdateComplete( ai
, crtc_idx
);
40 Radeon_OUTPLLP( ai
->regs
, ai
->si
->asic
,
41 crtc_idx
== 0 ? RADEON_PPLL_REF_DIV
: RADEON_P2PLL_REF_DIV
,
42 RADEON_PPLL_ATOMIC_UPDATE_W
,
43 ~RADEON_PPLL_ATOMIC_UPDATE_W
);
46 // calculate PLL dividers
47 // pll - info about PLL
48 // freq - whished frequency in Hz
49 // fixed_post_div - if != 0, fixed divider to be used
50 // dividers - filled with proper dividers
51 void Radeon_CalcPLLDividers(
52 const pll_info
*pll
, uint32 freq
, uint fixed_post_div
, pll_dividers
*dividers
)
54 // the PLL gets the reference
55 // pll_in = ref_freq / ref_div
56 // this must be within pll_in_min..pll_in_max
57 // the VCO of the PLL has the frequency
58 // vco = pll_in * feedback_div * extra_feedback_div
59 // = ref_freq / ref_div * feedback_div * extra_feedback_div
60 // where pre_feedback_div is hard-wired
61 // this must be within vco_min..vco_max
62 // the pixel clock is calculated as
63 // pll_out = vco / post_div / extra_post_div
64 // = ref_freq * feedback_div * extra_feedback_div / (ref_div * post_div * extra_post_div)
65 // where extra_post_div _may_ be choosable between 1 and 2
73 min_post_div_idx
, max_post_div_idx
,
74 post_div_idx
, extra_post_div_idx
,
75 best_post_div_idx
, best_extra_post_div_idx
;
78 best_ref_div
, best_feedback_div
, best_freq
;
80 best_error
, best_vco_dev
;
82 best_error
= 999999999;
84 // make compiler happy
85 best_post_div_idx
= 0;
86 best_extra_post_div_idx
= 0;
88 best_feedback_div
= 1;
92 if( fixed_post_div
== 0 ) {
96 pll
->post_divs
[max_post_div_idx
].divider
!= 0;
102 min_post_div_idx
= 0;
103 pll
->post_divs
[min_post_div_idx
].divider
!= fixed_post_div
;
107 max_post_div_idx
= min_post_div_idx
;
109 //SHOW_FLOW( 2, "idx of fixed post divider: %d", min_post_div_idx );
112 // post dividers are quite restrictive, so they provide little search space only
113 for( extra_post_div_idx
= 0; pll
->extra_post_divs
[extra_post_div_idx
].divider
!= 0; ++extra_post_div_idx
) {
114 for( post_div_idx
= min_post_div_idx
; post_div_idx
<= max_post_div_idx
; ++post_div_idx
) {
117 pll
->post_divs
[post_div_idx
].divider
118 * pll
->extra_post_divs
[extra_post_div_idx
].divider
;
120 // post devider determines VCO frequency, so determine and verify it;
121 // freq is in Hz, everything else is in 10 kHz units
122 // we use 10 kHz units as long as possible to avoid uint32 overflows
123 uint32 vco
= (freq
/ 10000) * post_div
;
125 //SHOW_FLOW( 2, "post_div=%d, vco=%d", post_div, vco );
127 if( vco
< pll
->vco_min
|| vco
> pll
->vco_max
)
130 //SHOW_FLOW0( 2, "jau" );
132 // we can either iterate through feedback or reference dividers;
133 // usually, there are fewer possible reference dividers, so I picked them
134 for( ref_div
= pll
->min_ref_div
; ref_div
<= pll
->max_ref_div
; ++ref_div
) {
135 uint32 feedback_div
, cur_freq
;
136 int32 error
, vco_dev
;
138 // this implies the frequency of the lock unit
139 uint32 pll_in
= pll
->ref_freq
/ ref_div
;
141 if( pll_in
< pll
->pll_in_min
|| pll_in
> pll
->pll_in_max
)
144 // well, only one variable is left
145 // timing is almost certainly valid, time to use Hz units
146 feedback_div
= RoundDiv64(
147 (int64
)freq
* ref_div
* post_div
,
148 pll
->ref_freq
* 10000 * pll
->extra_feedback_div
);
150 if( feedback_div
< pll
->min_feedback_div
||
151 feedback_div
> pll
->max_feedback_div
)
154 // let's see what we've got
155 cur_freq
= RoundDiv64(
156 (int64
)pll
->ref_freq
* 10000 * feedback_div
* pll
->extra_feedback_div
,
157 ref_div
* post_div
);
159 // absolute error in terms of output clock
160 error
= abs( cur_freq
- freq
);
161 // deviation from perfect VCO clock
162 vco_dev
= abs( vco
- pll
->best_vco
);
164 // if there is no optimal VCO frequency, choose setting with less error;
165 // if there is an optimal VCO frequency, choose new settings if
166 // - error is reduced significantly (100 Hz or more), or
167 // - output frequency is almost the same (less then 100 Hz difference) but
168 // VCO frequency is closer to best frequency
169 if( (pll
->best_vco
== 0 && error
< best_error
) ||
170 (pll
->best_vco
!= 0 &&
171 (error
< best_error
- 100 ||
172 (abs( error
- best_error
) < 100 && vco_dev
< best_vco_dev
))))
174 //SHOW_FLOW( 2, "got freq=%d, best_freq=%d", freq, cur_freq );
175 best_post_div_idx
= post_div_idx
;
176 best_extra_post_div_idx
= extra_post_div_idx
;
177 best_ref_div
= ref_div
;
178 best_feedback_div
= feedback_div
;
179 best_freq
= cur_freq
;
181 best_vco_dev
= vco_dev
;
187 dividers
->post_code
= pll
->post_divs
[best_post_div_idx
].code
;
188 dividers
->post
= pll
->post_divs
[best_post_div_idx
].divider
;
189 dividers
->extra_post_code
= pll
->post_divs
[best_extra_post_div_idx
].code
;
190 dividers
->extra_post
= pll
->post_divs
[best_extra_post_div_idx
].divider
;
191 dividers
->ref
= best_ref_div
;
192 dividers
->feedback
= best_feedback_div
;
193 dividers
->freq
= best_freq
;
195 /*SHOW_FLOW( 2, "post_code=%d, post=%d, extra_post_code=%d, extra_post=%d, ref=%d, feedback=%d, freq=%d",
196 dividers->post_code, dividers->post, dividers->extra_post_code,
197 dividers->extra_post, dividers->ref, dividers->feedback, dividers->freq );*/
201 // with a TV timing given, find a corresponding CRT timing.
202 // both timing must meet at the end of a frame, but as the PLL has a
203 // limited frequency granularity, you don't really get a CRT timing
204 // with precisely the same frame rate; the solution is to tweak the CRT
205 // image a bit by making it wider/taller/smaller until the frame rate
206 // drift is under a given threshold;
207 // we follow two aims:
208 // - primary, keep frame rate in sync
209 // - secondary, only tweak as much as unavoidable
210 void Radeon_MatchCRTPLL(
212 uint32 tv_v_total
, uint32 tv_h_total
, uint32 tv_frame_size_adjust
, uint32 freq
,
213 const display_mode
*mode
, uint32 max_v_tweak
, uint32 max_h_tweak
,
214 uint32 max_frame_rate_drift
, uint32 fixed_post_div
,
215 pll_dividers
*dividers
,
216 display_mode
*tweaked_mode
)
220 uint32 pix_per_tv_frame
;
222 SHOW_FLOW( 2, "fixed post divider: %d", fixed_post_div
);
224 // number of TV pixels per frame
225 pix_per_tv_frame
= tv_v_total
* tv_h_total
+ tv_frame_size_adjust
;
227 // starting with original data we tweak total horizontal and vertical size
228 // more and more until we find a proper CRT clock frequency
229 for( v_tweak
= 0; v_tweak
<= max_v_tweak
; ++v_tweak
) {
230 for( v_tweak_dir
= -1; v_tweak_dir
<= 1; v_tweak_dir
+= 2 ) {
234 uint32 v_total
= mode
->timing
.v_total
+ v_tweak
* v_tweak_dir
;
236 for( h_tweak
= 0; h_tweak
<= max_h_tweak
; ++h_tweak
) {
237 for( h_tweak_dir
= -1; h_tweak_dir
<= 1; h_tweak_dir
+= 2 ) {
238 uint32 pix_per_crt_frame
, frame_rate_drift
;
240 uint32 abs_crt_error
;
242 uint32 h_total
= mode
->timing
.h_total
+ h_tweak
* h_tweak_dir
;
244 // number of CRT pixels per frame
245 pix_per_crt_frame
= v_total
* h_total
;
247 // frame rate must be:
248 // frame_rate = freq / pix_per_tv_half_frame
249 // because of interlace, we must use half frames
250 // pix_per_tv_half_frame = pix_per_tv_frame / 2
251 // to get a CRT image with the same frame rate, we get
252 // crt_freq = frame_rate * pix_per_crt_frame
253 // = freq / (pix_per_tv_frame / 2) * pix_per_crt_frame
254 // formula is reordered as usual to improve accuracy
255 crt_freq
= (uint64
)freq
* pix_per_crt_frame
* 2 / pix_per_tv_frame
;
257 Radeon_CalcPLLDividers( pll
, crt_freq
, fixed_post_div
, dividers
);
259 // get absolute CRT clock error per second
260 abs_crt_error
= abs( dividers
->freq
- crt_freq
);
262 //SHOW_INFO( 2, "whished=%d, is=%d", crt_freq, dividers->freq );
264 // convert it to relative CRT clock error:
265 // rel_error = abs_crt_error / crt_freq
266 // now to absolute TV clock error per second:
267 // abs_tv_error = rel_error * tv_freq
268 // and finally to TV clock error per frame:
269 // frame_rate_drift = abs_tv_error / frame_rate
270 // = abs_crt_error / crt_freq * tv_freq / frame_rate
271 // this can be simplified by using:
272 // tv_freq = pix_per_tv_frame * frame_rate
274 // frame_rate_drift = abs_crt_error / crt_freq * pix_per_tv_frame * frame_rate / frame_rate
275 // = abs_crt_error / crt_freq * pix_per_tv_frame
276 frame_rate_drift
= (uint64
)abs_crt_error
* pix_per_tv_frame
/ freq
;
278 // if drift is within threshold, we take this setting and stop
279 // searching (later iteration will increasingly tweak screen size,
280 // and we don't really want that)
281 if( frame_rate_drift
<= max_frame_rate_drift
) {
282 SHOW_INFO( 2, "frame_rate_drift=%d, crt_freq=%d, v_total=%d, h_total=%d",
283 frame_rate_drift
, crt_freq
, v_total
, h_total
);
285 tweaked_mode
->timing
.pixel_clock
= crt_freq
;
286 tweaked_mode
->timing
.v_total
= v_total
;
287 tweaked_mode
->timing
.h_total
= h_total
;
297 // table to map divider to register value
298 static pll_divider_map post_divs
[] = {
304 // { 16, 5 }, // at least for pll2 of M6, this value is reserved
311 // normal PLLs have no extra post divider
312 static pll_divider_map extra_post_divs
[] = {
318 // extra post-divider provided by Rage Theatre
319 static pll_divider_map external_extra_post_divs
[] = {
326 // post-dividers of Rage Theatre
327 static pll_divider_map tv_post_divs
[] = {
347 // get PLL parameters of TV PLL
348 void Radeon_GetTVPLLConfiguration( const general_pll_info
*general_pll
, pll_info
*pll
,
349 bool internal_encoder
)
351 pll
->post_divs
= tv_post_divs
;
352 pll
->extra_post_divs
= internal_encoder
? extra_post_divs
: external_extra_post_divs
;
353 pll
->ref_freq
= general_pll
->ref_freq
;
354 pll
->vco_min
= 10000;
355 pll
->vco_max
= 25000;
356 // I'm not sure about the upper limit
357 pll
->min_ref_div
= 4;
358 pll
->max_ref_div
= 0x3ff;
359 // in the original code, they set it to 330kHz if PAL is requested and
360 // quartz is 27 MHz, but I don't see how these circumstances can effect the
361 // mimimal PLL input frequency
362 pll
->pll_in_min
= 20;//40;
363 // in the original code, they don't define an upper limit
364 pll
->pll_in_max
= 100;
365 pll
->extra_feedback_div
= 1;
366 pll
->min_feedback_div
= 4;
367 pll
->max_feedback_div
= 0x7ff;
368 pll
->best_vco
= 21000;
372 // get PLL parameters of CRT PLL used in conjunction with TV-out
373 void Radeon_GetTVCRTPLLConfiguration( const general_pll_info
*general_pll
, pll_info
*pll
,
374 bool internal_tv_encoder
)
376 pll
->post_divs
= post_divs
;
377 pll
->extra_post_divs
= extra_post_divs
;
378 pll
->ref_freq
= general_pll
->ref_freq
;
380 // in sample code, these limits are set in a strange way;
381 // as a first shot, I use the BIOS provided limits
382 /*pll->vco_min = general_pll->min_pll_freq;
383 pll->vco_max = general_pll->max_pll_freq;*/
385 // in sample code, they use a variable post divider during calculation, but
386 // use a fixed post divider for programming - the variable post divider is
387 // multiplied to the feedback divider;
388 // because of the fixed post divider (3), the VCO always runs far out of
389 // its stable frequency range, so we have hack the limits
391 pll
->vco_max
= general_pll
->max_pll_freq
;
393 // in sample code, lower limit is 4, but in register spec they say everything but 0/1
394 pll
->min_ref_div
= 2;
395 pll
->max_ref_div
= 0x3ff;
396 pll
->pll_in_min
= 20;
397 pll
->pll_in_max
= 100;
398 pll
->extra_feedback_div
= 1;
399 pll
->min_feedback_div
= 4;
400 pll
->max_feedback_div
= 0x7ff;
401 pll
->best_vco
= internal_tv_encoder
? 17500 : 21000;
405 // calc PLL dividers for CRT
406 // mode->timing.pixel_clock must be in Hz because required accuracy in TV-Out mode
407 void Radeon_CalcCRTPLLDividers(
408 const general_pll_info
*general_pll
, const display_mode
*mode
, pll_dividers
*dividers
)
412 pll
.post_divs
= post_divs
;
413 pll
.extra_post_divs
= extra_post_divs
;
414 pll
.ref_freq
= general_pll
->ref_freq
;
415 pll
.vco_min
= general_pll
->min_pll_freq
;
416 pll
.vco_max
= general_pll
->max_pll_freq
;
418 pll
.max_ref_div
= 0x3ff;
420 pll
.pll_in_max
= 100;
421 pll
.extra_feedback_div
= 1;
422 pll
.min_feedback_div
= 4;
423 pll
.max_feedback_div
= 0x7ff;
426 SHOW_FLOW( 2, "freq=%ld", mode
->timing
.pixel_clock
);
428 Radeon_CalcPLLDividers( &pll
, mode
->timing
.pixel_clock
, 0, dividers
);
432 // calculate PLL registers
433 // mode->timing.pixel_clock must be in Hz because required accuracy in TV-Out mode
434 // (old: freq is in 10kHz)
435 void Radeon_CalcPLLRegisters(
436 const display_mode
*mode
, const pll_dividers
*dividers
, pll_regs
*values
)
438 values
->dot_clock_freq
= dividers
->freq
;
439 values
->feedback_div
= dividers
->feedback
;
440 values
->post_div
= dividers
->post
;
441 values
->pll_output_freq
= dividers
->freq
* dividers
->post
;
443 values
->ppll_ref_div
= dividers
->ref
;
444 values
->ppll_div_3
= (dividers
->feedback
| (dividers
->post_code
<< 16));
445 // this is mad: the PLL controls the horizontal length in sub-byte precision!
446 values
->htotal_cntl
= mode
->timing
.h_total
& 7;
448 SHOW_FLOW( 2, "dot_clock_freq=%ld, pll_output_freq=%ld, ref_div=%d, feedback_div=%d, post_div=%d",
449 values
->dot_clock_freq
, values
->pll_output_freq
,
450 values
->ppll_ref_div
, values
->feedback_div
, values
->post_div
);
453 // write values into PLL registers
454 void Radeon_ProgramPLL(
455 accelerator_info
*ai
, int crtc_idx
, pll_regs
*values
)
457 vuint8
*regs
= ai
->regs
;
458 radeon_type asic
= ai
->si
->asic
;
462 // use some other PLL for pixel clock source to not fiddling with PLL
463 // while somebody is using it
464 Radeon_OUTPLLP( regs
, asic
, crtc_idx
== 0 ? RADEON_VCLK_ECP_CNTL
: RADEON_PIXCLKS_CNTL
,
465 RADEON_VCLK_SRC_CPU_CLK
, ~RADEON_VCLK_SRC_SEL_MASK
);
467 Radeon_OUTPLLP( regs
, asic
,
468 crtc_idx
== 0 ? RADEON_PPLL_CNTL
: RADEON_P2PLL_CNTL
,
470 | RADEON_PPLL_ATOMIC_UPDATE_EN
471 | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN
,
473 | RADEON_PPLL_ATOMIC_UPDATE_EN
474 | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN
) );
476 // select divider 3 (well, only required for first PLL)
477 OUTREGP( regs
, RADEON_CLOCK_CNTL_INDEX
,
478 RADEON_PLL_DIV_SEL_DIV3
,
479 ~RADEON_PLL_DIV_SEL_MASK
);
481 RADEONPllErrataAfterIndex(regs
, asic
);
483 if( ai
->si
->new_pll
&& crtc_idx
== 0 ) {
484 // starting with r300, the reference divider of the first PLL was
485 // moved to another bit position; at the old location, you only
486 // find the "BIOS suggested divider"; no clue why they did that
487 Radeon_OUTPLLP( regs
, asic
,
489 values
->ppll_ref_div
<< RADEON_PPLL_REF_DIV_ACC_SHIFT
,
490 ~RADEON_PPLL_REF_DIV_ACC_MASK
);
492 Radeon_OUTPLLP( regs
, asic
,
493 crtc_idx
== 0 ? RADEON_PPLL_REF_DIV
: RADEON_P2PLL_REF_DIV
,
494 values
->ppll_ref_div
,
495 ~RADEON_PPLL_REF_DIV_MASK
);
498 Radeon_OUTPLLP( regs
, asic
,
499 crtc_idx
== 0 ? RADEON_PPLL_DIV_3
: RADEON_P2PLL_DIV_0
,
501 ~RADEON_PPLL_FB3_DIV_MASK
);
503 Radeon_OUTPLLP( regs
, asic
,
504 crtc_idx
== 0 ? RADEON_PPLL_DIV_3
: RADEON_P2PLL_DIV_0
,
506 ~RADEON_PPLL_POST3_DIV_MASK
);
508 Radeon_PLLWriteUpdate( ai
, crtc_idx
);
509 Radeon_PLLWaitForReadUpdateComplete( ai
, crtc_idx
);
511 Radeon_OUTPLL( regs
, asic
,
512 crtc_idx
== 0 ? RADEON_HTOTAL_CNTL
: RADEON_HTOTAL2_CNTL
,
513 values
->htotal_cntl
);
515 Radeon_OUTPLLP( regs
, asic
,
516 crtc_idx
== 0 ? RADEON_PPLL_CNTL
: RADEON_P2PLL_CNTL
, 0,
519 | RADEON_PPLL_ATOMIC_UPDATE_EN
520 | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN
) );
522 // there is no way to check whether PLL has settled, so wait a bit
525 // use PLL for pixel clock again
526 Radeon_OUTPLLP( regs
, asic
,
527 crtc_idx
== 0 ? RADEON_VCLK_ECP_CNTL
: RADEON_PIXCLKS_CNTL
,
528 RADEON_VCLK_SRC_PPLL_CLK
, ~RADEON_VCLK_SRC_SEL_MASK
);