xref: /haiku/src/add-ons/accelerants/skeleton/SetDisplayMode.c (revision 1deede7388b04dbeec5af85cae7164735ea9e70d)
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 	eng_set_bool_state sbs;
23 
24 	/* set the magic number so the driver knows we're for real */
25 	sbs.magic = SKEL_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, ENG_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 	eng_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 = eng_dac_crt_connected();
116 //		crt2 = eng_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 //			eng_general_head_select(true);
136 //		else
137 //			eng_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 = eng_dac_crt_connected();
249 //			crt2 = eng_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 			eng_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 status = B_OK;
283 
284 		if (status==B_ERROR)
285 			LOG(8,("CRTC: error setting pixel clock (internal DAC)\n"));
286 
287 		/* set the colour depth for CRTC1 and the DAC */
288 		/* first set the colordepth */
289 //		head1_depth(colour_mode);
290 		/* then(!) program the PAL (<8bit colordepth does not support 8bit PAL) */
291 //		head1_mode(colour_mode,1.0);
292 
293 		/* set the display pitch */
294 //		head1_set_display_pitch();
295 
296 		/* tell the card what memory to display */
297 //		head1_set_display_start(startadd,colour_depth1);
298 
299 		/* set the timing */
300 //		head1_set_timing(target);
301 
302 		//fixme: shut-off the videoPLL if it exists...
303 	}
304 
305 	/* update driver's mode store */
306 	si->dm = target;
307 
308 	/* turn screen one on */
309 //	head1_dpms(display, h, v);
310 	/* turn screen two on if a dualhead mode is active */
311 //	if (target.flags & DUALHEAD_BITS) head2_dpms(display,h,v);
312 
313 	/* set up acceleration for this mode */
314 //	eng_acc_init();
315 	/* set up overlay unit for this mode */
316 //	eng_bes_init();
317 
318 	LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0));
319 
320 	/* enable interrupts using the kernel driver */
321 	interrupt_enable(true);
322 
323 	/* optimize memory-access if needed */
324 //	head1_mem_priority(colour_depth1);
325 
326 	/* Tune RAM CAS-latency if needed. Must be done *here*! */
327 	eng_set_cas_latency();
328 
329 	return B_OK;
330 }
331 
332 /*
333 	Set which pixel of the virtual frame buffer will show up in the
334 	top left corner of the display device.  Used for page-flipping
335 	games and virtual desktops.
336 */
337 status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start) {
338 	uint8 colour_depth;
339 	uint32 startadd,startadd_right;
340 
341 	LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start));
342 
343 	/* nVidia cards support pixelprecise panning on both heads in all modes:
344 	 * No stepping granularity needed! */
345 
346 	/* determine bits used for the colordepth */
347 	switch(si->dm.space)
348 	{
349 	case B_CMAP8:
350 		colour_depth=8;
351 		break;
352 	case B_RGB15_LITTLE:
353 	case B_RGB16_LITTLE:
354 		colour_depth=16;
355 		break;
356 	case B_RGB24_LITTLE:
357 		colour_depth=24;
358 		break;
359 	case B_RGB32_LITTLE:
360 		colour_depth=32;
361 		break;
362 	default:
363 		return B_ERROR;
364 	}
365 
366 	/* do not run past end of display */
367 	switch (si->dm.flags & DUALHEAD_BITS)
368 	{
369 	case DUALHEAD_ON:
370 	case DUALHEAD_SWITCH:
371 		if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width)
372 			return B_ERROR;
373 		break;
374 	default:
375 		if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width)
376 			return B_ERROR;
377 		break;
378 	}
379 	if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height)
380 		return B_ERROR;
381 
382 	/* everybody remember where we parked... */
383 	si->dm.h_display_start = h_display_start;
384 	si->dm.v_display_start = v_display_start;
385 
386 	/* actually set the registers */
387 	//fixme: seperate both heads: we need a secondary si->fbc!
388 	startadd = v_display_start * si->fbc.bytes_per_row;
389 	startadd += h_display_start * (colour_depth >> 3);
390 	startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
391 	startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3);
392 
393 	interrupt_enable(false);
394 
395 	switch (si->dm.flags & DUALHEAD_BITS)
396 	{
397 		case DUALHEAD_ON:
398 		case DUALHEAD_SWITCH:
399 //			head1_set_display_start(startadd,colour_depth);
400 //			head2_set_display_start(startadd_right,colour_depth);
401 			break;
402 		case DUALHEAD_OFF:
403 //			head1_set_display_start(startadd,colour_depth);
404 			break;
405 		case DUALHEAD_CLONE:
406 //			head1_set_display_start(startadd,colour_depth);
407 //			head2_set_display_start(startadd,colour_depth);
408 			break;
409 	}
410 
411 	interrupt_enable(true);
412 	return B_OK;
413 }
414 
415 /* Set the indexed color palette */
416 void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) {
417 	int i;
418 	uint8 *r,*g,*b;
419 
420 	/* Protect gamma correction when not in CMAP8 */
421 	if (si->dm.space != B_CMAP8) return;
422 
423 	r=si->color_data;
424 	g=r+256;
425 	b=g+256;
426 
427 	i=first;
428 	while (count--)
429 	{
430 		r[i]=*color_data++;
431 		g[i]=*color_data++;
432 		b[i]=*color_data++;
433 		i++;
434 	}
435 //	head1_palette(r,g,b);
436 	if (si->dm.flags & DUALHEAD_BITS) head2_palette(r,g,b);
437 }
438 
439 /* Put the display into one of the Display Power Management modes. */
440 status_t SET_DPMS_MODE(uint32 dpms_flags) {
441 	interrupt_enable(false);
442 
443 	LOG(4,("SET_DPMS_MODE: 0x%08x\n", dpms_flags));
444 
445 	if (si->dm.flags & DUALHEAD_BITS) /*dualhead*/
446 	{
447 		switch(dpms_flags)
448 		{
449 		case B_DPMS_ON:	/* H: on, V: on, display on */
450 //			head1_dpms(true, true, true);
451 //			if (si->ps.secondary_head) head2_dpms(true, true, true);
452 			break;
453 		case B_DPMS_STAND_BY:
454 //			head1_dpms(false, false, true);
455 //			if (si->ps.secondary_head) head2_dpms(false, false, true);
456 			break;
457 		case B_DPMS_SUSPEND:
458 //			head1_dpms(false, true, false);
459 //			if (si->ps.secondary_head) head2_dpms(false, true, false);
460 			break;
461 		case B_DPMS_OFF: /* H: off, V: off, display off */
462 //			head1_dpms(false, false, false);
463 //			if (si->ps.secondary_head) head2_dpms(false, false, false);
464 			break;
465 		default:
466 			LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags));
467 			interrupt_enable(true);
468 			return B_ERROR;
469 		}
470 	}
471 	else /* singlehead */
472 	{
473 		switch(dpms_flags)
474 		{
475 		case B_DPMS_ON:	/* H: on, V: on, display on */
476 //			head1_dpms(true, true, true);
477 			break;
478 		case B_DPMS_STAND_BY:
479 //			head1_dpms(false, false, true);
480 			break;
481 		case B_DPMS_SUSPEND:
482 //			head1_dpms(false, true, false);
483 			break;
484 		case B_DPMS_OFF: /* H: off, V: off, display off */
485 //			head1_dpms(false, false, false);
486 			break;
487 		default:
488 			LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags));
489 			interrupt_enable(true);
490 			return B_ERROR;
491 		}
492 	}
493 	interrupt_enable(true);
494 	return B_OK;
495 }
496 
497 /* Report device DPMS capabilities */
498 uint32 DPMS_CAPABILITIES(void) {
499 	return 	(B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF);
500 }
501 
502 /* Return the current DPMS mode */
503 uint32 DPMS_MODE(void) {
504 	bool display, h, v;
505 
506 	interrupt_enable(false);
507 //	head1_dpms_fetch(&display, &h, &v);
508 display = h = v = true;
509 
510 	interrupt_enable(true);
511 
512 	if (display && h && v)
513 		return B_DPMS_ON;
514 	else if(v)
515 		return B_DPMS_STAND_BY;
516 	else if(h)
517 		return B_DPMS_SUSPEND;
518 	else
519 		return B_DPMS_OFF;
520 }
521