xref: /haiku/src/add-ons/accelerants/nvidia/engine/nv_dac.c (revision 01b25646004ff628ecad0281a9795e5e90f71746)
1 /* program the DAC */
2 /* Author:
3    Rudolf Cornelissen 12/2003
4 */
5 
6 #define MODULE_BIT 0x00010000
7 
8 #include "nv_std.h"
9 
10 static status_t nv4_nv10_nv20_dac_pix_pll_find(
11 	display_mode target,float * calc_pclk,uint8 * m_result,uint8 * n_result,uint8 * p_result, uint8 test);
12 static status_t g100_g400max_dac_sys_pll_find(
13 	float req_sclk,float * calc_sclk,uint8 * m_result,uint8 * n_result,uint8 * p_result);
14 
15 /*set the mode, brightness is a value from 0->2 (where 1 is equivalent to direct)*/
16 status_t nv_dac_mode(int mode,float brightness)
17 {
18 	uint8 *r,*g,*b;
19 	int i, ri;
20 
21 	/*set colour arrays to point to space reserved in shared info*/
22 	r = si->color_data;
23 	g = r + 256;
24 	b = g + 256;
25 
26 	LOG(4,("DAC: Setting screen mode %d brightness %f\n", mode, brightness));
27 	/* init the palette for brightness specified */
28 	/* (Nvidia cards always use MSbits from screenbuffer as index for PAL) */
29 	for (i = 0; i < 256; i++)
30 	{
31 		ri = i * brightness;
32 		if (ri > 255) ri = 255;
33 		b[i] = g[i] = r[i] = ri;
34 	}
35 
36 	if (nv_dac_palette(r,g,b) != B_OK) return B_ERROR;
37 
38 	/*set the mode - also sets VCLK dividor*/
39 //	DXIW(MULCTRL, mode);
40 //	LOG(2,("DAC: mulctrl 0x%02x\n", DXIR(MULCTRL)));
41 
42 	/* disable palette RAM adressing mask */
43 	NV_REG8(NV8_PALMASK) = 0xff;
44 	LOG(2,("DAC: PAL pixrdmsk readback $%02x\n", NV_REG8(NV8_PALMASK)));
45 
46 	return B_OK;
47 }
48 
49 /*program the DAC palette using the given r,g,b values*/
50 status_t nv_dac_palette(uint8 r[256],uint8 g[256],uint8 b[256])
51 {
52 	int i;
53 
54 	LOG(4,("DAC: setting palette\n"));
55 
56 	/* select first PAL adress before starting programming */
57 	NV_REG8(NV8_PALINDW) = 0x00;
58 
59 	/* loop through all 256 to program DAC */
60 	for (i = 0; i < 256; i++)
61 	{
62 		/* the 6 implemented bits are on b0-b5 of the bus */
63 		NV_REG8(NV8_PALDATA) = r[i];
64 		NV_REG8(NV8_PALDATA) = g[i];
65 		NV_REG8(NV8_PALDATA) = b[i];
66 	}
67 	if (NV_REG8(NV8_PALINDW) != 0x00)
68 	{
69 		LOG(8,("DAC: PAL write index incorrect after programming\n"));
70 		return B_ERROR;
71 	}
72 if (1)
73  {//reread LUT
74 	uint8 R, G, B;
75 
76 	/* select first PAL adress to read (modulo 3 counter) */
77 	NV_REG8(NV8_PALINDR) = 0x00;
78 	for (i = 0; i < 256; i++)
79 	{
80 		R = NV_REG8(NV8_PALDATA);
81 		G = NV_REG8(NV8_PALDATA);
82 		B = NV_REG8(NV8_PALDATA);
83 		if ((r[i] != R) || (g[i] != G) || (b[i] != B))
84 			LOG(1,("DAC palette %d: w %x %x %x, r %x %x %x\n", i, r[i], g[i], b[i], R, G, B)); // apsed
85 	}
86  }
87 
88 	return B_OK;
89 }
90 
91 /*program the pixpll - frequency in kHz*/
92 /*important notes:
93  * PIXPLLC is used - others should be kept as is
94  * BESCLK,CRTC2 are not touched
95  */
96 status_t nv_dac_set_pix_pll(display_mode target)
97 {
98 	uint8 m=0,n=0,p=0;
99 //	uint time = 0;
100 
101 	float pix_setting, req_pclk;
102 	status_t result;
103 
104 	req_pclk = (target.timing.pixel_clock)/1000.0;
105 	LOG(4,("DAC: Setting PIX PLL for pixelclock %f\n", req_pclk));
106 
107 	/* signal that we actually want to set the mode */
108 	result = nv_dac_pix_pll_find(target,&pix_setting,&m,&n,&p, 1);
109 	if (result != B_OK)
110 	{
111 		return result;
112 	}
113 
114 	/*reprogram (disable,select,wait for stability,enable)*/
115 //	DXIW(PIXCLKCTRL,(DXIR(PIXCLKCTRL)&0x0F)|0x04);  /*disable the PIXPLL*/
116 //	DXIW(PIXCLKCTRL,(DXIR(PIXCLKCTRL)&0x0C)|0x01);  /*select the PIXPLL*/
117 
118 	/* select pixelPLL registerset C */
119 	DACW(PLLSEL, 0x10000700);
120 
121 	/* program new frequency */
122 	DACW(PIXPLLC, ((p << 16) | (n << 8) | m));
123 
124 	/* Wait for the PIXPLL frequency to lock until timeout occurs */
125 //fixme: do NV cards have a LOCK indication bit??
126 /*	while((!(DXIR(PIXPLLSTAT)&0x40)) & (time <= 2000))
127 	{
128 		time++;
129 		snooze(1);
130 	}
131 
132 	if (time > 2000)
133 		LOG(2,("DAC: PIX PLL frequency not locked!\n"));
134 	else
135 		LOG(2,("DAC: PIX PLL frequency locked\n"));
136 	DXIW(PIXCLKCTRL,DXIR(PIXCLKCTRL)&0x0B);         //enable the PIXPLL
137 */
138 
139 //for now:
140 	/* Give the PIXPLL frequency some time to lock... */
141 	snooze(1000);
142 	LOG(2,("DAC: PIX PLL frequency should be locked now...\n"));
143 
144 	return B_OK;
145 }
146 
147 /* find nearest valid pix pll */
148 status_t nv_dac_pix_pll_find
149 	(display_mode target,float * calc_pclk,uint8 * m_result,uint8 * n_result,uint8 * p_result, uint8 test)
150 {
151 	switch (si->ps.card_type) {
152 		default:   return nv4_nv10_nv20_dac_pix_pll_find(target, calc_pclk, m_result, n_result, p_result, test);
153 	}
154 	return B_ERROR;
155 }
156 
157 /* find nearest valid pixel PLL setting */
158 static status_t nv4_nv10_nv20_dac_pix_pll_find(
159 	display_mode target,float * calc_pclk,uint8 * m_result,uint8 * n_result,uint8 * p_result, uint8 test)
160 {
161 	int m = 0, n = 0, p = 0/*, m_max*/;
162 	float error, error_best = 999999999;
163 	int best[3];
164 	float f_vco, max_pclk;
165 	float req_pclk = target.timing.pixel_clock/1000.0;
166 
167 	/* determine the max. reference-frequency postscaler setting for the
168 	 * current card (see G100, G200 and G400 specs). */
169 /*	switch(si->ps.card_type)
170 	{
171 	case G100:
172 		LOG(4,("DAC: G100 restrictions apply\n"));
173 		m_max = 7;
174 		break;
175 	case G200:
176 		LOG(4,("DAC: G200 restrictions apply\n"));
177 		m_max = 7;
178 		break;
179 	default:
180 		LOG(4,("DAC: G400/G400MAX restrictions apply\n"));
181 		m_max = 32;
182 		break;
183 	}
184 */
185 	LOG(4,("DAC: NV4/NV10/NV20 restrictions apply\n"));
186 
187 	/* determine the max. pixelclock for the current videomode */
188 	switch (target.space)
189 	{
190 		case B_CMAP8:
191 			max_pclk = si->ps.max_dac1_clock_8;
192 			break;
193 		case B_RGB15_LITTLE:
194 		case B_RGB16_LITTLE:
195 			max_pclk = si->ps.max_dac1_clock_16;
196 			break;
197 		case B_RGB24_LITTLE:
198 			max_pclk = si->ps.max_dac1_clock_24;
199 			break;
200 		case B_RGB32_LITTLE:
201 			max_pclk = si->ps.max_dac1_clock_32;
202 			break;
203 		default:
204 			/* use fail-safe value */
205 			max_pclk = si->ps.max_dac1_clock_32;
206 			break;
207 	}
208 	/* if some dualhead mode is active, an extra restriction might apply */
209 	if ((target.flags & DUALHEAD_BITS) && (target.space == B_RGB32_LITTLE))
210 		max_pclk = si->ps.max_dac1_clock_32dh;
211 
212 	/* Make sure the requested pixelclock is within the PLL's operational limits */
213 	/* lower limit is min_pixel_vco divided by highest postscaler-factor */
214 	if (req_pclk < (si->ps.min_pixel_vco / 16.0))
215 	{
216 		LOG(4,("DAC: clamping pixclock: requested %fMHz, set to %fMHz\n",
217 										req_pclk, (float)(si->ps.min_pixel_vco / 16.0)));
218 		req_pclk = (si->ps.min_pixel_vco / 16.0);
219 	}
220 	/* upper limit is given by pins in combination with current active mode */
221 	if (req_pclk > max_pclk)
222 	{
223 		LOG(4,("DAC: clamping pixclock: requested %fMHz, set to %fMHz\n",
224 														req_pclk, (float)max_pclk));
225 		req_pclk = max_pclk;
226 	}
227 
228 	/* iterate through all valid PLL postscaler settings */
229 	for (p=0x01; p < 0x20; p = p<<1)
230 	{
231 		/* calculate the needed VCO frequency for this postscaler setting */
232 		f_vco = req_pclk * p;
233 
234 		/* check if this is within range of the VCO specs */
235 		if ((f_vco >= si->ps.min_pixel_vco) && (f_vco <= si->ps.max_pixel_vco))
236 		{
237 			/* NV31 (FX5600) tweak (missing register for 2nd VCO postscaler) */
238 			f_vco /= si->pixpll_vco_div2;
239 
240 			/* iterate trough all valid reference-frequency postscaler settings */
241 			for (m = 7; m <= 14; m++)
242 			{
243 				/* check if phase-discriminator will be within operational limits */
244 				if (((si->ps.f_ref / m) < 1.0) || ((si->ps.f_ref / m) > 2.0)) continue;
245 
246 				/* calculate VCO postscaler setting for current setup.. */
247 				n = (int)(((f_vco * m) / si->ps.f_ref) + 0.5);
248 				/* ..and check for validity */
249 				if ((n < 1) || (n > 255))	continue;
250 
251 				/* find error in frequency this setting gives */
252 				/* si->pixpll_vco_div2 below is NV31 (FX5600) tweak (missing register) */
253 				error =
254 					fabs((req_pclk / si->pixpll_vco_div2) - (((si->ps.f_ref / m) * n) / p));
255 
256 				/* note the setting if best yet */
257 				if (error < error_best)
258 				{
259 					error_best = error;
260 					best[0]=m;
261 					best[1]=n;
262 					best[2]=p;
263 				}
264 			}
265 		}
266 	}
267 
268 	/* setup the scalers programming values for found optimum setting */
269 	m = best[0];
270 	n = best[1];
271 	p = best[2];
272 
273 	/* log the VCO frequency found */
274 	f_vco = ((si->ps.f_ref / m) * n);
275 	/* NV31 (FX5600) tweak (missing register for 2nd VCO postscaler) */
276 	f_vco *= si->pixpll_vco_div2;
277 
278 	LOG(2,("DAC: pix VCO frequency found %fMhz\n", f_vco));
279 
280 	/* return the results */
281 	*calc_pclk = (f_vco / p);
282 	*m_result = m;
283 	*n_result = n;
284 	switch(p)
285 	{
286 	case 1:
287 		p = 0x00;
288 		break;
289 	case 2:
290 		p = 0x01;
291 		break;
292 	case 4:
293 		p = 0x02;
294 		break;
295 	case 8:
296 		p = 0x03;
297 		break;
298 	case 16:
299 		p = 0x04;
300 		break;
301 	}
302 	*p_result = p;
303 
304 	/* display the found pixelclock values */
305 	LOG(2,("DAC: pix PLL check: requested %fMHz got %fMHz, mnp 0x%02x 0x%02x 0x%02x\n",
306 		req_pclk, *calc_pclk, *m_result, *n_result, *p_result));
307 
308 	return B_OK;
309 }
310 
311 /* find nearest valid system PLL setting */
312 static status_t g100_g400max_dac_sys_pll_find(
313 	float req_sclk,float * calc_sclk,uint8 * m_result,uint8 * n_result,uint8 * p_result)
314 {
315 	int m = 0, n = 0, p = 0, m_max;
316 	float error, error_best = 999999999;
317 	int best[3];
318 	float f_vco;
319 
320 	/* determine the max. reference-frequency postscaler setting for the
321 	 * current card (see G100, G200 and G400 specs). */
322 	switch(si->ps.card_type)
323 	{
324 /*	case G100:
325 		LOG(4,("DAC: G100 restrictions apply\n"));
326 		m_max = 7;
327 		break;
328 	case G200:
329 		LOG(4,("DAC: G200 restrictions apply\n"));
330 		m_max = 7;
331 		break;
332 */	default:
333 		LOG(4,("DAC: G400/G400MAX restrictions apply\n"));
334 		m_max = 32;
335 		break;
336 	}
337 
338 	/* Make sure the requested systemclock is within the PLL's operational limits */
339 	/* lower limit is min_system_vco divided by highest postscaler-factor */
340 	if (req_sclk < (si->ps.min_system_vco / 8.0))
341 	{
342 		LOG(4,("DAC: clamping sysclock: requested %fMHz, set to %fMHz\n",
343 										req_sclk, (float)(si->ps.min_system_vco / 8.0)));
344 		req_sclk = (si->ps.min_system_vco / 8.0);
345 	}
346 	/* upper limit is max_system_vco */
347 	if (req_sclk > si->ps.max_system_vco)
348 	{
349 		LOG(4,("DAC: clamping sysclock: requested %fMHz, set to %fMHz\n",
350 										req_sclk, (float)si->ps.max_system_vco));
351 		req_sclk = si->ps.max_system_vco;
352 	}
353 
354 	/* iterate through all valid PLL postscaler settings */
355 	for (p=0x01; p < 0x10; p = p<<1)
356 	{
357 		/* calculate the needed VCO frequency for this postscaler setting */
358 		f_vco = req_sclk * p;
359 
360 		/* check if this is within range of the VCO specs */
361 		if ((f_vco >= si->ps.min_system_vco) && (f_vco <= si->ps.max_system_vco))
362 		{
363 			/* iterate trough all valid reference-frequency postscaler settings */
364 			for (m = 2; m <= m_max; m++)
365 			{
366 				/* calculate VCO postscaler setting for current setup.. */
367 				n = (int)(((f_vco * m) / si->ps.f_ref) + 0.5);
368 				/* ..and check for validity */
369 				if ((n < 8) || (n > 128))	continue;
370 
371 				/* find error in frequency this setting gives */
372 				error = fabs(req_sclk - (((si->ps.f_ref / m) * n) / p));
373 
374 				/* note the setting if best yet */
375 				if (error < error_best)
376 				{
377 					error_best = error;
378 					best[0]=m;
379 					best[1]=n;
380 					best[2]=p;
381 				}
382 			}
383 		}
384 	}
385 
386 	/* setup the scalers programming values for found optimum setting */
387 	m=best[0] - 1;
388 	n=best[1] - 1;
389 	p=best[2] - 1;
390 
391 	/* calc the needed PLL loopbackfilter setting belonging to current VCO speed,
392 	 * for the current card (see G100, G200 and G400 specs). */
393 	f_vco = (si->ps.f_ref / (m + 1)) * (n + 1);
394 	LOG(2,("DAC: sys VCO frequency found %fMhz\n", f_vco));
395 
396 	switch(si->ps.card_type)
397 	{
398 	default:
399 		for(;;)
400 		{
401 			if (f_vco >= 240) {p |= (0x03 << 3); break;};
402 			if (f_vco >= 170) {p |= (0x02 << 3); break;};
403 			if (f_vco >= 110) {p |= (0x01 << 3); break;};
404 			break;
405 		}
406 		break;
407 	}
408 
409 	/* return the results */
410 	*calc_sclk = f_vco / ((p & 0x07) + 1);
411 	*m_result = m;
412 	*n_result = n;
413 	*p_result = p;
414 
415 	/* display the found pixelclock values */
416 	LOG(2,("DAC: sys PLL check: requested %fMHz got %fMHz, mnp 0x%02x 0x%02x 0x%02x\n",
417 		req_sclk, *calc_sclk, *m_result, *n_result, *p_result));
418 
419 	return B_OK;
420 }
421 
422 /*set up system pll - NB mclk is memory clock */
423 status_t g400_dac_set_sys_pll()
424 {
425 	/* values for DAC sys pll registers */
426 	uint8 m, n, p;
427 //	uint time = 0;
428 	float calc_sclk;
429 
430 	LOG(1,("DAC: Setting up G400/G400MAX system clock\n"));
431 	g100_g400max_dac_sys_pll_find((float)si->ps.std_engine_clock, &calc_sclk, &m, &n, &p);
432 
433 	/* reprogram the clock - set PCI/AGP, program, set to programmed */
434 	/* clear, so don't o/clock addons */
435 //	CFGW(OPTION2, 0);
436 	/* disable the SYSPLL */
437 //	CFGW(OPTION, CFGR(OPTION) | 0x04);
438 	/* select the PCI/AGP clock */
439 //	CFGW(OPTION3, 0);
440 	/* enable the SYSPLL */
441 //	CFGW(OPTION, CFGR(OPTION) & 0xfffffffb);
442 
443 	/* program the new clock */
444 //	DXIW(SYSPLLM, m);
445 //	DXIW(SYSPLLN, n);
446 //	DXIW(SYSPLLP, p);
447 
448 	/* Wait for the SYSPLL frequency to lock until timeout occurs */
449 /*	while((!(DXIR(SYSPLLSTAT)&0x40)) & (time <= 2000))
450 	{
451 		time++;
452 		snooze(1);
453 	}
454 
455 	if (time > 2000)
456 		LOG(2,("DAC: sys PLL frequency not locked!\n"));
457 	else
458 		LOG(2,("DAC: sys PLL frequency locked\n"));
459 */
460 	/* disable the SYSPLL */
461 //	CFGW(OPTION, CFGR(OPTION) | 0x04);
462 	/* setup Gclk, Mclk and Wclk divs via PINS and select SYSPLL as system clock source */
463 //	CFGW(OPTION3, si->ps.option3_reg);
464 	/* make sure the PLLs are not swapped (set default config) */
465 //	CFGW(OPTION, CFGR(OPTION) & 0xffffffbf);
466 	/* enable the SYSPLL (and make sure the SYSPLL is indeed powered up) */
467 //	CFGW(OPTION, (CFGR(OPTION) & 0xfffffffb) | 0x20);
468 
469 	return B_OK;
470 }
471