143809Sjkh
243809Sjkh/*
343809Sjkh	Copyright 1999, Be Incorporated.   All Rights Reserved.
487047Sru	This file may be used under the terms of the Be Sample Code License.
543809Sjkh
643809Sjkh	Other authors:
743809Sjkh	Mark Watson,
843809Sjkh	Apsed,
943809Sjkh	Rudolf Cornelissen 11/2002-12/2015
1059674Ssheldonh*/
1159674Ssheldonh
1259674Ssheldonh#define MODULE_BIT 0x00200000
1354949Ssheldonh
1443809Sjkh#include "acc_std.h"
1543809Sjkh
1650472Speter/* First validate the mode, then call lots of bit banging stuff to set the mode(s)! */
1743809Sjkhstatus_t SET_DISPLAY_MODE(display_mode *mode_to_set)
1843809Sjkh{
1948290Sjseger	/* BOUNDS WARNING:
2043809Sjkh	 * It's impossible to deviate whatever small amount in a display_mode if the lower
2143809Sjkh	 * and upper limits are the same!
2243809Sjkh	 * Besides:
2348785Siwasaki	 * BeOS (tested R5.0.3PE) is failing BWindowScreen::SetFrameBuffer() if PROPOSEMODE
2448785Siwasaki	 * returns B_BAD_VALUE!
2548785Siwasaki	 * Which means PROPOSEMODE should not return that on anything except on
2643809Sjkh	 * deviations for:
2743809Sjkh	 * display_mode.virtual_width;
2867793Ssanpei	 * display_mode.virtual_height;
2943809Sjkh	 * display_mode.timing.h_display;
3048554Shosokawa	 * display_mode.timing.v_display;
3158979Siwasaki	 * So:
3284537Ssheldonh	 * We don't use bounds here by making sure bounds and target are the same struct!
3375181Sbmah	 * (See the call to PROPOSE_DISPLAY_MODE below) */
3443809Sjkh	display_mode /*bounds,*/ target;
3579825Sroam
3643809Sjkh	uint8 colour_depth1 = 32;
3787047Sru	uint32 startadd,startadd_right;
3876946Sdd//	bool crt1, crt2, cross;
3988676Ssheldonh
4088676Ssheldonh	/* Adjust mode to valid one and fail if invalid */
4143809Sjkh	target /*= bounds*/ = *mode_to_set;
4243809Sjkh	/* show the mode bits */
4343809Sjkh	LOG(1, ("SETMODE: (ENTER) initial modeflags: $%08x\n", target.flags));
4443809Sjkh	LOG(1, ("SETMODE: requested target pixelclock %ukHz\n",  target.timing.pixel_clock));
4543809Sjkh	LOG(1, ("SETMODE: requested virtual_width %u, virtual_height %u\n",
4664749Sjhb										target.virtual_width, target.virtual_height));
4748880Sjkh
4843809Sjkh	/* See BOUNDS WARNING above... */
4948842Sjkh	if (PROPOSE_DISPLAY_MODE(&target, &target, &target) == B_ERROR)	return B_ERROR;
5048842Sjkh
5143809Sjkh	/* make sure a possible 3D add-on will block rendering and re-initialize itself.
5245542Sdes	 * note: update in _this_ order only */
5343809Sjkh	/* SET_DISPLAY_MODE will reset this flag when it's done. */
5443809Sjkh	si->engine.threeD.mode_changing = true;
5587047Sru	/* every 3D add-on will reset this bit-flag when it's done. */
5657014Spaul	si->engine.threeD.newmode = 0xffffffff;
5761961Sdillon	/* every 3D clone needs to reclaim a slot.
5861961Sdillon	 * note: this also cleans up reserved channels for killed 3D clones.. */
5961961Sdillon	si->engine.threeD.clones = 0x00000000;
6061961Sdillon
6144990Sbrian	/* disable interrupts using the kernel driver */
6287047Sru	head1_interrupt_enable(false);
6390957Scjc	if (si->ps.secondary_head) head2_interrupt_enable(false);
6487047Sru
6566745Sdarrenr	/* disable TVout if supported */
6686856Sdarrenr	if (si->ps.tvout) BT_stop_tvout();
6766745Sdarrenr
6866745Sdarrenr	/* turn off screen(s) _after_ TVout is disabled (if applicable) */
6986856Sdarrenr	head1_dpms(false, false, false, true);
7086856Sdarrenr	if (si->ps.secondary_head) head2_dpms(false, false, false, true);
7186856Sdarrenr	if (si->ps.tvout) BT_dpms(false);
7266745Sdarrenr
7366745Sdarrenr	/*where in framebuffer the screen is (should this be dependant on previous MOVEDISPLAY?)*/
7486856Sdarrenr	startadd = (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
7586856Sdarrenr
7686856Sdarrenr	/* calculate and set new mode bytes_per_row */
7787047Sru	nv_general_validate_pic_size (&target, &si->fbc.bytes_per_row, &si->acc_mode);
7885219Sdarrenr
7986856Sdarrenr	/*Perform the very long mode switch!*/
8085219Sdarrenr	if (target.flags & DUALHEAD_BITS) /*if some dualhead mode*/
8177154Sobrien	{
8289808Scjc		uint8 colour_depth2 = colour_depth1;
8349704Sobrien
8460977Swilko		/* init display mode for secondary head */
8560977Swilko		display_mode target2 = target;
8651209Sdes
8760685Swollman		LOG(1,("SETMODE: setting DUALHEAD mode\n"));
8849603Sdes
8949603Sdes		/* validate flags for secondary TVout */
9048687Speter		//fixme: remove or block on autodetect fail. (is now shutoff)
9183677Sbrooks		if ((0) && (target2.flags & TV_BITS))
9283677Sbrooks		{
9343809Sjkh			target.flags &= ~TV_BITS;//still needed for some routines...
9443809Sjkh			target2.flags &= ~TV_BITS;
9564677Ssheldonh			LOG(1,("SETMODE: blocking TVout: no TVout cable connected!\n"));
9643809Sjkh		}
9743809Sjkh
9843809Sjkh		/* detect which connectors have a CRT connected */
9943809Sjkh		//fixme: 'hot-plugging' for analog monitors removed: remove code as well;
10043809Sjkh		//or make it work with digital panels connected as well.
10143809Sjkh//		crt1 = nv_dac_crt_connected();
10277651Sbrian//		crt2 = nv_dac2_crt_connected();
10377651Sbrian		/* connect outputs 'straight-through' */
10477651Sbrian//		if (crt1)
10577651Sbrian//		{
10677651Sbrian			/* connector1 is used as primary output */
10743809Sjkh//			cross = false;
10853665Salfred//		}
10949110Sbrian//		else
11049110Sbrian//		{
11149110Sbrian//			if (crt2)
11250193Sbrian				/* connector2 is used as primary output */
11349110Sbrian//				cross = true;
11464471Sbrian//			else
11549110Sbrian				/* no CRT detected: assume connector1 is used as primary output */
11674462Salfred//				cross = false;
11743809Sjkh//		}
11878905Sdd		/* set output connectors assignment if possible */
11958400Sbillf		if ((target.flags & DUALHEAD_BITS) == DUALHEAD_SWITCH)
12082831Srwatson			/* invert output assignment in switch mode */
12163980Seivind			nv_general_head_select(true);
12278905Sdd		else
12348697Ssheldonh			nv_general_head_select(false);
12443809Sjkh
12543809Sjkh		/* set the pixel clock PLL(s) */
12643809Sjkh		LOG(8,("SETMODE: target clock %dkHz\n",target.timing.pixel_clock));
12743809Sjkh		if (head1_set_pix_pll(target) == B_ERROR)
12843809Sjkh			LOG(8,("SETMODE: error setting pixel clock (internal DAC)\n"));
12943809Sjkh
13082191Skuriyama		LOG(8,("SETMODE: target2 clock %dkHz\n",target2.timing.pixel_clock));
13143809Sjkh		if (head2_set_pix_pll(target2) == B_ERROR)
13280515Smarkm			LOG(8,("SETMODE: error setting pixel clock (DAC2)\n"));
13380515Smarkm
13480515Smarkm		/*set the colour depth for CRTC1 and the DAC */
13580515Smarkm		switch(target.space)
13680515Smarkm		{
13780515Smarkm		case B_CMAP8:
13880515Smarkm			colour_depth1 =  8;
13980515Smarkm			head1_mode(BPP8, 1.0);
14080515Smarkm			head1_depth(BPP8);
14180515Smarkm			break;
14280515Smarkm		case B_RGB15_LITTLE:
14343809Sjkh			colour_depth1 = 16;
14480515Smarkm			head1_mode(BPP15, 1.0);
14543809Sjkh			head1_depth(BPP15);
14643809Sjkh			break;
14774462Salfred		case B_RGB16_LITTLE:
14874462Salfred			colour_depth1 = 16;
14974462Salfred			head1_mode(BPP16, 1.0);
15074462Salfred			head1_depth(BPP16);
15174462Salfred			break;
15274462Salfred		case B_RGB32_LITTLE:
15374462Salfred			colour_depth1 = 32;
15474462Salfred			head1_mode(BPP32, 1.0);
15587047Sru			head1_depth(BPP32);
15674462Salfred			break;
15787047Sru		}
15874462Salfred		/*set the colour depth for CRTC2 and DAC2 */
15974462Salfred		switch(target2.space)
16043809Sjkh		{
16165306Sobrien		case B_CMAP8:
16243809Sjkh			colour_depth2 =  8;
16343809Sjkh			head2_mode(BPP8, 1.0);
16443809Sjkh			head2_depth(BPP8);
16543809Sjkh			break;
16643809Sjkh		case B_RGB15_LITTLE:
16787047Sru			colour_depth2 = 16;
16843809Sjkh			head2_mode(BPP15, 1.0);
16963773Sasmodai			head2_depth(BPP15);
17043809Sjkh			break;
17158710Sdillon		case B_RGB16_LITTLE:
17285114Salfred			colour_depth2 = 16;
17385114Salfred			head2_mode(BPP16, 1.0);
17463980Seivind			head2_depth(BPP16);
17574462Salfred			break;
17643809Sjkh		case B_RGB32_LITTLE:
17743809Sjkh			colour_depth2 = 32;
17843809Sjkh			head2_mode(BPP32, 1.0);
17943809Sjkh			head2_depth(BPP32);
18043809Sjkh			break;
18143809Sjkh		}
18243809Sjkh
18343809Sjkh		/* check if we are doing interlaced TVout mode */
18449393Seivind		//fixme: we don't support interlaced mode?
18543809Sjkh		si->interlaced_tv_mode = false;
18674949Sphk
18754683Sroberto		/*set the display(s) pitches*/
18854683Sroberto		head1_set_display_pitch ();
18954683Sroberto		//fixme: seperate for real dualhead modes:
19043809Sjkh		//we need a secondary si->fbc!
19174462Salfred		head2_set_display_pitch ();
19243809Sjkh
19343809Sjkh		/*work out where the "right" screen starts*/
19443809Sjkh		startadd_right = startadd + (target.timing.h_display * (colour_depth1 >> 3));
19543809Sjkh
19643809Sjkh		/* Tell card what memory to display */
19743809Sjkh		switch (target.flags & DUALHEAD_BITS)
19843809Sjkh		{
19943809Sjkh		case DUALHEAD_ON:
20043809Sjkh		case DUALHEAD_SWITCH:
20143809Sjkh			head1_set_display_start(startadd,colour_depth1);
20243809Sjkh			head2_set_display_start(startadd_right,colour_depth2);
20343809Sjkh			break;
20443809Sjkh		case DUALHEAD_CLONE:
20543809Sjkh			head1_set_display_start(startadd,colour_depth1);
20643809Sjkh			head2_set_display_start(startadd,colour_depth2);
20743809Sjkh			break;
20843809Sjkh		}
20943809Sjkh
21043809Sjkh		/* set the timing */
21143809Sjkh		head1_set_timing(target);
21243809Sjkh		head2_set_timing(target2);
21343809Sjkh
21443809Sjkh		/* TVout support: program TVout encoder and modify CRTC timing */
21587047Sru		if (si->ps.tvout && (target2.flags & TV_BITS)) BT_setmode(target2);
21643809Sjkh	}
21743809Sjkh	else /* single head mode */
21843809Sjkh	{
21943809Sjkh		int colour_mode = BPP32;
22043809Sjkh
22143809Sjkh		/* connect output */
22243809Sjkh		if (si->ps.secondary_head)
22343809Sjkh		{
22443809Sjkh			/* detect which connectors have a CRT connected */
22543809Sjkh			//fixme: 'hot-plugging' for analog monitors removed: remove code as well;
22643809Sjkh			//or make it work with digital panels connected as well.
22743809Sjkh//			crt1 = nv_dac_crt_connected();
22843809Sjkh//			crt2 = nv_dac2_crt_connected();
22943809Sjkh			/* connect outputs 'straight-through' */
23080209Shm//			if (crt1)
23143809Sjkh//			{
23280209Shm				/* connector1 is used as primary output */
23343809Sjkh//				cross = false;
23475920Sschweikh//			}
23576592Sschweikh//			else
23643809Sjkh//			{
23743809Sjkh//				if (crt2)
23843809Sjkh					/* connector2 is used as primary output */
23943809Sjkh//					cross = true;
24043809Sjkh//				else
24143809Sjkh					/* no CRT detected: assume connector1 is used as primary output */
24257398Sshin//					cross = false;
24357398Sshin//			}
24457398Sshin			/* set output connectors assignment if possible */
24567906Sume			nv_general_head_select(false);
24687464Snsayer		}
24757944Sshin
24857944Sshin		switch(target.space)
24957944Sshin		{
25057944Sshin		case B_CMAP8:        colour_depth1 =  8; colour_mode = BPP8;  break;
25157398Sshin		case B_RGB15_LITTLE: colour_depth1 = 16; colour_mode = BPP15; break;
25257398Sshin		case B_RGB16_LITTLE: colour_depth1 = 16; colour_mode = BPP16; break;
25357398Sshin		case B_RGB32_LITTLE: colour_depth1 = 32; colour_mode = BPP32; break;
25457398Sshin		default:
25557398Sshin			LOG(8,("SETMODE: Invalid singlehead colour depth 0x%08x\n", target.space));
25657398Sshin			return B_ERROR;
25767906Sume		}
25867906Sume
25957398Sshin		/* set the pixel clock PLL */
26057398Sshin		if (head1_set_pix_pll(target) == B_ERROR)
26157398Sshin			LOG(8,("CRTC: error setting pixel clock (internal DAC)\n"));
26274418Sume
26374418Sume		/* set the colour depth for CRTC1 and the DAC */
26478935Sume		/* first set the colordepth */
26557398Sshin		head1_depth(colour_mode);
26657398Sshin		/* then(!) program the PAL (<8bit colordepth does not support 8bit PAL) */
26778493Sume		head1_mode(colour_mode,1.0);
26857944Sshin
26957944Sshin		/* set the display pitch */
27057944Sshin		head1_set_display_pitch();
27171632Sume
27257398Sshin		/* tell the card what memory to display */
27384421Sume		head1_set_display_start(startadd,colour_depth1);
27484421Sume
27584421Sume		/* set the timing */
27684421Sume		head1_set_timing(target);
27757398Sshin
27857944Sshin		/* TVout support: program TVout encoder and modify CRTC timing */
27957944Sshin		if (si->ps.tvout && (target.flags & TV_BITS)) BT_setmode(target);
28057944Sshin
28157944Sshin		//fixme: shut-off the videoPLL if it exists...
28257944Sshin	}
28357944Sshin
28457944Sshin	/* update driver's mode store */
28557944Sshin	si->dm = target;
28657944Sshin
28778475Sume	/* update FIFO data fetching according to mode */
28878475Sume	nv_crtc_update_fifo();
28978475Sume	if (si->ps.secondary_head) nv_crtc2_update_fifo();
29067906Sume
29158752Sshin	/* set up acceleration for this mode */
29267906Sume	/* note:
29367906Sume	 * Maybe later we can forget about non-DMA mode (depends on 3D acceleration
29467906Sume	 * attempts). */
29567906Sume	if (!si->settings.block_acc) {
29667906Sume		if (!si->settings.dma_acc)
29767906Sume			nv_acc_init();
29867906Sume		else
29943809Sjkh			nv_acc_init_dma();
30043809Sjkh	}
30143809Sjkh
30243809Sjkh	/* set up overlay unit for this mode */
30343809Sjkh	nv_bes_init();
30443809Sjkh
30543809Sjkh	/* note freemem range */
30643809Sjkh	/* first free adress follows hardcursor and workspace */
30743809Sjkh	si->engine.threeD.mem_low = si->fbc.bytes_per_row * si->dm.virtual_height;
30843809Sjkh	if (si->settings.hardcursor) si->engine.threeD.mem_low += 2048;
30943809Sjkh	/* last free adress is end-of-ram minus max space needed for overlay bitmaps */
31043809Sjkh	//fixme possible:
31143809Sjkh	//if overlay buffers are allocated subtract buffersize from mem_high;
31243809Sjkh	//only allocate overlay buffers if 3D is not in use. (block overlay during 3D)
31343809Sjkh	si->engine.threeD.mem_high = si->ps.memory_size - 1;
31443809Sjkh	/* Keep some extra distance as a workaround for certain bugs (see
31543809Sjkh	 * DriverInterface.h for an explanation). */
31643809Sjkh	if (si->ps.card_arch < NV40A)
31787047Sru		si->engine.threeD.mem_high -= PRE_NV40_OFFSET;
31843809Sjkh	else
31987047Sru		si->engine.threeD.mem_high -= NV40_PLUS_OFFSET;
32075708Sache
32175708Sache	si->engine.threeD.mem_high -= (MAXBUFFERS * 1024 * 1024 * 2); /* see overlay.c file */
32243809Sjkh
32376110Sdd	/* restore screen(s) output state(s) */
32443809Sjkh	SET_DPMS_MODE(si->dpms_flags);
32543809Sjkh
32643809Sjkh	/* enable interrupts using the kernel driver */
32793853Sgshapiro	//fixme:
32843809Sjkh	//add head2 once we use one driver instance 'per head' (instead of 'per card')
32943809Sjkh	head1_interrupt_enable(true);
33093853Sgshapiro
33193853Sgshapiro	/* make sure a possible 3D add-on will re-initialize itself by signalling ready */
33293853Sgshapiro	si->engine.threeD.mode_changing = false;
33393314Sgshapiro
33493314Sgshapiro	/* optimize memory-access if needed */
33590808Sgshapiro//	head1_mem_priority(colour_depth1);
33693314Sgshapiro
33793314Sgshapiro	/* Tune RAM CAS-latency if needed. Must be done *here*! */
33893314Sgshapiro	nv_set_cas_latency();
33974198Speter
34090808Sgshapiro	LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0));
34190808Sgshapiro
34290808Sgshapiro	return B_OK;
34393314Sgshapiro}
34493853Sgshapiro
34593853Sgshapiro/*
34693853Sgshapiro	Set which pixel of the virtual frame buffer will show up in the
34793853Sgshapiro	top left corner of the display device.  Used for page-flipping
34893853Sgshapiro	games and virtual desktops.
34993853Sgshapiro*/
35093853Sgshapirostatus_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start) {
35193853Sgshapiro	uint8 colour_depth;
35293853Sgshapiro	uint32 startadd,startadd_right;
35393853Sgshapiro
35493853Sgshapiro	LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start));
35593853Sgshapiro
35693853Sgshapiro	/* nVidia cards support pixelprecise panning on both heads in all modes:
35793853Sgshapiro	 * No stepping granularity needed! */
35851827Sbillf
35984730Sdes	/* determine bits used for the colordepth */
36087047Sru	switch(si->dm.space)
36187047Sru	{
36251038Scpiazza	case B_CMAP8:
36343809Sjkh		colour_depth=8;
36443809Sjkh		break;
36573242Sjkh	case B_RGB15_LITTLE:
36671121Sdes	case B_RGB16_LITTLE:
36751290Sobrien		colour_depth=16;
36843809Sjkh		break;
36954642Sgallatin	case B_RGB24_LITTLE:
37043809Sjkh		colour_depth=24;
37164520Sjdp		break;
37243809Sjkh	case B_RGB32_LITTLE:
37343809Sjkh		colour_depth=32;
37443809Sjkh		break;
37543809Sjkh	default:
37687047Sru		return B_ERROR;
37743809Sjkh	}
37892192Srwatson
37943809Sjkh	/* do not run past end of display */
38087047Sru	switch (si->dm.flags & DUALHEAD_BITS)
38166634Sbrian	{
38287047Sru	case DUALHEAD_ON:
38367180Sjwd	case DUALHEAD_SWITCH:
38471014Sdougb		if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width)
38587047Sru			return B_ERROR;
38687047Sru		break;
38787047Sru	default:
38887047Sru		if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width)
38987047Sru			return B_ERROR;
39043809Sjkh		break;
39143809Sjkh	}
39259674Ssheldonh	if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height)
39359674Ssheldonh		return B_ERROR;
39443809Sjkh
39543809Sjkh	/* everybody remember where we parked... */
39659674Ssheldonh	si->dm.h_display_start = h_display_start;
39787047Sru	si->dm.v_display_start = v_display_start;
39887047Sru
39987047Sru	/* actually set the registers */
40087047Sru	//fixme: seperate both heads: we need a secondary si->fbc!
40187047Sru	startadd = v_display_start * si->fbc.bytes_per_row;
40287047Sru	startadd += h_display_start * (colour_depth >> 3);
40387047Sru	startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
40487047Sru	startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3);
40587047Sru
40687047Sru	/* disable interrupts using the kernel driver */
40787047Sru	head1_interrupt_enable(false);
40887047Sru	if (si->ps.secondary_head) head2_interrupt_enable(false);
40987047Sru
41087047Sru	switch (si->dm.flags & DUALHEAD_BITS)
41187047Sru	{
41287047Sru		case DUALHEAD_ON:
41359674Ssheldonh		case DUALHEAD_SWITCH:
414			head1_set_display_start(startadd,colour_depth);
415			head2_set_display_start(startadd_right,colour_depth);
416			break;
417		case DUALHEAD_OFF:
418			head1_set_display_start(startadd,colour_depth);
419			break;
420		case DUALHEAD_CLONE:
421			head1_set_display_start(startadd,colour_depth);
422			head2_set_display_start(startadd,colour_depth);
423			break;
424	}
425
426	//fixme:
427	//add head2 once we use one driver instance 'per head' (instead of 'per card')
428	head1_interrupt_enable(true);
429
430	return B_OK;
431}
432
433/* Set the indexed color palette */
434void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) {
435	int i;
436	uint8 *r,*g,*b;
437
438	/* Protect gamma correction when not in CMAP8 */
439	if (si->dm.space != B_CMAP8) return;
440
441	r=si->color_data;
442	g=r+256;
443	b=g+256;
444
445	i=first;
446	while (count--)
447	{
448		r[i]=*color_data++;
449		g[i]=*color_data++;
450		b[i]=*color_data++;
451		i++;
452	}
453	head1_palette(r,g,b);
454	if (si->dm.flags & DUALHEAD_BITS) head2_palette(r,g,b);
455}
456
457/* Put the display into one of the Display Power Management modes. */
458status_t SET_DPMS_MODE(uint32 dpms_flags)
459{
460	bool display, h1h, h1v, h2h, h2v, do_p1, do_p2;
461
462	/* disable interrupts using the kernel driver */
463	head1_interrupt_enable(false);
464	if (si->ps.secondary_head) head2_interrupt_enable(false);
465
466	LOG(4,("SET_DPMS_MODE: $%08x\n", dpms_flags));
467
468	/* note current DPMS state for our reference */
469	si->dpms_flags = dpms_flags;
470
471	/* preset: DPMS for panels should be executed */
472	do_p1 = do_p2 = true;
473
474	/* determine signals to send to head(s) */
475	display = h1h = h1v = h2h = h2v = true;
476	switch(dpms_flags)
477	{
478	case B_DPMS_ON:	/* H: on, V: on, display on */
479		break;
480	case B_DPMS_STAND_BY:
481		display = h1h = h2h = false;
482		break;
483	case B_DPMS_SUSPEND:
484		display = h1v = h2v = false;
485		break;
486	case B_DPMS_OFF: /* H: off, V: off, display off */
487		display = h1h = h1v = h2h = h2v = false;
488		break;
489	default:
490		LOG(8,("SET: Invalid DPMS settings $%08x\n", dpms_flags));
491		//fixme:
492		//add head2 once we use one driver instance 'per head' (instead of 'per card')
493		head1_interrupt_enable(true);
494
495		return B_ERROR;
496	}
497
498	/* CRTC used for TVout needs specific DPMS programming */
499	if (si->dm.flags & TV_BITS)
500	{
501		/* TV_PRIMARY tells us that the head to be used with TVout is the head that's
502		 * actually assigned as being the primary head at powerup:
503		 * so non dualhead-mode-dependant, and not 'fixed' CRTC1! */
504		if (si->dm.flags & TV_PRIMARY)
505		{
506			LOG(4,("SET_DPMS_MODE: tuning primary head DPMS settings for TVout compatibility\n"));
507
508			if ((si->dm.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH)
509			{
510				if (!(si->settings.vga_on_tv))
511				{
512					/* block VGA output on head displaying on TV */
513					/* Note:
514					 * this specific sync setting is required: Vsync is used to keep TVout
515					 * synchronized to the CRTC 'vertically' (otherwise 'rolling' occurs).
516					 * This leaves Hsync only for shutting off the VGA screen. */
517					h1h = false;
518					h1v = true;
519					/* block panel DPMS updates */
520					do_p1 = false;
521				}
522				else
523				{
524					/* when concurrent VGA is used alongside TVout on a head, DPMS is safest
525					 * applied this way: Vsync is needed for stopping TVout successfully when
526					 * a (new) modeswitch occurs.
527					 * (see routine BT_stop_tvout() in nv_brooktreetv.c) */
528					/* Note:
529					 * applying 'normal' DPMS here and forcing Vsync on in the above mentioned
530					 * routine seems to not always be enough: sometimes image generation will
531					 * not resume in that case. */
532					h1h = display;
533					h1v = true;
534				}
535			}
536			else
537			{
538				if (!(si->settings.vga_on_tv))
539				{
540					h2h = false;
541					h2v = true;
542					do_p2 = false;
543				}
544				else
545				{
546					h2h = display;
547					h2v = true;
548				}
549			}
550		}
551		else
552		{
553			LOG(4,("SET_DPMS_MODE: tuning secondary head DPMS settings for TVout compatibility\n"));
554
555			if ((si->dm.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH)
556			{
557				if (!(si->settings.vga_on_tv))
558				{
559					h2h = false;
560					h2v = true;
561					do_p2 = false;
562				}
563				else
564				{
565					h2h = display;
566					h2v = true;
567				}
568			}
569			else
570			{
571				if (!(si->settings.vga_on_tv))
572				{
573					h1h = false;
574					h1v = true;
575					do_p1 = false;
576				}
577				else
578				{
579					h1h = display;
580					h1v = true;
581				}
582			}
583		}
584	}
585
586	/* issue actual DPMS commands as far as applicable */
587	head1_dpms(display, h1h, h1v, do_p1);
588	if ((si->ps.secondary_head) && (si->dm.flags & DUALHEAD_BITS))
589		head2_dpms(display, h2h, h2v, do_p2);
590	if (si->dm.flags & TV_BITS)
591		BT_dpms(display);
592
593	//fixme:
594	//add head2 once we use one driver instance 'per head' (instead of 'per card')
595	head1_interrupt_enable(true);
596
597	return B_OK;
598}
599
600/* Report device DPMS capabilities */
601uint32 DPMS_CAPABILITIES(void)
602{
603	return 	(B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF);
604}
605
606/* Return the current DPMS mode */
607uint32 DPMS_MODE(void)
608{
609	return si->dpms_flags;
610}
611