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