vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / accelerants / radeon / pll.c
blob1dc57aeaa5b18434431bcc94ca9f53caf54d6db3
1 /*
2 Copyright (c) 2002-2004, Thomas Kurschel
5 Part of Radeon accelerant
7 Takes care of PLL
8 */
11 #include "radeon_accelerant.h"
13 #include "pll_regs.h"
14 #include "pll_access.h"
15 #include "utils.h"
16 #include <stdlib.h>
17 #include "set_mode.h"
20 static void Radeon_PLLWaitForReadUpdateComplete(
21 accelerator_info *ai, int crtc_idx )
23 int i;
25 // we should wait forever, but
26 // 1. this is unsafe
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 )
31 return;
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
67 // synonyms are:
68 // ref_div = M
69 // feedback_div = N
70 // post_div = P
72 int
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;
77 uint32
78 best_ref_div, best_feedback_div, best_freq;
79 int32
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;
87 best_ref_div = 1;
88 best_feedback_div = 1;
89 best_freq = 1;
90 best_vco_dev = 1;
92 if( fixed_post_div == 0 ) {
93 min_post_div_idx = 0;
94 for(
95 max_post_div_idx = 0;
96 pll->post_divs[max_post_div_idx].divider != 0;
97 ++max_post_div_idx )
99 --max_post_div_idx;
100 } else {
101 for(
102 min_post_div_idx = 0;
103 pll->post_divs[min_post_div_idx].divider != fixed_post_div;
104 ++min_post_div_idx )
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 ) {
115 uint32 ref_div;
116 uint32 post_div =
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 )
128 continue;
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 )
142 continue;
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 )
152 continue;
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;
180 best_error = error;
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(
211 const pll_info *pll,
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 )
218 uint32 v_tweak;
219 int32 v_tweak_dir;
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 ) {
231 uint32 h_tweak;
232 int32 h_tweak_dir;
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;
239 uint32 crt_freq;
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
273 // so we get:
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;
288 return;
297 // table to map divider to register value
298 static pll_divider_map post_divs[] = {
299 { 1, 0 },
300 { 2, 1 },
301 { 4, 2 },
302 { 8, 3 },
303 { 3, 4 },
304 // { 16, 5 }, // at least for pll2 of M6, this value is reserved
305 { 6, 6 },
306 { 12, 7 },
307 { 0, 0 }
311 // normal PLLs have no extra post divider
312 static pll_divider_map extra_post_divs[] = {
313 { 1, 1 },
314 { 0, 0 }
318 // extra post-divider provided by Rage Theatre
319 static pll_divider_map external_extra_post_divs[] = {
320 { 1, 0 },
321 { 2, 1 },
322 { 0, 0 }
326 // post-dividers of Rage Theatre
327 static pll_divider_map tv_post_divs[] = {
328 { 1, 1 },
329 { 2, 2 },
330 { 3, 3 },
331 { 4, 4 },
332 { 5, 5 },
333 { 6, 6 },
334 { 7, 7 },
335 { 8, 8 },
336 { 9, 9 },
337 { 10, 10 },
338 { 11, 11 },
339 { 12, 12 },
340 { 13, 13 },
341 { 14, 14 },
342 { 15, 15 },
343 { 0, 0 }
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
390 pll->vco_min = 4000;
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 )
410 pll_info pll;
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;
417 pll.min_ref_div = 2;
418 pll.max_ref_div = 0x3ff;
419 pll.pll_in_min = 40;
420 pll.pll_in_max = 100;
421 pll.extra_feedback_div = 1;
422 pll.min_feedback_div = 4;
423 pll.max_feedback_div = 0x7ff;
424 pll.best_vco = 0;
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;
460 SHOW_FLOW0( 2, "" );
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,
469 RADEON_PPLL_RESET
470 | RADEON_PPLL_ATOMIC_UPDATE_EN
471 | RADEON_PPLL_VGA_ATOMIC_UPDATE_EN,
472 ~(RADEON_PPLL_RESET
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,
488 RADEON_PPLL_REF_DIV,
489 values->ppll_ref_div << RADEON_PPLL_REF_DIV_ACC_SHIFT,
490 ~RADEON_PPLL_REF_DIV_ACC_MASK );
491 } else {
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,
500 values->ppll_div_3,
501 ~RADEON_PPLL_FB3_DIV_MASK );
503 Radeon_OUTPLLP( regs, asic,
504 crtc_idx == 0 ? RADEON_PPLL_DIV_3 : RADEON_P2PLL_DIV_0,
505 values->ppll_div_3,
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,
517 ~(RADEON_PPLL_RESET
518 | RADEON_PPLL_SLEEP
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
523 snooze( 5000 );
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 );