xref: /haiku/src/add-ons/accelerants/via/SetDisplayMode.c (revision 1a3518cf757c2da8006753f83962da5935bbc82b)
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-9/2005
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 = VIA_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 
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 //	eng_acc_init();
314 	/* set up overlay unit for this mode */
315 	eng_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 	eng_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 	/* VIA CRTC1 handles multiples of 8 for 8bit, 4 for 16bit, 2 for 32 bit
343 	   VIA CRTC2 is yet unknown...
344      */
345 
346 	/* reset lower bits, don't return an error! */
347 	if (si->dm.flags & DUALHEAD_BITS)
348 	{
349 		//fixme for VIA...
350 		switch(si->dm.space)
351 		{
352 		case B_RGB16_LITTLE:
353 			colour_depth=16;
354 			h_display_start &= ~0x1f;
355 			break;
356 		case B_RGB32_LITTLE:
357 			colour_depth=32;
358 			h_display_start &= ~0x0f;
359 			break;
360 		default:
361 			LOG(8,("SET:Invalid DH colour depth 0x%08x, should never happen\n", si->dm.space));
362 			return B_ERROR;
363 		}
364 	}
365 	else
366 	{
367 		switch(si->dm.space)
368 		{
369 		case B_CMAP8:
370 			colour_depth=8;
371 			h_display_start &= ~0x07;
372 			break;
373 		case B_RGB15_LITTLE: case B_RGB16_LITTLE:
374 			colour_depth=16;
375 			h_display_start &= ~0x03;
376 			break;
377 		case B_RGB32_LITTLE:
378 			colour_depth=32;
379 			h_display_start &= ~0x01;
380 			break;
381 		default:
382 			return B_ERROR;
383 		}
384 	}
385 
386 	/* do not run past end of display */
387 	switch (si->dm.flags & DUALHEAD_BITS)
388 	{
389 	case DUALHEAD_ON:
390 	case DUALHEAD_SWITCH:
391 		if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width)
392 			return B_ERROR;
393 		break;
394 	default:
395 		if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width)
396 			return B_ERROR;
397 		break;
398 	}
399 	if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height)
400 		return B_ERROR;
401 
402 	/* everybody remember where we parked... */
403 	si->dm.h_display_start = h_display_start;
404 	si->dm.v_display_start = v_display_start;
405 
406 	/* actually set the registers */
407 	//fixme: seperate both heads: we need a secondary si->fbc!
408 	startadd = v_display_start * si->fbc.bytes_per_row;
409 	startadd += h_display_start * (colour_depth >> 3);
410 	startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
411 	startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3);
412 
413 	interrupt_enable(false);
414 
415 	switch (si->dm.flags & DUALHEAD_BITS)
416 	{
417 		case DUALHEAD_ON:
418 		case DUALHEAD_SWITCH:
419 			head1_set_display_start(startadd,colour_depth);
420 //			head2_set_display_start(startadd_right,colour_depth);
421 			break;
422 		case DUALHEAD_OFF:
423 			head1_set_display_start(startadd,colour_depth);
424 			break;
425 		case DUALHEAD_CLONE:
426 			head1_set_display_start(startadd,colour_depth);
427 //			head2_set_display_start(startadd,colour_depth);
428 			break;
429 	}
430 
431 	interrupt_enable(true);
432 	return B_OK;
433 }
434 
435 /* Set the indexed color palette */
436 void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) {
437 	int i;
438 	uint8 *r,*g,*b;
439 
440 	/* Protect gamma correction when not in CMAP8 */
441 	if (si->dm.space != B_CMAP8) return;
442 
443 	r=si->color_data;
444 	g=r+256;
445 	b=g+256;
446 
447 	i=first;
448 	while (count--)
449 	{
450 		r[i]=*color_data++;
451 		g[i]=*color_data++;
452 		b[i]=*color_data++;
453 		i++;
454 	}
455 	head1_palette(r,g,b);
456 //	if (si->dm.flags & DUALHEAD_BITS) head2_palette(r,g,b);
457 }
458 
459 /* Put the display into one of the Display Power Management modes. */
460 status_t SET_DPMS_MODE(uint32 dpms_flags) {
461 	interrupt_enable(false);
462 
463 	LOG(4,("SET_DPMS_MODE: 0x%08x\n", dpms_flags));
464 
465 #if 0
466 	if (si->dm.flags & DUALHEAD_BITS) /*dualhead*/
467 	{
468 		switch(dpms_flags)
469 		{
470 		case B_DPMS_ON:	/* H: on, V: on, display on */
471 			head1_dpms(true, true, true);
472 			if (si->ps.secondary_head) head2_dpms(true, true, true);
473 			break;
474 		case B_DPMS_STAND_BY:
475 			head1_dpms(false, false, true);
476 			if (si->ps.secondary_head) head2_dpms(false, false, true);
477 			break;
478 		case B_DPMS_SUSPEND:
479 			head1_dpms(false, true, false);
480 			if (si->ps.secondary_head) head2_dpms(false, true, false);
481 			break;
482 		case B_DPMS_OFF: /* H: off, V: off, display off */
483 			head1_dpms(false, false, false);
484 			if (si->ps.secondary_head) head2_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 	} else /* singlehead */
492 #endif
493 	{
494 		switch(dpms_flags)
495 		{
496 		case B_DPMS_ON:	/* H: on, V: on, display on */
497 			head1_dpms(true, true, true);
498 			break;
499 		case B_DPMS_STAND_BY:
500 			head1_dpms(false, false, true);
501 			break;
502 		case B_DPMS_SUSPEND:
503 			head1_dpms(false, true, false);
504 			break;
505 		case B_DPMS_OFF: /* H: off, V: off, display off */
506 			head1_dpms(false, false, false);
507 			break;
508 		default:
509 			LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags));
510 			interrupt_enable(true);
511 			return B_ERROR;
512 		}
513 	}
514 	interrupt_enable(true);
515 	return B_OK;
516 }
517 
518 /* Report device DPMS capabilities */
519 uint32 DPMS_CAPABILITIES(void) {
520 	return 	(B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF);
521 }
522 
523 /* Return the current DPMS mode */
524 uint32 DPMS_MODE(void) {
525 	bool display, h, v;
526 
527 	interrupt_enable(false);
528 	head1_dpms_fetch(&display, &h, &v);
529 
530 	interrupt_enable(true);
531 
532 	if (display && h && v)
533 		return B_DPMS_ON;
534 	else if(v)
535 		return B_DPMS_STAND_BY;
536 	else if(h)
537 		return B_DPMS_SUSPEND;
538 	else
539 		return B_DPMS_OFF;
540 }
541