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