xref: /haiku/src/add-ons/accelerants/nvidia/SetDisplayMode.c (revision 81f5654c124bf46fba0fd251f208e2d88d81e1ce)
1 
2 /*
3 	Copyright 1999, Be Incorporated.   All Rights Reserved.
4 	This file may be used under the terms of the Be Sample Code License.
5 
6 	Other authors:
7 	Mark Watson,
8 	Apsed,
9 	Rudolf Cornelissen 11/2002-4/2004
10 */
11 
12 #define MODULE_BIT 0x00200000
13 
14 #include "acc_std.h"
15 
16 /*
17 	Enable/Disable interrupts.  Just a wrapper around the
18 	ioctl() to the kernel driver.
19 */
20 static void interrupt_enable(bool flag) {
21 	status_t result;
22 	nv_set_bool_state sbs;
23 
24 	/* set the magic number so the driver knows we're for real */
25 	sbs.magic = NV_PRIVATE_DATA_MAGIC;
26 	sbs.do_it = flag;
27 	/* contact driver and get a pointer to the registers and shared data */
28 	result = ioctl(fd, NV_RUN_INTERRUPTS, &sbs, sizeof(sbs));
29 }
30 
31 /* First validate the mode, then call lots of bit banging stuff to set the mode(s)! */
32 status_t SET_DISPLAY_MODE(display_mode *mode_to_set)
33 {
34 	/* BOUNDS WARNING:
35 	 * It's impossible to deviate whatever small amount in a display_mode if the lower
36 	 * and upper limits are the same!
37 	 * Besides:
38 	 * BeOS (tested R5.0.3PE) is failing BWindowScreen::SetFrameBuffer() if PROPOSEMODE
39 	 * returns B_BAD_VALUE!
40 	 * Which means PROPOSEMODE should not return that on anything except on
41 	 * deviations for:
42 	 * display_mode.virtual_width;
43 	 * display_mode.virtual_height;
44 	 * display_mode.timing.h_display;
45 	 * display_mode.timing.v_display;
46 	 * So:
47 	 * We don't use bounds here by making sure bounds and target are the same struct!
48 	 * (See the call to PROPOSE_DISPLAY_MODE below) */
49 	display_mode /*bounds,*/ target;
50 
51 	uint8 colour_depth1 = 32;
52 	status_t result;
53 	uint32 startadd,startadd_right;
54 	bool display, h, v;
55 //	bool crt1, crt2, cross;
56 
57 	/* Adjust mode to valid one and fail if invalid */
58 	target /*= bounds*/ = *mode_to_set;
59 	/* show the mode bits */
60 	LOG(1, ("SETMODE: (ENTER) initial modeflags: $%08x\n", target.flags));
61 	LOG(1, ("SETMODE: requested target pixelclock %dkHz\n",  target.timing.pixel_clock));
62 	LOG(1, ("SETMODE: requested virtual_width %d, virtual_height %d\n",
63 										target.virtual_width, target.virtual_height));
64 
65 	/* See BOUNDS WARNING above... */
66 	if (PROPOSE_DISPLAY_MODE(&target, &target, &target) == B_ERROR)	return B_ERROR;
67 
68 	/* if not dualhead capable card clear dualhead flags */
69 	if (!(target.flags & DUALHEAD_CAPABLE))
70 	{
71 		target.flags &= ~DUALHEAD_BITS;
72 	}
73 	/* if not TVout capable card clear TVout flags */
74 	if (!(target.flags & TV_CAPABLE))
75 	{
76 		target.flags &= ~TV_BITS;
77 	}
78 	LOG(1, ("SETMODE: (CONT.) validated command modeflags: $%08x\n", target.flags));
79 
80 	/* disable interrupts using the kernel driver */
81 	interrupt_enable(false);
82 
83 	/* find current DPMS state, then turn off screen(s) */
84 	head1_dpms_fetch(&display, &h, &v);
85 	head1_dpms(false, false, false);
86 	if (si->ps.secondary_head) head2_dpms(false, false, false);
87 
88 	/*where in framebuffer the screen is (should this be dependant on previous MOVEDISPLAY?)*/
89 	startadd = (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
90 
91 	/* calculate and set new mode bytes_per_row */
92 	nv_general_validate_pic_size (&target, &si->fbc.bytes_per_row, &si->acc_mode);
93 
94 	/*Perform the very long mode switch!*/
95 	if (target.flags & DUALHEAD_BITS) /*if some dualhead mode*/
96 	{
97 		uint8 colour_depth2 = colour_depth1;
98 
99 		/* init display mode for secondary head */
100 		display_mode target2 = target;
101 
102 		LOG(1,("SETMODE: setting DUALHEAD mode\n"));
103 
104 		/* validate flags for secondary TVout */
105 		if ((i2c_sec_tv_adapter() != B_OK) && (target2.flags & TV_BITS))
106 		{
107 			target.flags &= ~TV_BITS;//still needed for some routines...
108 			target2.flags &= ~TV_BITS;
109 			LOG(1,("SETMODE: blocking TVout: no TVout cable connected!\n"));
110 		}
111 
112 		/* detect which connectors have a CRT connected */
113 		//fixme: 'hot-plugging' for analog monitors removed: remove code as well;
114 		//or make it work with digital panels connected as well.
115 //		crt1 = nv_dac_crt_connected();
116 //		crt2 = nv_dac2_crt_connected();
117 		/* connect outputs 'straight-through' */
118 //		if (crt1)
119 //		{
120 			/* connector1 is used as primary output */
121 //			cross = false;
122 //		}
123 //		else
124 //		{
125 //			if (crt2)
126 				/* connector2 is used as primary output */
127 //				cross = true;
128 //			else
129 				/* no CRT detected: assume connector1 is used as primary output */
130 //				cross = false;
131 //		}
132 		/* set output connectors assignment if possible */
133 		if ((target.flags & DUALHEAD_BITS) == DUALHEAD_SWITCH)
134 			/* invert output assignment in switch mode */
135 			nv_general_head_select(true);
136 		else
137 			nv_general_head_select(false);
138 
139 		/* set the pixel clock PLL(s) */
140 		LOG(8,("SETMODE: target clock %dkHz\n",target.timing.pixel_clock));
141 		if (head1_set_pix_pll(target) == B_ERROR)
142 			LOG(8,("SETMODE: error setting pixel clock (internal DAC)\n"));
143 
144 		/* we do not need to set the pixelclock here for a head that's in TVout mode */
145 		if (!(target2.flags & TV_BITS))
146 		{
147 			LOG(8,("SETMODE: target2 clock %dkHz\n",target2.timing.pixel_clock));
148 			if (head2_set_pix_pll(target2) == B_ERROR)
149 				LOG(8,("SETMODE: error setting pixel clock (DAC2)\n"));
150 		}
151 
152 		/*set the colour depth for CRTC1 and the DAC */
153 		switch(target.space)
154 		{
155 		case B_CMAP8:
156 			colour_depth1 =  8;
157 			head1_mode(BPP8, 1.0);
158 			head1_depth(BPP8);
159 			break;
160 		case B_RGB15_LITTLE:
161 			colour_depth1 = 16;
162 			head1_mode(BPP15, 1.0);
163 			head1_depth(BPP15);
164 			break;
165 		case B_RGB16_LITTLE:
166 			colour_depth1 = 16;
167 			head1_mode(BPP16, 1.0);
168 			head1_depth(BPP16);
169 			break;
170 		case B_RGB32_LITTLE:
171 			colour_depth1 = 32;
172 			head1_mode(BPP32, 1.0);
173 			head1_depth(BPP32);
174 			break;
175 		}
176 		/*set the colour depth for CRTC2 and DAC2 */
177 		switch(target2.space)
178 		{
179 		case B_CMAP8:
180 			colour_depth2 =  8;
181 			head2_mode(BPP8, 1.0);
182 			head2_depth(BPP8);
183 			break;
184 		case B_RGB15_LITTLE:
185 			colour_depth2 = 16;
186 			head2_mode(BPP15, 1.0);
187 			head2_depth(BPP15);
188 			break;
189 		case B_RGB16_LITTLE:
190 			colour_depth2 = 16;
191 			head2_mode(BPP16, 1.0);
192 			head2_depth(BPP16);
193 			break;
194 		case B_RGB32_LITTLE:
195 			colour_depth2 = 32;
196 			head2_mode(BPP32, 1.0);
197 			head2_depth(BPP32);
198 			break;
199 		}
200 
201 		/* check if we are doing interlaced TVout mode */
202 		si->interlaced_tv_mode = false;
203 /*		if ((target2.flags & TV_BITS) && (si->ps.card_type >= G450))
204 			si->interlaced_tv_mode = true;
205 */
206 		/*set the display(s) pitches*/
207 		head1_set_display_pitch ();
208 		//fixme: seperate for real dualhead modes:
209 		//we need a secondary si->fbc!
210 		head2_set_display_pitch ();
211 
212 		/*work out where the "right" screen starts*/
213 		startadd_right = startadd + (target.timing.h_display * (colour_depth1 >> 3));
214 
215 		/* Tell card what memory to display */
216 		switch (target.flags & DUALHEAD_BITS)
217 		{
218 		case DUALHEAD_ON:
219 		case DUALHEAD_SWITCH:
220 			head1_set_display_start(startadd,colour_depth1);
221 			head2_set_display_start(startadd_right,colour_depth2);
222 			break;
223 		case DUALHEAD_CLONE:
224 			head1_set_display_start(startadd,colour_depth1);
225 			head2_set_display_start(startadd,colour_depth2);
226 			break;
227 		}
228 
229 		/* set the timing */
230 		head1_set_timing(target);
231 		/* we do not need to setup CRTC2 here for a head that's in TVout mode */
232 		if (!(target2.flags & TV_BITS))	result = head2_set_timing(target2);
233 
234 		/* TVout support: setup CRTC2 and it's pixelclock */
235 		if (si->ps.tvout && (target2.flags & TV_BITS)) maventv_init(target2);
236 	}
237 	else /* single head mode */
238 	{
239 		status_t status;
240 		int colour_mode = BPP32;
241 
242 		/* connect output */
243 		if (si->ps.secondary_head)
244 		{
245 			/* detect which connectors have a CRT connected */
246 			//fixme: 'hot-plugging' for analog monitors removed: remove code as well;
247 			//or make it work with digital panels connected as well.
248 //			crt1 = nv_dac_crt_connected();
249 //			crt2 = nv_dac2_crt_connected();
250 			/* connect outputs 'straight-through' */
251 //			if (crt1)
252 //			{
253 				/* connector1 is used as primary output */
254 //				cross = false;
255 //			}
256 //			else
257 //			{
258 //				if (crt2)
259 					/* connector2 is used as primary output */
260 //					cross = true;
261 //				else
262 					/* no CRT detected: assume connector1 is used as primary output */
263 //					cross = false;
264 //			}
265 			/* set output connectors assignment if possible */
266 			nv_general_head_select(false);
267 		}
268 
269 		switch(target.space)
270 		{
271 		case B_CMAP8:        colour_depth1 =  8; colour_mode = BPP8;  break;
272 		case B_RGB15_LITTLE: colour_depth1 = 16; colour_mode = BPP15; break;
273 		case B_RGB16_LITTLE: colour_depth1 = 16; colour_mode = BPP16; break;
274 		case B_RGB32_LITTLE: colour_depth1 = 32; colour_mode = BPP32; break;
275 		default:
276 			LOG(8,("SETMODE: Invalid singlehead colour depth 0x%08x\n", target.space));
277 			return B_ERROR;
278 		}
279 
280 		/* set the pixel clock PLL */
281 		status = head1_set_pix_pll(target);
282 
283 		if (status==B_ERROR)
284 			LOG(8,("CRTC: error setting pixel clock (internal DAC)\n"));
285 
286 		/* set the colour depth for CRTC1 and the DAC */
287 		/* first set the colordepth */
288 		head1_depth(colour_mode);
289 		/* then(!) program the PAL (<8bit colordepth does not support 8bit PAL) */
290 		head1_mode(colour_mode,1.0);
291 
292 		/* set the display pitch */
293 		head1_set_display_pitch();
294 
295 		/* tell the card what memory to display */
296 		head1_set_display_start(startadd,colour_depth1);
297 
298 		/* set the timing */
299 		head1_set_timing(target);
300 
301 		//fixme: shut-off the videoPLL if it exists...
302 	}
303 
304 	/* update driver's mode store */
305 	si->dm = target;
306 
307 	/* turn screen one on */
308 	head1_dpms(display, h, v);
309 	/* turn screen two on if a dualhead mode is active */
310 	if (target.flags & DUALHEAD_BITS) head2_dpms(display,h,v);
311 
312 	/* set up acceleration for this mode */
313 	nv_acc_init();
314 	/* set up overlay unit for this mode */
315 	nv_bes_init();
316 
317 	LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0));
318 
319 	/* enable interrupts using the kernel driver */
320 	interrupt_enable(true);
321 
322 	/* optimize memory-access if needed */
323 //	head1_mem_priority(colour_depth1);
324 
325 	/* Tune RAM CAS-latency if needed. Must be done *here*! */
326 	nv_set_cas_latency();
327 
328 	return B_OK;
329 }
330 
331 /*
332 	Set which pixel of the virtual frame buffer will show up in the
333 	top left corner of the display device.  Used for page-flipping
334 	games and virtual desktops.
335 */
336 status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start) {
337 	uint8 colour_depth;
338 	uint32 startadd,startadd_right;
339 
340 	LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start));
341 
342 	/* nVidia cards support pixelprecise panning on both heads in all modes:
343 	 * No stepping granularity needed! */
344 
345 	/* determine bits used for the colordepth */
346 	switch(si->dm.space)
347 	{
348 	case B_CMAP8:
349 		colour_depth=8;
350 		break;
351 	case B_RGB15_LITTLE:
352 	case B_RGB16_LITTLE:
353 		colour_depth=16;
354 		break;
355 	case B_RGB24_LITTLE:
356 		colour_depth=24;
357 		break;
358 	case B_RGB32_LITTLE:
359 		colour_depth=32;
360 		break;
361 	default:
362 		return B_ERROR;
363 	}
364 
365 	/* do not run past end of display */
366 	switch (si->dm.flags & DUALHEAD_BITS)
367 	{
368 	case DUALHEAD_ON:
369 	case DUALHEAD_SWITCH:
370 		if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width)
371 			return B_ERROR;
372 		break;
373 	default:
374 		if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width)
375 			return B_ERROR;
376 		break;
377 	}
378 	if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height)
379 		return B_ERROR;
380 
381 	/* everybody remember where we parked... */
382 	si->dm.h_display_start = h_display_start;
383 	si->dm.v_display_start = v_display_start;
384 
385 	/* actually set the registers */
386 	//fixme: seperate both heads: we need a secondary si->fbc!
387 	startadd = v_display_start * si->fbc.bytes_per_row;
388 	startadd += h_display_start * (colour_depth >> 3);
389 	startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
390 	startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3);
391 
392 	interrupt_enable(false);
393 
394 	switch (si->dm.flags & DUALHEAD_BITS)
395 	{
396 		case DUALHEAD_ON:
397 		case DUALHEAD_SWITCH:
398 			head1_set_display_start(startadd,colour_depth);
399 			head2_set_display_start(startadd_right,colour_depth);
400 			break;
401 		case DUALHEAD_OFF:
402 			head1_set_display_start(startadd,colour_depth);
403 			break;
404 		case DUALHEAD_CLONE:
405 			head1_set_display_start(startadd,colour_depth);
406 			head2_set_display_start(startadd,colour_depth);
407 			break;
408 	}
409 
410 	interrupt_enable(true);
411 	return B_OK;
412 }
413 
414 /* Set the indexed color palette */
415 void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) {
416 	int i;
417 	uint8 *r,*g,*b;
418 
419 	/* Protect gamma correction when not in CMAP8 */
420 	if (si->dm.space != B_CMAP8) return;
421 
422 	r=si->color_data;
423 	g=r+256;
424 	b=g+256;
425 
426 	i=first;
427 	while (count--)
428 	{
429 		r[i]=*color_data++;
430 		g[i]=*color_data++;
431 		b[i]=*color_data++;
432 		i++;
433 	}
434 	head1_palette(r,g,b);
435 	if (si->dm.flags & DUALHEAD_BITS) head2_palette(r,g,b);
436 }
437 
438 /* Put the display into one of the Display Power Management modes. */
439 status_t SET_DPMS_MODE(uint32 dpms_flags) {
440 	interrupt_enable(false);
441 
442 	LOG(4,("SET_DPMS_MODE: 0x%08x\n", dpms_flags));
443 
444 	if (si->dm.flags & DUALHEAD_BITS) /*dualhead*/
445 	{
446 		switch(dpms_flags)
447 		{
448 		case B_DPMS_ON:	/* H: on, V: on, display on */
449 			head1_dpms(true, true, true);
450 			if (si->ps.secondary_head) head2_dpms(true, true, true);
451 			break;
452 		case B_DPMS_STAND_BY:
453 			head1_dpms(false, false, true);
454 			if (si->ps.secondary_head) head2_dpms(false, false, true);
455 			break;
456 		case B_DPMS_SUSPEND:
457 			head1_dpms(false, true, false);
458 			if (si->ps.secondary_head) head2_dpms(false, true, false);
459 			break;
460 		case B_DPMS_OFF: /* H: off, V: off, display off */
461 			head1_dpms(false, false, false);
462 			if (si->ps.secondary_head) head2_dpms(false, false, false);
463 			break;
464 		default:
465 			LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags));
466 			interrupt_enable(true);
467 			return B_ERROR;
468 		}
469 	}
470 	else /* singlehead */
471 	{
472 		switch(dpms_flags)
473 		{
474 		case B_DPMS_ON:	/* H: on, V: on, display on */
475 			head1_dpms(true, true, true);
476 			break;
477 		case B_DPMS_STAND_BY:
478 			head1_dpms(false, false, true);
479 			break;
480 		case B_DPMS_SUSPEND:
481 			head1_dpms(false, true, false);
482 			break;
483 		case B_DPMS_OFF: /* H: off, V: off, display off */
484 			head1_dpms(false, false, false);
485 			break;
486 		default:
487 			LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags));
488 			interrupt_enable(true);
489 			return B_ERROR;
490 		}
491 	}
492 	interrupt_enable(true);
493 	return B_OK;
494 }
495 
496 /* Report device DPMS capabilities */
497 uint32 DPMS_CAPABILITIES(void) {
498 	return 	(B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF);
499 }
500 
501 /* Return the current DPMS mode */
502 uint32 DPMS_MODE(void) {
503 	bool display, h, v;
504 
505 	interrupt_enable(false);
506 	head1_dpms_fetch(&display, &h, &v);
507 	interrupt_enable(true);
508 
509 	if (display && h && v)
510 		return B_DPMS_ON;
511 	else if(v)
512 		return B_DPMS_STAND_BY;
513 	else if(h)
514 		return B_DPMS_SUSPEND;
515 	else
516 		return B_DPMS_OFF;
517 }
518