xref: /haiku/src/add-ons/accelerants/matrox/SetDisplayMode.c (revision 9eb55bc1d104b8fda80898f8b25c94d8000c8255)
1 /*
2 	Copyright 1999, Be Incorporated.   All Rights Reserved.
3 	This file may be used under the terms of the Be Sample Code License.
4 
5 	Other authors:
6 	Mark Watson,
7 	Apsed,
8 	Rudolf Cornelissen 11/2002-1/2004
9 */
10 
11 #define MODULE_BIT 0x00200000
12 
13 #include "acc_std.h"
14 
15 /*
16 	Enable/Disable interrupts.  Just a wrapper around the
17 	ioctl() to the kernel driver.
18 */
19 static void interrupt_enable(bool flag) {
20 	status_t result;
21 	gx00_set_bool_state sbs;
22 
23 	/* set the magic number so the driver knows we're for real */
24 	sbs.magic = GX00_PRIVATE_DATA_MAGIC;
25 	sbs.do_it = flag;
26 	/* contact driver and get a pointer to the registers and shared data */
27 	result = ioctl(fd, GX00_RUN_INTERRUPTS, &sbs, sizeof(sbs));
28 }
29 
30 /* First validate the mode, then call lots of bit banging stuff to set the mode(s)! */
31 status_t SET_DISPLAY_MODE(display_mode *mode_to_set)
32 {
33 	/* BOUNDS WARNING:
34 	 * It's impossible to deviate whatever small amount in a display_mode if the lower
35 	 * and upper limits are the same!
36 	 * Besides:
37 	 * BeOS (tested R5.0.3PE) is failing BWindowScreen::SetFrameBuffer() if PROPOSEMODE
38 	 * returns B_BAD_VALUE!
39 	 * Which means PROPOSEMODE should not return that on anything except on
40 	 * deviations for:
41 	 * display_mode.virtual_width;
42 	 * display_mode.virtual_height;
43 	 * display_mode.timing.h_display;
44 	 * display_mode.timing.v_display;
45 	 * So:
46 	 * We don't use bounds here by making sure bounds and target are the same struct!
47 	 * (See the call to PROPOSE_DISPLAY_MODE below) */
48 	display_mode /*bounds,*/ target;
49 
50 	uint8 colour_depth1 = 32;
51 	status_t result;
52 	uint32 startadd,startadd_right;
53 	bool display, h, v;
54 
55 	/* Adjust mode to valid one and fail if invalid */
56 	target /*= bounds*/ = *mode_to_set;
57 	/* show the mode bits */
58 	LOG(1, ("SETMODE: (ENTER) initial modeflags: $%08x\n", target.flags));
59 	LOG(1, ("SETMODE: requested target pixelclock %dkHz\n",  target.timing.pixel_clock));
60 	LOG(1, ("SETMODE: requested virtual_width %d, virtual_height %d\n",
61 										target.virtual_width, target.virtual_height));
62 
63 	/* See BOUNDS WARNING above... */
64 	if (PROPOSE_DISPLAY_MODE(&target, &target, &target) == B_ERROR)	return B_ERROR;
65 
66 	/* if not dualhead capable card clear dualhead flags */
67 	if (!(target.flags & DUALHEAD_CAPABLE))
68 	{
69 		target.flags &= ~DUALHEAD_BITS;
70 	}
71 	/* if not TVout capable card clear TVout flags */
72 	if (!(target.flags & TV_CAPABLE))
73 	{
74 		target.flags &= ~TV_BITS;
75 	}
76 	LOG(1, ("SETMODE: (CONT.) validated command modeflags: $%08x\n", target.flags));
77 
78 	/* overlay engine, cursor and MOVE_DISPLAY need to know the status even when
79 	 * in singlehead mode */
80 	si->switched_crtcs = false;
81 
82 	/* disable interrupts using the kernel driver */
83 	interrupt_enable(false);
84 
85 	/* find current DPMS state, then turn off screen(s) */
86 	gx00_crtc_dpms_fetch(&display, &h, &v);
87 	gx00_crtc_dpms(false, false, false);
88 	if (si->ps.secondary_head) g400_crtc2_dpms(false, false, false);
89 
90 	/*where in framebuffer the screen is (should this be dependant on previous MOVEDISPLAY?)*/
91 	startadd = (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
92 
93 	/* calculate and set new mode bytes_per_row */
94 	gx00_general_validate_pic_size (&target, &si->fbc.bytes_per_row, &si->acc_mode);
95 
96 	/*Perform the very long mode switch!*/
97 	if (target.flags & DUALHEAD_BITS) /*if some dualhead mode*/
98 	{
99 		uint8 colour_depth2 = colour_depth1;
100 
101 		/* init display mode for secondary head */
102 		display_mode target2 = target;
103 
104 		LOG(1,("SETMODE: setting DUALHEAD mode\n"));
105 
106 		/* validate flags for secondary TVout */
107 		if ((i2c_sec_tv_adapter() != B_OK) && (target2.flags & TV_BITS))
108 		{
109 			target.flags &= ~TV_BITS;//still needed for some routines...
110 			target2.flags &= ~TV_BITS;
111 			LOG(1,("SETMODE: blocking TVout: no TVout cable connected!\n"));
112 		}
113 
114 		/* set the pixel clock PLL(s) */
115 		LOG(8,("SETMODE: target clock %dkHz\n",target.timing.pixel_clock));
116 		if (gx00_dac_set_pix_pll(target) == B_ERROR)
117 			LOG(8,("SETMODE: error setting pixel clock (internal DAC)\n"));
118 
119 		/* we do not need to set the pixelclock here for a head that's in TVout mode */
120 		if (!(target2.flags & TV_BITS))
121 		{
122 			LOG(8,("SETMODE: target2 clock %dkHz\n",target2.timing.pixel_clock));
123 			if (gx00_maven_set_vid_pll(target2) == B_ERROR)
124 				LOG(8,("SETMODE: error setting pixel clock (MAVEN)\n"));
125 		}
126 
127 		/*set the colour depth for CRTC1 and the DAC */
128 		switch(target.space)
129 		{
130 		case B_RGB16_LITTLE:
131 			colour_depth1 = 16;
132 			gx00_dac_mode(BPP16, 1.0);
133 			gx00_crtc_depth(BPP16);
134 			break;
135 		case B_RGB32_LITTLE:
136 			colour_depth1 = 32;
137 			gx00_dac_mode(BPP32, 1.0);
138 			gx00_crtc_depth(BPP32);
139 			break;
140 		}
141 		/*set the colour depth for CRTC2 and the MAVEN */
142 		switch(target2.space)
143 		{
144 		case B_RGB16_LITTLE:
145 			colour_depth2 = 16;
146 			gx00_maven_mode(BPP16, 1.0);
147 			g400_crtc2_depth(BPP16);
148 			break;
149 		case B_RGB32_LITTLE:
150 			colour_depth2 = 32;
151 			gx00_maven_mode(BPP32DIR, 1.0);
152 			g400_crtc2_depth(BPP32DIR);
153 			break;
154 		}
155 
156 		/* check if we are doing interlaced TVout mode */
157 		si->interlaced_tv_mode = false;
158 		if ((target2.flags & TV_BITS) && (si->ps.card_type >= G450))
159 			si->interlaced_tv_mode = true;
160 
161 		/*set the display(s) pitches*/
162 		gx00_crtc_set_display_pitch ();
163 		//fixme: seperate for real dualhead modes:
164 		//we need a secondary si->fbc!
165 		g400_crtc2_set_display_pitch ();
166 
167 		/*work out where the "right" screen starts*/
168 		startadd_right=startadd+(target.timing.h_display * (colour_depth1 >> 3));
169 
170 		/* calculate needed MAVEN-CRTC delay: formula valid for straight-through CRTC's */
171 		si->crtc_delay = 44 + 0 * (colour_depth2 == 16);
172 
173 		/* set the outputs */
174 		switch (si->ps.card_type)
175 		{
176 		case G400:
177 		case G400MAX:
178 			/* setup vertical timing adjust for crtc connected to the MAVEN:
179 			 * assuming connected straight through. */
180 			/* (extra "blanking" line for MAVEN hardware design fault) */
181 			target2.timing.v_display++;
182 
183 			switch (target.flags & DUALHEAD_BITS)
184 			{
185 			case DUALHEAD_ON:
186 			case DUALHEAD_CLONE:
187 				gx00_general_dac_select(DS_CRTC1DAC_CRTC2MAVEN);
188 				si->switched_crtcs = false;
189 				break;
190 			case DUALHEAD_SWITCH:
191 				if (i2c_sec_tv_adapter() == B_OK)
192 				{
193 					/* Don't switch CRTC's because MAVEN YUV is impossible then,
194 					 * and primary head output will be limited to 135Mhz pixelclock. */
195 					LOG(4,("SETMODE: secondary TV-adapter detected, switching buffers\n"));
196 					gx00_general_dac_select(DS_CRTC1DAC_CRTC2MAVEN);
197 					si->switched_crtcs = true;
198 				}
199 				else
200 				{
201 					/* This limits the pixelclocks on both heads to 135Mhz,
202 					 * but you can use overlay on the other output now. */
203 					LOG(4,("SETMODE: no secondary TV-adapter detected, switching CRTCs\n"));
204 					gx00_general_dac_select(DS_CRTC1MAVEN_CRTC2DAC);
205 					si->switched_crtcs = false;
206 					/* re-calculate MAVEN-CRTC delay: formula valid for crossed CRTC's */
207 					si->crtc_delay = 17 + 4 * (colour_depth1 == 16);
208 					/* re-setup vertical timing adjust for crtc connected to the MAVEN:
209 					 * cross connected. */
210 					/* (extra "blanking" line for MAVEN hardware design fault) */
211 					target.timing.v_display++;
212 					target2.timing.v_display--;
213 				}
214 				break;
215 			}
216 			break;
217 		case G450:
218 		case G550:
219 			if (!si->ps.primary_dvi)
220 			/* output connector use is always 'straight-through' */
221 			//fixme: re-evaluate when DVI is setup...
222 			{
223 				switch (target.flags & DUALHEAD_BITS)
224 				{
225 				case DUALHEAD_ON:
226 				case DUALHEAD_CLONE:
227 					gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2);
228 					si->switched_crtcs = false;
229 					break;
230 				case DUALHEAD_SWITCH:
231 					if (i2c_sec_tv_adapter() == B_OK)
232 					{
233 						/* Don't switch CRTC's because MAVEN YUV and TVout is impossible then,
234 						 * and primary head output will be limited to 235Mhz pixelclock. */
235 						LOG(4,("SETMODE: secondary TV-adapter detected, switching buffers\n"));
236 						gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2);
237 						si->switched_crtcs = true;
238 					}
239 					else
240 					{
241 						/* This limits the pixelclocks on both heads to 235Mhz,
242 						 * but you can use overlay on the other output now. */
243 						LOG(4,("SETMODE: no secondary TV-adapter detected, switching CRTCs\n"));
244 						gx00_general_dac_select(DS_CRTC1CON2_CRTC2CON1);
245 						si->switched_crtcs = false;
246 					}
247 					break;
248 				}
249 			}
250 			else
251 			/* output connector use is cross-linked if no TV cable connected! */
252 			//fixme: re-evaluate when DVI is setup...
253 			{
254 				switch (target.flags & DUALHEAD_BITS)
255 				{
256 				case DUALHEAD_ON:
257 				case DUALHEAD_CLONE:
258 					if (i2c_sec_tv_adapter() == B_OK)
259 					{
260 						gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2);
261 						si->switched_crtcs = false;
262 					}
263 					else
264 					{
265 						/* This limits the pixelclocks on both heads to 235Mhz,
266 						 * but you can use overlay on the other output now. */
267 						gx00_general_dac_select(DS_CRTC1CON2_CRTC2CON1);
268 						si->switched_crtcs = false;
269 					}
270 					break;
271 				case DUALHEAD_SWITCH:
272 					if (i2c_sec_tv_adapter() == B_OK)
273 					{
274 						/* Don't switch CRTC's because MAVEN YUV and TVout is impossible then,
275 						 * and primary head output will be limited to 235Mhz pixelclock. */
276 						LOG(4,("SETMODE: secondary TV-adapter detected, switching buffers\n"));
277 						gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2);
278 						si->switched_crtcs = true;
279 					}
280 					else
281 					{
282 						LOG(4,("SETMODE: no secondary TV-adapter detected, switching CRTCs\n"));
283 						gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2);
284 						si->switched_crtcs = false;
285 					}
286 					break;
287 				}
288 			}
289 			break;
290 		default:
291 			break;
292 		}
293 
294 		if (si->switched_crtcs)
295 		{
296 				uint32 temp = startadd;
297 				startadd = startadd_right;
298 				startadd_right = temp;
299 		}
300 
301 		/*Tell card what memory to display*/
302 		switch (target.flags & DUALHEAD_BITS)
303 		{
304 		case DUALHEAD_ON:
305 		case DUALHEAD_SWITCH:
306 			gx00_crtc_set_display_start(startadd,colour_depth1);
307 			g400_crtc2_set_display_start(startadd_right,colour_depth2);
308 			break;
309 		case DUALHEAD_CLONE:
310 			gx00_crtc_set_display_start(startadd,colour_depth1);
311 			g400_crtc2_set_display_start(startadd,colour_depth2);
312 			break;
313 		}
314 
315 		/* set the timing */
316 		gx00_crtc_set_timing(target);
317 		/* we do not need to setup CRTC2 here for a head that's in TVout mode */
318 		if (!(target2.flags & TV_BITS))	result = g400_crtc2_set_timing(target2);
319 
320 		/* TVout support: setup CRTC2 and it's pixelclock */
321 		if (si->ps.secondary_tvout && (target2.flags & TV_BITS)) maventv_init(target2);
322 	}
323 	else /* single head mode */
324 	{
325 		status_t status;
326 		int colour_mode = BPP32;
327 
328 		switch(target.space)
329 		{
330 		case B_CMAP8:        colour_depth1 =  8; colour_mode = BPP8;  break;
331 		case B_RGB15_LITTLE: colour_depth1 = 16; colour_mode = BPP15; break;
332 		case B_RGB16_LITTLE: colour_depth1 = 16; colour_mode = BPP16; break;
333 		case B_RGB32_LITTLE: colour_depth1 = 32; colour_mode = BPP32; break;
334 		default:
335 			LOG(8,("SETMODE: Invalid singlehead colour depth 0x%08x\n", target.space));
336 			return B_ERROR;
337 		}
338 
339 		/* set the pixel clock PLL */
340 		if (si->ps.card_type >= G100)
341 			status = gx00_dac_set_pix_pll(target);
342 		else
343 		{
344 			status = mil2_dac_set_pix_pll((target.timing.pixel_clock)/1000.0, colour_depth1);
345 		}
346 		if (status==B_ERROR)
347 			LOG(8,("CRTC: error setting pixel clock (internal DAC)\n"));
348 
349 		/* set the colour depth for CRTC1 and the DAC */
350 		gx00_dac_mode(colour_mode,1.0);
351 		gx00_crtc_depth(colour_mode);
352 
353 		/* set the display pitch */
354 		gx00_crtc_set_display_pitch();
355 
356 		/* tell the card what memory to display */
357 		gx00_crtc_set_display_start(startadd,colour_depth1);
358 
359 		/* enable primary analog output */
360 		switch (si->ps.card_type)
361 		{
362 		case G100:
363 		case G200:
364 		case G400:
365 		case G400MAX:
366 			gx00_general_dac_select(DS_CRTC1DAC_CRTC2MAVEN);
367 			break;
368 		case G450:
369 		case G550:
370 			gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2);
371 			gx50_general_output_select();
372 			break;
373 		default:
374 			break;
375 		}
376 
377 		/* set the timing */
378 		gx00_crtc_set_timing(target);
379 
380 		//fixme: shut-off the videoPLL if it exists...
381 	}
382 
383 	/* update driver's mode store */
384 	si->dm = target;
385 
386 	/* turn screen one on */
387 	gx00_crtc_dpms(display, h, v);
388 	/* turn screen two on if a dualhead mode is active */
389 	if (target.flags & DUALHEAD_BITS) g400_crtc2_dpms(display, h, v);
390 
391 	/* set up acceleration for this mode */
392 	gx00_acc_init();
393 
394 	/* clear line at bottom of screen if dualhead mode:
395 	 * MAVEN hardware design fault 'fix'. */
396 	if ((target.flags & DUALHEAD_BITS) && (si->ps.card_type <= G400MAX))
397 		gx00_maven_clrline();
398 
399 	LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0));
400 
401 	/* enable interrupts using the kernel driver */
402 	interrupt_enable(true);
403 
404 	/* optimize memory-access if needed */
405 	gx00_crtc_mem_priority(colour_depth1);
406 
407 	/* Tune RAM CAS-latency if needed. Must be done *here*! */
408 	mga_set_cas_latency();
409 
410 	return B_OK;
411 }
412 
413 /*
414 	Set which pixel of the virtual frame buffer will show up in the
415 	top left corner of the display device.  Used for page-flipping
416 	games and virtual desktops.
417 */
418 status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start) {
419 	uint8 colour_depth;
420 	uint32 startadd,startadd_right;
421 
422 	LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start));
423 
424 	/* G400 CRTC1 handles multiples of 8 for 8bit, 4 for 16bit, 2 for 32 bit
425 	   G400 CRTC2 handles multiples of 32 for 16-bit and 16 for 32-bit - must stoop to this in dualhead
426      */
427 
428 	/* reset lower bits, don't return an error! */
429 	if (si->dm.flags & DUALHEAD_BITS)
430 	{
431 		switch(si->dm.space)
432 		{
433 		case B_RGB16_LITTLE:
434 			colour_depth=16;
435 			h_display_start &= ~0x1f;
436 			break;
437 		case B_RGB32_LITTLE:
438 			colour_depth=32;
439 			h_display_start &= ~0x0f;
440 			break;
441 		default:
442 			LOG(8,("SET:Invalid DH colour depth 0x%08x, should never happen\n", si->dm.space));
443 			return B_ERROR;
444 		}
445 	}
446 	else
447 	{
448 		switch(si->dm.space)
449 		{
450 		case B_CMAP8:
451 			colour_depth=8;
452 			h_display_start &= ~0x07;
453 			break;
454 		case B_RGB15_LITTLE: case B_RGB16_LITTLE:
455 			colour_depth=16;
456 			h_display_start &= ~0x03;
457 			break;
458 		case B_RGB32_LITTLE:
459 			colour_depth=32;
460 			h_display_start &= ~0x01;
461 			break;
462 		default:
463 			return B_ERROR;
464 		}
465 	}
466 
467 	/* do not run past end of display */
468 	switch (si->dm.flags & DUALHEAD_BITS)
469 	{
470 	case DUALHEAD_ON:
471 	case DUALHEAD_SWITCH:
472 		if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width)
473 			return B_ERROR;
474 		break;
475 	default:
476 		if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width)
477 			return B_ERROR;
478 		break;
479 	}
480 	if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height)
481 		return B_ERROR;
482 
483 	/* everybody remember where we parked... */
484 	si->dm.h_display_start = h_display_start;
485 	si->dm.v_display_start = v_display_start;
486 
487 	/* actually set the registers */
488 	//fixme: seperate both heads: we need a secondary si->fbc!
489 	startadd = v_display_start * si->fbc.bytes_per_row;
490 	startadd += h_display_start * (colour_depth >> 3);
491 	startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
492 	startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3);
493 
494 	/* account for switched CRTC's */
495 	if (si->switched_crtcs)
496 	{
497 		uint32 temp = startadd;
498 		startadd = startadd_right;
499 		startadd_right = temp;
500 	}
501 
502 	interrupt_enable(false);
503 
504 	switch (si->dm.flags&DUALHEAD_BITS)
505 	{
506 		case DUALHEAD_ON:
507 		case DUALHEAD_SWITCH:
508 			gx00_crtc_set_display_start(startadd,colour_depth);
509 			g400_crtc2_set_display_start(startadd_right,colour_depth);
510 			break;
511 		case DUALHEAD_OFF:
512 			gx00_crtc_set_display_start(startadd,colour_depth);
513 			break;
514 		case DUALHEAD_CLONE:
515 			gx00_crtc_set_display_start(startadd,colour_depth);
516 			g400_crtc2_set_display_start(startadd,colour_depth);
517 			break;
518 	}
519 
520 	interrupt_enable(true);
521 	return B_OK;
522 }
523 
524 /*
525 	Set the indexed color palette.
526 */
527 void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) {
528 	int i;
529 	uint8 *r,*g,*b;
530 
531 	/* Protect gamma correction when not in CMAP8 */
532 	if (si->dm.space != B_CMAP8) return;
533 
534 	r=si->color_data;
535 	g=r+256;
536 	b=g+256;
537 
538 	i=first;
539 	while (count--)
540 	{
541 		r[i]=*color_data++;
542 		g[i]=*color_data++;
543 		b[i]=*color_data++;
544 		i++;
545 	}
546 	gx00_dac_palette(r,g,b);
547 }
548 
549 /* Put the display into one of the Display Power Management modes. */
550 status_t SET_DPMS_MODE(uint32 dpms_flags) {
551 	interrupt_enable(false);
552 
553 	LOG(4,("SET_DPMS_MODE: 0x%08x\n", dpms_flags));
554 
555 	if (si->dm.flags & DUALHEAD_BITS) /* dualhead */
556 	{
557 		switch(dpms_flags)
558 		{
559 		case B_DPMS_ON:	/* H: on, V: on */
560 			gx00_crtc_dpms(true, true, true);
561 			if (si->ps.secondary_head) g400_crtc2_dpms(true, true, true);
562 			break;
563 		case B_DPMS_STAND_BY:
564 			if (si->settings.greensync)
565 			{
566 				/* blank screen, but keep sync running */
567 				gx00_crtc_dpms(false, true, true);
568 			}
569 			else
570 			{
571 				gx00_crtc_dpms(false, false, true);
572 			}
573 			if (si->ps.secondary_head)
574 			{
575 				if ((si->dm.flags & TV_BITS) && (si->ps.card_type > G400MAX))
576 				{
577 					/* keep display enabled in TVout modes for G450 and G550! */
578 					g400_crtc2_dpms(true, false, true);
579 				}
580 				else
581 				{
582 					g400_crtc2_dpms(false, false, true);
583 				}
584 			}
585 			break;
586 		case B_DPMS_SUSPEND:
587 			if (si->settings.greensync)
588 			{
589 				/* blank screen, but keep sync running */
590 				gx00_crtc_dpms(false, true, true);
591 			}
592 			else
593 			{
594 				gx00_crtc_dpms(false, true, false);
595 			}
596 			if (si->ps.secondary_head)
597 			{
598 				if ((si->dm.flags & TV_BITS) && (si->ps.card_type > G400MAX))
599 				{
600 					/* keep display enabled in TVout modes for G450 and G550! */
601 					g400_crtc2_dpms(true, true, false);
602 				}
603 				else
604 				{
605 					g400_crtc2_dpms(false, true, false);
606 				}
607 			}
608 			break;
609 		case B_DPMS_OFF: /* H: off, V: off, display off */
610 			if (si->settings.greensync)
611 			{
612 				/* blank screen, but keep sync running */
613 				gx00_crtc_dpms(false, true, true);
614 			}
615 			else
616 			{
617 				gx00_crtc_dpms(false, false, false);
618 			}
619 			if (si->ps.secondary_head)
620 			{
621 				if ((si->dm.flags & TV_BITS) && (si->ps.card_type > G400MAX))
622 				{
623 					/* keep display enabled in TVout modes for G450 and G550! */
624 					g400_crtc2_dpms(true, false, false);
625 				}
626 				else
627 				{
628 					g400_crtc2_dpms(false, false, false);
629 				}
630 			}
631 			break;
632 		default:
633 			LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags));
634 			interrupt_enable(true);
635 			return B_ERROR;
636 		}
637 	}
638 	else /* singlehead */
639 	{
640 		switch(dpms_flags)
641 		{
642 		case B_DPMS_ON:	/* H: on, V: on */
643 			gx00_crtc_dpms(true, true, true);
644 			break;
645 		case B_DPMS_STAND_BY:
646 			if (si->settings.greensync)
647 			{
648 				/* blank screen, but keep sync running */
649 				gx00_crtc_dpms(false, true, true);
650 			}
651 			else
652 			{
653 				gx00_crtc_dpms(false, false, true);
654 			}
655 			break;
656 		case B_DPMS_SUSPEND:
657 			if (si->settings.greensync)
658 			{
659 				/* blank screen, but keep sync running */
660 				gx00_crtc_dpms(false, true, true);
661 			}
662 			else
663 			{
664 				gx00_crtc_dpms(false, true, false);
665 			}
666 			break;
667 		case B_DPMS_OFF: /* H: off, V: off, display off */
668 			if (si->settings.greensync)
669 			{
670 				/* blank screen, but keep sync running */
671 				gx00_crtc_dpms(false, true, true);
672 			}
673 			else
674 			{
675 				gx00_crtc_dpms(false, false, false);
676 			}
677 			break;
678 		default:
679 			LOG(8,("SET: Invalid DPMS settings (SH) 0x%08x\n", dpms_flags));
680 			interrupt_enable(true);
681 			return B_ERROR;
682 		}
683 	}
684 	interrupt_enable(true);
685 	return B_OK;
686 }
687 
688 /* Report device DPMS capabilities. */
689 uint32 DPMS_CAPABILITIES(void)
690 {
691 	if (si->settings.greensync)
692 		/* we can blank the screen on CRTC1, G400 CRTC2 does not support intermediate
693 		 * modes anyway. */
694 		//fixme: G450/G550 support full DPMS on CRTC2...
695 		return 	B_DPMS_ON | B_DPMS_OFF;
696 	else
697 		/* normally CRTC1 supports full DPMS (and on G450/G550 CRTC2 also).. */
698 		return 	B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF;
699 }
700 
701 /* Return the current DPMS mode. */
702 uint32 DPMS_MODE(void) {
703 	bool display, h, v;
704 
705 	interrupt_enable(false);
706 	gx00_crtc_dpms_fetch(&display, &h, &v);
707 	interrupt_enable(true);
708 
709 	if (display && h && v)
710 		return B_DPMS_ON;
711 	else if (si->settings.greensync)
712 		return B_DPMS_OFF;
713 	else if (v)
714 		return B_DPMS_STAND_BY;
715 	else if (h)
716 		return B_DPMS_SUSPEND;
717 	else
718 		return B_DPMS_OFF;
719 }
720