xref: /haiku/src/add-ons/accelerants/matrox/engine/mga_maven.c (revision c8113d4d63749319086dd7431cdc0259b2b62a7e)
1 /* program the MAVEN in monitor mode */
2 
3 /* Authors:
4    Mark Watson 6/2000,
5    Rudolf Cornelissen 1/2003-5/2006
6 
7    Thanx to Petr Vandrovec for writing matroxfb.
8 */
9 
10 #define MODULE_BIT 0x00001000
11 
12 #include "mga_std.h"
13 
14 status_t g450_g550_maven_set_vid_pll(display_mode target);
15 status_t g100_g400max_maven_set_vid_pll(display_mode target);
16 
17 /* This routine blanks the first 'line' below the screen used if there's memory left
18  * to place it in. This will prevent overscanning rubbish on the MAVEN DAC, but only
19  * if no screen using virtual height is setup.
20  * In the rare event the mode and overlay use up so much RAM there's not enough room
21  * left for one blank line, you will get overscanning rubbish from the overlay as it
22  * will overwrite the blank line placed here.
23  * This 'rectangle fill' is done in software because not all modes are supported by
24  * the acc engine.
25  * This function exists to partly overcome a G400 MAVEN hardware design fault, which
26  * influences output in both monitor and TVout modes. The fault is that the MAVEN
27  * keeps repeatedly displaying the last 8 pixels fetched from memory until fetching
28  * restarts after a vertical retrace.
29  * In order to let the maven fetch black pixels, an extra line has to be added to its
30  * CRTC timing. This line needs to be entirely filled black, so with zeros. This line
31  * will be displayed as last visible line, and the last 8 pixels of it will be
32  * repeated during vertical retrace.
33  * Note that the overscanning rubbish can be suppressed in TVout modes by extending
34  * the vertical sync pulse all the way 'to the end'. */
gx00_maven_clrline()35 status_t gx00_maven_clrline()
36 {
37 	uint32 x, screensize, pointer_reservation;
38 	uint8* adr;
39 
40 	/* this function is nolonger needed on G450/G550 cards */
41 	if (si->ps.card_type > G400MAX) return B_OK;
42 
43 	/* checkout space needed for hardcursor (if any) */
44 	pointer_reservation = 0;
45 	/* MIL 1/2 cards have a seperate buffer for the cursorbitmap inside the DAC */
46 	if ((si->ps.card_type >= G100) && si->settings.hardcursor) pointer_reservation = 1024;
47 
48 	/* calculate actual screensize */
49 	screensize = si->fbc.bytes_per_row * si->dm.virtual_height;
50 
51 	/* check if there's room left for a full blank line following the actual screen */
52 	if ((screensize + si->fbc.bytes_per_row + pointer_reservation) <=
53 		(si->ps.memory_size * 1024 * 1024))
54 	{
55 		LOG(4,("MAVEN: clearing line directly below screen\n"));
56 
57 		/* calculate first adress beyond actual screen */
58 		adr = (uint8*)si->fbc.frame_buffer;
59 		adr += screensize;
60 		/* clear the line */
61 		for (x = 0; x < si->fbc.bytes_per_row; x++)
62 		{
63 			*(adr + x) = 0x00;
64 		}
65 	}
66 	else
67 		LOG(4,("MAVEN: not clearing line directly below screen: no memory left\n"));
68 
69 	return B_OK;
70 }
71 
gx00_maven_dpms(bool display,bool h,bool v)72 status_t gx00_maven_dpms(bool display, bool h, bool v)
73 {
74 	/* this function is nolonger needed on G450/G550 cards */
75 	if (si->ps.card_type > G400MAX) return B_OK;
76 
77 	if (display && h && v)
78 	{
79 		/* turn on screen */
80 		if (!(si->dm.flags & TV_BITS))
81 		{
82 			/* monitor mode */
83 			MAVW(MONEN, 0xb2);
84 			MAVW(MONSET, 0x20);		/* must be set to this in monitor mode */
85 			MAVW(OUTMODE, 0x03);	/* output: monitor mode */
86 			MAVW(STABLE, 0x22);		/* makes picture stable? */
87 			MAVW(TEST, 0x00);		/* turn off test signal */
88 		}
89 		else
90 		{
91 			/* TVout mode */
92 			MAVW(MONEN, 0xb3);
93 			MAVW(MONSET, 0x20);
94 			MAVW(OUTMODE, 0x08);	/* output: SVideo/Composite */
95 			MAVW(STABLE, 0x02);		/* makes picture stable? */
96 			//fixme? linux uses 0x14...
97 			MAVW(TEST, (MAVR(TEST) & 0x10));
98 		}
99 	}
100 	else
101 	{
102 		/* turn off screen using a few methods! */
103 		MAVW(STABLE, 0x6a);
104 		MAVW(OUTMODE, 0x00);
105 	}
106 
107 	return B_OK;
108 }
109 
110 /*set a mode line - inputs are in pixels/scanlines*/
gx00_maven_set_timing(display_mode target)111 status_t gx00_maven_set_timing(display_mode target)
112 {
113 	uint8 temp, cnt, offset, loop;
114 
115 	/* this function is nolonger needed on G450/G550 cards */
116 	if (si->ps.card_type > G400MAX) return B_OK;
117 
118 	LOG(4,("MAVEN: setting timing\n"));
119 
120 	/*check horizontal timing parameters are to nearest 8 pixels*/
121 	if ((target.timing.h_display & 0x07)	|
122 		(target.timing.h_sync_start & 0x07)	|
123 		(target.timing.h_sync_end & 0x07)	|
124 		(target.timing.h_total & 0x07))
125 	{
126 		LOG(8,("MAVEN: Horizontal timing is not multiples of 8 pixels\n"));
127 		return B_ERROR;
128 	}
129 
130 	/*program the MAVEN*/
131 	MAVWW(LASTLINEL, target.timing.h_total);
132 	MAVWW(HSYNCLENL, (target.timing.h_sync_end - target.timing.h_sync_start));
133 	MAVWW(HSYNCSTRL, (target.timing.h_total - target.timing.h_sync_start));
134 	MAVWW(HDISPLAYL, ((target.timing.h_total - target.timing.h_sync_start) +
135 					   target.timing.h_display));
136 	MAVWW(HTOTALL, (target.timing.h_total + 1));
137 
138 	MAVWW(VSYNCLENL, (target.timing.v_sync_end - target.timing.v_sync_start - 1));
139 	MAVWW(VSYNCSTRL, (target.timing.v_total - target.timing.v_sync_start));
140 	MAVWW(VDISPLAYL, (target.timing.v_total - 1));
141 	MAVWW(VTOTALL, (target.timing.v_total - 1));
142 
143 	MAVWW(HVIDRSTL, (target.timing.h_total - si->crtc_delay));
144 	MAVWW(VVIDRSTL, (target.timing.v_total - 2));
145 
146 	/* setup HSYNC & VSYNC polarity */
147 	LOG(2,("MAVEN: sync polarity: "));
148 	temp = MAVR(LUMA);
149 
150 	/* find out which offset from the 'reset position' we need */
151 	switch (((target.timing.flags & B_POSITIVE_HSYNC) >> (29 - 0)) |
152 			((target.timing.flags & B_POSITIVE_VSYNC) >> (30 - 1)))
153 	{
154 	case 0:
155 		/* H neg, V neg */
156 		LOG(2,("H:neg V:neg\n"));
157 		offset = 2;
158 		break;
159 	case 1:
160 		/* H pos, V neg */
161 		LOG(2,("H:pos V:neg\n"));
162 		offset = 3;
163 		break;
164 	case 2:
165 		/* H neg, V pos */
166 		LOG(2,("H:neg V:pos\n"));
167 		offset = 1;
168 		break;
169 	case 3:
170 	default:
171 		/* H pos, V pos */
172 		LOG(2,("H:pos V:pos\n"));
173 		offset = 0;
174 		break;
175 	}
176 	/* calculate the number of steps we need to make from the current 'position' */
177 	cnt = 0;
178 	if ((offset - ((int)si->maven_syncpol_offset)) < 0) cnt = 4;
179 	cnt += offset - si->maven_syncpol_offset;
180 	/* note the offset from 'reset position' we will have now */
181 	si->maven_syncpol_offset = offset;
182 
183 	/* program the maven: */
184 	/* make sure pulse bit is reset */
185 	temp &= ~0x20;
186 	MAVW(LUMA, temp);
187 	snooze(5);
188 	/* enable maven sync polarity programming */
189 	temp |= 0x10;
190 	MAVW(LUMA, temp);
191 	snooze(5);
192 	/* pulse sync programming bit until we have the polarities we want */
193 	for (loop = 0; loop < cnt; loop++)
194 	{
195 		MAVW(LUMA, (temp | 0x20));
196 		snooze(5);
197 		MAVW(LUMA, (temp & ~0x20));
198 		snooze(5);
199 	}
200 	/* disable maven sync polarity programming and reset pulse bit */
201 	MAVW(LUMA, (temp & ~0x30));
202 
203 	return B_OK;
204 }
205 
206 /*set the mode, brightness is a value from 0->2 (where 1 is equivalent to direct)*/
gx00_maven_mode(int mode,float brightness)207 status_t gx00_maven_mode(int mode,float brightness)
208 {
209 	uint8 luma;
210 
211 	/* this function is nolonger needed on G450/G550 cards */
212 	if (si->ps.card_type > G400MAX) return B_OK;
213 
214 	/* set luma to a suitable value for brightness */
215 	/* assuming 0x0a is a sensible value */
216 	/* fixme:
217 	 * it looks like b6 and/or b7 determine the luma: just two values possible. */
218 	/* NOTE: b4 and b5 have another function, don't set! (sync polarity programming) */
219 	luma = (uint8)(0x0a * brightness);
220 	if (luma > 0x0f) luma = 0x0f;
221 
222 	MAVW(LUMA, luma);
223 	LOG(4,("MAVEN: LUMA setting - %x\n", luma));
224 
225 	return B_OK;
226 }
227 
gx00_maven_shutoff()228 void gx00_maven_shutoff()
229 {
230 	switch (si->ps.card_type)
231 	{
232 		case G100:
233 		case G200:
234 		//fixme: see if this works on G400 too:
235 		//case G400:
236 		//case G400MAX:
237 			/* prevents distortions on CRTC1... */
238 			MAVW(TEST, 0x03);
239 			MAVW(MONEN, 0x00);
240 			MAVW(MONSET, 0x00);
241 			break;
242 		default:
243 			break;
244 	}
245 }
246 
gx00_maven_set_vid_pll(display_mode target)247 status_t gx00_maven_set_vid_pll(display_mode target)
248 {
249 	switch (si->ps.card_type)
250 	{
251 		case G450:
252 		case G550:
253 			return g450_g550_maven_set_vid_pll(target);
254 			break;
255 		default:
256 			return g100_g400max_maven_set_vid_pll(target);
257 			break;
258 	}
259 	return B_ERROR;
260 }
261 
g450_g550_maven_set_vid_pll(display_mode target)262 status_t g450_g550_maven_set_vid_pll(display_mode target)
263 {
264 	uint8 m=0,n=0,p=0;
265 	uint time = 0;
266 
267 	float pix_setting, req_pclk;
268 	status_t result;
269 
270 	req_pclk = (target.timing.pixel_clock)/1000.0;
271 	LOG(4,("MAVEN: Setting VID PLL for pixelclock %f\n", req_pclk));
272 
273 	result = g450_g550_maven_vid_pll_find(target,&pix_setting,&m,&n,&p, 1);
274 	if (result != B_OK)
275 	{
276 		return result;
277 	}
278 
279 	/*reprogram (disable,select,wait for stability,enable)*/
280 	CR2W(CTL, (CR2R(CTL) | 0x08)); 					/* disable the VIDPLL */
281 	CR2W(CTL, (CR2R(CTL) | 0x06)); 					/* select the VIDPLL */
282 	DXIW(VIDPLLM,(m));								/* set m value */
283 	DXIW(VIDPLLN,(n));								/* set n value */
284 	DXIW(VIDPLLP,(p));								/* set p value */
285 
286 	/* Wait for the VIDPLL frequency to lock until timeout occurs */
287 	while((!(DXIR(VIDPLLSTAT) & 0x40)) & (time <= 2000))
288 	{
289 		time++;
290 		snooze(1);
291 	}
292 
293 	if (time > 2000)
294 		LOG(2,("MAVEN: VID PLL frequency not locked!\n"));
295 	else
296 		LOG(2,("MAVEN: VID PLL frequency locked\n"));
297 	CR2W(CTL, (CR2R(CTL) & ~0x08)); 				/* enable the VIDPLL */
298 
299 	return B_OK;
300 }
301 
302 /* program the video PLL in the MAVEN */
g100_g400max_maven_set_vid_pll(display_mode target)303 status_t g100_g400max_maven_set_vid_pll(display_mode target)
304 {
305 	uint8 m=0,n=0,p=0;
306 
307 	float pix_setting, req_pclk;
308 	status_t result;
309 
310 	req_pclk = (target.timing.pixel_clock)/1000.0;
311 	LOG(4,("MAVEN: Setting VID PLL for pixelclock %f\n", req_pclk));
312 
313 	result = g100_g400max_maven_vid_pll_find(target,&pix_setting,&m,&n,&p);
314 	if (result != B_OK)
315 	{
316 		return result;
317 	}
318 
319 	/*reprogram (select,wait for stability)*/
320 	MAVW(PIXPLLM,(m));								/* set m value */
321 	MAVW(PIXPLLN,(n));								/* set n value */
322 	MAVW(PIXPLLP,(p | 0x80));						/* set p value enabling PLL */
323 
324 	/* Wait for the VIDPLL frequency to lock: detection is not possible it seems */
325 	snooze(2000);
326 
327 	LOG(2,("MAVEN: VID PLL frequency should be locked now...\n"));
328 
329 	return B_OK;
330 }
331 
332 
333 /* find nearest valid video PLL setting */
g100_g400max_maven_vid_pll_find(display_mode target,float * calc_pclk,uint8 * m_result,uint8 * n_result,uint8 * p_result)334 status_t g100_g400max_maven_vid_pll_find(
335 	display_mode target, float* calc_pclk, uint8* m_result, uint8* n_result,
336 	uint8* p_result)
337 {
338 	int m = 0, n = 0, p = 0, m_max;
339 	float error, error_best = INT_MAX;
340 	int best[3] = {0, 0, 0};
341 	float f_vco, max_pclk;
342 	float req_pclk = target.timing.pixel_clock/1000.0;
343 
344 	/* determine the max. reference-frequency postscaler setting for the current card */
345 	//fixme: check G100 and G200 m_max if possible...
346 	switch (si->ps.card_type)
347 	{
348 	case G100:
349 		LOG(4, ("MAVEN: G100 restrictions apply\n"));
350 		m_max = 32;
351 		break;
352 	case G200:
353 		LOG(4, ("MAVEN: G200 restrictions apply\n"));
354 		m_max = 32;
355 		break;
356 	default:
357 		LOG(4, ("MAVEN: G400/G400MAX restrictions apply\n"));
358 		m_max = 32;
359 		break;
360 	}
361 
362 	/* determine the max. pixelclock for the current videomode */
363 	switch (target.space)
364 	{
365 		case B_RGB16_LITTLE:
366 			max_pclk = si->ps.max_dac2_clock_16;
367 			break;
368 		case B_RGB32_LITTLE:
369 			max_pclk = si->ps.max_dac2_clock_32;
370 			break;
371 		default:
372 			/* use fail-safe value */
373 			max_pclk = si->ps.max_dac2_clock_32;
374 			break;
375 	}
376 	/* if some dualhead mode is active, an extra restriction might apply */
377 	if ((target.flags & DUALHEAD_BITS) && (target.space == B_RGB32_LITTLE))
378 		max_pclk = si->ps.max_dac2_clock_32dh;
379 
380 	/* Make sure the requested pixelclock is within the PLL's operational limits */
381 	/* lower limit is min_video_vco divided by highest postscaler-factor */
382 	if (req_pclk < (si->ps.min_video_vco / 8.0))
383 	{
384 		LOG(4, ("MAVEN: clamping vidclock: requested %fMHz, set to %fMHz\n",
385 			req_pclk, (float)(si->ps.min_video_vco / 8.0)));
386 		req_pclk = (si->ps.min_video_vco / 8.0);
387 	}
388 	/* upper limit is given by pins in combination with current active mode */
389 	if (req_pclk > max_pclk)
390 	{
391 		LOG(4, ("MAVEN: clamping vidclock: requested %fMHz, set to %fMHz\n",
392 			req_pclk, (float)max_pclk));
393 		req_pclk = max_pclk;
394 	}
395 
396 	/* iterate through all valid PLL postscaler settings */
397 	for (p=0x01; p < 0x10; p = p<<1)
398 	{
399 		/* calculate the needed VCO frequency for this postscaler setting */
400 		f_vco = req_pclk * p;
401 
402 		/* check if this is within range of the VCO specs */
403 		if ((f_vco >= si->ps.min_video_vco) && (f_vco <= si->ps.max_video_vco))
404 		{
405 			/* iterate trough all valid reference-frequency postscaler settings */
406 			for (m = 2; m <= m_max; m++)
407 			{
408 				/* calculate VCO postscaler setting for current setup.. */
409 				n = (int)(((f_vco * m) / si->ps.f_ref) + 0.5);
410 				/* ..and check for validity */
411 				if ((n < 8) || (n > 128))	continue;
412 
413 				/* find error in frequency this setting gives */
414 				error = fabs(req_pclk - (((si->ps.f_ref / m) * n) / p));
415 
416 				/* note the setting if best yet */
417 				if (error < error_best)
418 				{
419 					error_best = error;
420 					best[0]=m;
421 					best[1]=n;
422 					best[2]=p;
423 				}
424 			}
425 		}
426 	}
427 
428 	/* setup the scalers programming values for found optimum setting */
429 	m=best[0] - 1;
430 	n=best[1] - 1;
431 	p=best[2] - 1;
432 
433 	/* calc the needed PLL loopbackfilter setting belonging to current VCO speed */
434 	f_vco = (si->ps.f_ref / (m + 1)) * (n + 1);
435 	LOG(2, ("MAVEN: vid VCO frequency found %fMhz\n", f_vco));
436 
437 	switch (si->ps.card_type)
438 	{
439 	case G100:
440 	case G200:
441 		for (;;)
442 		{
443 			if (f_vco >= 180) {p |= (0x03 << 3); break;};
444 			if (f_vco >= 140) {p |= (0x02 << 3); break;};
445 			if (f_vco >= 100) {p |= (0x01 << 3); break;};
446 			break;
447 		}
448 		break;
449 	default:
450 		for (;;)
451 		{
452 			if (f_vco >= 240) {p |= (0x03 << 3); break;};
453 			if (f_vco >= 170) {p |= (0x02 << 3); break;};
454 			if (f_vco >= 110) {p |= (0x01 << 3); break;};
455 			break;
456 		}
457 		break;
458 	}
459 
460 	/* return the results */
461 	*calc_pclk = f_vco / ((p & 0x07) + 1);
462 	*m_result = m;
463 	*n_result = n;
464 	*p_result = p;
465 
466 	/* display the found pixelclock values */
467 	LOG(2, ("MAVEN: vid PLL check: req. %fMHz got %fMHz, mnp 0x%02x 0x%02x 0x%02x\n",
468 		req_pclk, *calc_pclk, *m_result, *n_result, *p_result));
469 
470 	return B_OK;
471 }
472 
473 
gx50_maven_check_vid_pll(uint8 m,uint8 n,uint8 p)474 static status_t gx50_maven_check_vid_pll(uint8 m, uint8 n, uint8 p)
475 {
476 	uint time = 0, count = 0;
477 
478 	/* reprogram (disable,select,wait for stability,enable) */
479 	CR2W(CTL, (CR2R(CTL) | 0x06)); 					/* select the VIDPLL */
480 	DXIW(VIDPLLM,(m));								/* set m value */
481 	DXIW(VIDPLLN,(n));								/* set n value */
482 	DXIW(VIDPLLP,(p));								/* set p value */
483 
484 	/* give the PLL 1mS at least to get a lock */
485 	time = 0;
486 	while((!(DXIR(VIDPLLSTAT) & 0x40)) & (time <= 1000))
487 	{
488 		time++;
489 		snooze(1);
490 	}
491 
492 	/* no lock aquired, not useable */
493 	if (time > 1000) return B_ERROR;
494 
495 	/* check if lock holds for at least 90% of the time */
496 	for (time = 0, count = 0; time <= 1000; time++)
497 	{
498 		if(DXIR(VIDPLLSTAT) & 0x40) count++;
499 		snooze(1);
500 	}
501 	/* we have a winner */
502 	if (count >= 900) return B_OK;
503 
504 	/* nogo, the PLL does not stabilize */
505 	return B_ERROR;
506 }
507 
gx50_maven_check_vid_pll_range(uint8 m,uint8 n,uint8 * p,uint8 * q)508 static status_t gx50_maven_check_vid_pll_range(uint8 m, uint8 n, uint8 *p, uint8 *q)
509 {
510 	uint8 s=0, p_backup = *p;
511 
512 	/* preset no candidate, non working setting */
513 	*q = 0;
514 	/* preset lowest range filter */
515 	*p &= 0x47;
516 
517 	/* iterate through all possible filtersettings */
518 	CR2W(CTL, (CR2R(CTL) | 0x08)); /* disable the VIDPLL */
519 
520 	for (s = 0; s < 8 ;s++)
521 	{
522 		if (gx50_maven_check_vid_pll(m, n, *p)== B_OK)
523 		{
524 			/* now check 3 closest lower and higher settings */
525 			if ((gx50_maven_check_vid_pll(m, n - 3, *p)== B_OK) &&
526 				(gx50_maven_check_vid_pll(m, n - 2, *p)== B_OK) &&
527 				(gx50_maven_check_vid_pll(m, n - 1, *p)== B_OK) &&
528 				(gx50_maven_check_vid_pll(m, n + 1, *p)== B_OK) &&
529 				(gx50_maven_check_vid_pll(m, n + 2, *p)== B_OK) &&
530 				(gx50_maven_check_vid_pll(m, n + 3, *p)== B_OK))
531 			{
532 				LOG(2,("MAVEN: found optimal working VCO filter: #%d\n",s));
533 				/* preset first choice setting found */
534 				*q = 1;
535 				/* we are done */
536 				CR2W(CTL, (CR2R(CTL) & ~0x08)); /* enable the VIDPLL */
537 				return B_OK;
538 			}
539 			else
540 			{
541 				LOG(2,("MAVEN: found critical but working VCO filter: #%d\n",s));
542 				/* preset backup setting found */
543 				*q = 2;
544 				/* remember this setting */
545 				p_backup = *p;
546 				/* let's continue to see if a better filter exists */
547 			}
548 		}
549 	/* new filtersetting to try */
550 	*p += (1 << 3);
551 	}
552 
553 	/* return the (last found) backup result, or the original p value */
554 	*p = p_backup;
555 	CR2W(CTL, (CR2R(CTL) & ~0x08)); /* enable the VIDPLL */
556 	/* we found only a non-optimal value */
557 	if (*q == 2) return B_OK;
558 
559 	/* nothing worked at all */
560 	LOG(2,("MAVEN: no working VCO filter found!\n"));
561 	return B_ERROR;
562 }
563 
564 
565 /* find nearest valid video PLL setting */
g450_g550_maven_vid_pll_find(display_mode target,float * calc_pclk,uint8 * m_result,uint8 * n_result,uint8 * p_result,uint8 test)566 status_t g450_g550_maven_vid_pll_find
567 	(display_mode target, float* calc_pclk, uint8* m_result, uint8* n_result,
568 	uint8* p_result, uint8 test)
569 {
570 	int m = 0, n = 0;
571 	uint8 p = 0, q = 0;
572 	float error, error_best = INFINITY;
573 	int best[3] = {0, 0, 0};
574 	float f_vco, max_pclk;
575 	float req_pclk = target.timing.pixel_clock/1000.0;
576 
577 	LOG(4, ("MAVEN: G450/G550 restrictions apply\n"));
578 
579 	/* determine the max. pixelclock for the current videomode */
580 	switch (target.space)
581 	{
582 		case B_RGB16_LITTLE:
583 			max_pclk = si->ps.max_dac2_clock_16;
584 			break;
585 		case B_RGB32_LITTLE:
586 			max_pclk = si->ps.max_dac2_clock_32;
587 			break;
588 		default:
589 			/* use fail-safe value */
590 			max_pclk = si->ps.max_dac2_clock_32;
591 			break;
592 	}
593 	/* if some dualhead mode is active, an extra restriction might apply */
594 	if ((target.flags & DUALHEAD_BITS) && (target.space == B_RGB32_LITTLE))
595 		max_pclk = si->ps.max_dac2_clock_32dh;
596 
597 	/* Make sure the requested pixelclock is within the PLL's operational limits */
598 	/* lower limit is min_pixel_vco divided by highest postscaler-factor */
599 	if (req_pclk < (si->ps.min_video_vco / 16.0))
600 	{
601 		LOG(4, ("MAVEN: clamping vidclock: requested %fMHz, set to %fMHz\n",
602 			req_pclk, (float)(si->ps.min_video_vco / 16.0)));
603 		req_pclk = (si->ps.min_video_vco / 16.0);
604 	}
605 	/* upper limit is given by pins in combination with current active mode */
606 	if (req_pclk > max_pclk)
607 	{
608 		LOG(4, ("MAVEN: clamping vidclock: requested %fMHz, set to %fMHz\n",
609 			req_pclk, (float)max_pclk));
610 		req_pclk = max_pclk;
611 	}
612 
613 	/* iterate through all valid PLL postscaler settings */
614 	for (p=0x01; p < 0x20; p = p<<1)
615 	{
616 		/* calculate the needed VCO frequency for this postscaler setting */
617 		f_vco = req_pclk * p;
618 
619 		/* check if this is within range of the VCO specs */
620 		if ((f_vco >= si->ps.min_video_vco) && (f_vco <= si->ps.max_video_vco))
621 		{
622 			/* iterate trough all valid reference-frequency postscaler settings */
623 			for (m = 2; m <= 32; m++)
624 			{
625 				/* calculate VCO postscaler setting for current setup.. */
626 				n = (int)(((f_vco * m) / (si->ps.f_ref * 2)) + 0.5);
627 				/* ..and check for validity, BUT:
628 				 * Keep in mind that we need to be able to test n-3 ... n+3! */
629 				if ((n < (8 + 3)) || (n > (128 - 3)))	continue;
630 
631 				/* find error in frequency this setting gives */
632 				error = fabs(req_pclk - ((((si->ps.f_ref * 2)/ m) * n) / p));
633 
634 				/* note the setting if best yet */
635 				if (error < error_best)
636 				{
637 					error_best = error;
638 					best[0]=m;
639 					best[1]=n;
640 					best[2]=p;
641 				}
642 			}
643 		}
644 	}
645 
646 	/* setup the scalers programming values for found optimum setting */
647 	m=best[0] - 1;
648 	n=best[1] - 2;
649 	switch (best[2])
650 	{
651 	case 1:
652 		p = 0x40;
653 		break;
654 	case 2:
655 		p = 0x00;
656 		break;
657 	case 4:
658 		p = 0x01;
659 		break;
660 	case 8:
661 		p = 0x02;
662 		break;
663 	case 16:
664 		p = 0x03;
665 		break;
666 	}
667 
668 	/* log the closest VCO speed found */
669 	f_vco = ((si->ps.f_ref * 2) / (m + 1)) * (n + 2);
670 	LOG(2, ("MAVEN: vid VCO frequency found %fMhz\n", f_vco));
671 
672 	/* now find the filtersetting that matches best with this frequency by testing.
673 	 * for now we assume this routine succeeds to get us a stable setting */
674 	if (test)
675 		gx50_maven_check_vid_pll_range(m, n, &p, &q);
676 	else
677 		LOG(2, ("MAVEN: Not testing G450/G550 VCO feedback filters\n"));
678 
679 	/* return the results */
680 	*calc_pclk = f_vco / best[2];
681 	*m_result = m;
682 	*n_result = n;
683 	*p_result = p;
684 
685 	/* display the found pixelclock values */
686 	LOG(2, ("MAVEN: vid PLL check: req. %fMHz got %fMHz, mnp 0x%02x 0x%02x 0x%02x\n",
687 		req_pclk, *calc_pclk, *m_result, *n_result, *p_result));
688 
689 	return B_OK;
690 }
691