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-6/2008
10*/
11
12#define MODULE_BIT 0x00200000
13
14#include "acc_std.h"
15
16/* First validate the mode, then call lots of bit banging stuff to set the mode(s)! */
17status_t SET_DISPLAY_MODE(display_mode *mode_to_set)
18{
19	/* BOUNDS WARNING:
20	 * It's impossible to deviate whatever small amount in a display_mode if the lower
21	 * and upper limits are the same!
22	 * Besides:
23	 * BeOS (tested R5.0.3PE) is failing BWindowScreen::SetFrameBuffer() if PROPOSEMODE
24	 * returns B_BAD_VALUE!
25	 * Which means PROPOSEMODE should not return that on anything except on
26	 * deviations for:
27	 * display_mode.virtual_width;
28	 * display_mode.virtual_height;
29	 * display_mode.timing.h_display;
30	 * display_mode.timing.v_display;
31	 * So:
32	 * We don't use bounds here by making sure bounds and target are the same struct!
33	 * (See the call to PROPOSE_DISPLAY_MODE below) */
34	display_mode /*bounds,*/ target;
35
36	uint8 colour_depth1 = 32;
37	uint32 startadd,startadd_right;
38//	bool crt1, crt2, cross;
39
40	/* Adjust mode to valid one and fail if invalid */
41	target /*= bounds*/ = *mode_to_set;
42	/* show the mode bits */
43	LOG(1, ("SETMODE: (ENTER) initial modeflags: $%08x\n", target.flags));
44	LOG(1, ("SETMODE: requested target pixelclock %dkHz\n",  target.timing.pixel_clock));
45	LOG(1, ("SETMODE: requested virtual_width %d, virtual_height %d\n",
46										target.virtual_width, target.virtual_height));
47
48	/* See BOUNDS WARNING above... */
49	if (PROPOSE_DISPLAY_MODE(&target, &target, &target) == B_ERROR)	return B_ERROR;
50
51	/* make sure a possible 3D add-on will block rendering and re-initialize itself.
52	 * note: update in _this_ order only */
53	/* SET_DISPLAY_MODE will reset this flag when it's done. */
54	si->engine.threeD.mode_changing = true;
55	/* every 3D add-on will reset this bit-flag when it's done. */
56	si->engine.threeD.newmode = 0xffffffff;
57	/* every 3D clone needs to reclaim a slot.
58	 * note: this also cleans up reserved channels for killed 3D clones.. */
59	si->engine.threeD.clones = 0x00000000;
60
61	/* disable interrupts using the kernel driver */
62//	head1_interrupt_enable(false);
63//	if (si->ps.secondary_head) head2_interrupt_enable(false);
64
65	/* turn off screen(s) */
66//	head1_dpms(false, false, false, true);
67//	if (si->ps.secondary_head) head2_dpms(false, false, false, true);
68
69	/*where in framebuffer the screen is (should this be dependant on previous MOVEDISPLAY?)*/
70	startadd = (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
71
72	/* calculate and set new mode bytes_per_row */
73	nv_general_validate_pic_size (&target, &si->fbc.bytes_per_row, &si->acc_mode);
74
75	/*Perform the very long mode switch!*/
76	if (target.flags & DUALHEAD_BITS) /*if some dualhead mode*/
77	{
78		uint8 colour_depth2 = colour_depth1;
79
80		/* init display mode for secondary head */
81		display_mode target2 = target;
82
83		LOG(1,("SETMODE: setting DUALHEAD mode\n"));
84
85		/* detect which connectors have a CRT connected */
86		//fixme: 'hot-plugging' for analog monitors removed: remove code as well;
87		//or make it work with digital panels connected as well.
88//		crt1 = nv_dac_crt_connected();
89//		crt2 = nv_dac2_crt_connected();
90		/* connect outputs 'straight-through' */
91//		if (crt1)
92//		{
93			/* connector1 is used as primary output */
94//			cross = false;
95//		}
96//		else
97//		{
98//			if (crt2)
99				/* connector2 is used as primary output */
100//				cross = true;
101//			else
102				/* no CRT detected: assume connector1 is used as primary output */
103//				cross = false;
104//		}
105		/* set output connectors assignment if possible */
106//		if ((target.flags & DUALHEAD_BITS) == DUALHEAD_SWITCH)
107			/* invert output assignment in switch mode */
108//			nv_general_head_select(true);
109//		else
110//			nv_general_head_select(false);
111
112		/* set the pixel clock PLL(s) */
113		LOG(8,("SETMODE: target clock %dkHz\n",target.timing.pixel_clock));
114//		if (head1_set_pix_pll(target) == B_ERROR)
115//			LOG(8,("SETMODE: error setting pixel clock (internal DAC)\n"));
116
117		LOG(8,("SETMODE: target2 clock %dkHz\n",target2.timing.pixel_clock));
118//		if (head2_set_pix_pll(target2) == B_ERROR)
119//			LOG(8,("SETMODE: error setting pixel clock (DAC2)\n"));
120
121		/*set the colour depth for CRTC1 and the DAC */
122		switch(target.space)
123		{
124		case B_CMAP8:
125			colour_depth1 =  8;
126//			head1_mode(BPP8, 1.0);
127//			head1_depth(BPP8);
128			break;
129		case B_RGB15_LITTLE:
130			colour_depth1 = 16;
131//			head1_mode(BPP15, 1.0);
132//			head1_depth(BPP15);
133			break;
134		case B_RGB16_LITTLE:
135			colour_depth1 = 16;
136//			head1_mode(BPP16, 1.0);
137//			head1_depth(BPP16);
138			break;
139		case B_RGB32_LITTLE:
140			colour_depth1 = 32;
141//			head1_mode(BPP32, 1.0);
142//			head1_depth(BPP32);
143			break;
144		}
145		/*set the colour depth for CRTC2 and DAC2 */
146		switch(target2.space)
147		{
148		case B_CMAP8:
149			colour_depth2 =  8;
150//			head2_mode(BPP8, 1.0);
151//			head2_depth(BPP8);
152			break;
153		case B_RGB15_LITTLE:
154			colour_depth2 = 16;
155//			head2_mode(BPP15, 1.0);
156//			head2_depth(BPP15);
157			break;
158		case B_RGB16_LITTLE:
159			colour_depth2 = 16;
160//			head2_mode(BPP16, 1.0);
161//			head2_depth(BPP16);
162			break;
163		case B_RGB32_LITTLE:
164			colour_depth2 = 32;
165//			head2_mode(BPP32, 1.0);
166//			head2_depth(BPP32);
167			break;
168		}
169
170		/*set the display(s) pitches*/
171//		head1_set_display_pitch ();
172		//fixme: seperate for real dualhead modes:
173		//we need a secondary si->fbc!
174//		head2_set_display_pitch ();
175
176		/*work out where the "right" screen starts*/
177		startadd_right = startadd + (target.timing.h_display * (colour_depth1 >> 3));
178
179		/* Tell card what memory to display */
180		switch (target.flags & DUALHEAD_BITS)
181		{
182		case DUALHEAD_ON:
183		case DUALHEAD_SWITCH:
184//			head1_set_display_start(startadd,colour_depth1);
185//			head2_set_display_start(startadd_right,colour_depth2);
186			break;
187		case DUALHEAD_CLONE:
188//			head1_set_display_start(startadd,colour_depth1);
189//			head2_set_display_start(startadd,colour_depth2);
190			break;
191		}
192
193		/* set the timing */
194//		head1_set_timing(target);
195//		head2_set_timing(target2);
196	}
197	else /* single head mode */
198	{
199		int colour_mode = BPP32;
200
201		/* connect output */
202		if (si->ps.secondary_head)
203		{
204			/* detect which connectors have a CRT connected */
205			//fixme: 'hot-plugging' for analog monitors removed: remove code as well;
206			//or make it work with digital panels connected as well.
207//			crt1 = nv_dac_crt_connected();
208//			crt2 = nv_dac2_crt_connected();
209			/* connect outputs 'straight-through' */
210//			if (crt1)
211//			{
212				/* connector1 is used as primary output */
213//				cross = false;
214//			}
215//			else
216//			{
217//				if (crt2)
218					/* connector2 is used as primary output */
219//					cross = true;
220//				else
221					/* no CRT detected: assume connector1 is used as primary output */
222//					cross = false;
223//			}
224			/* set output connectors assignment if possible */
225			nv_general_head_select(false);
226		}
227
228		switch(target.space)
229		{
230		case B_CMAP8:        colour_depth1 =  8; colour_mode = BPP8;  break;
231		case B_RGB15_LITTLE: colour_depth1 = 16; colour_mode = BPP15; break;
232		case B_RGB16_LITTLE: colour_depth1 = 16; colour_mode = BPP16; break;
233		case B_RGB32_LITTLE: colour_depth1 = 32; colour_mode = BPP32; break;
234		default:
235			LOG(8,("SETMODE: Invalid singlehead colour depth 0x%08x\n", target.space));
236			return B_ERROR;
237		}
238
239		/* set the pixel clock PLL */
240//		if (head1_set_pix_pll(target) == B_ERROR)
241//			LOG(8,("CRTC: error setting pixel clock (internal DAC)\n"));
242
243		/* set the colour depth for CRTC1 and the DAC */
244		/* first set the colordepth */
245//		head1_depth(colour_mode);
246		/* then(!) program the PAL (<8bit colordepth does not support 8bit PAL) */
247//		head1_mode(colour_mode,1.0);
248
249		/* set the display pitch */
250//		head1_set_display_pitch();
251
252		/* tell the card what memory to display */
253//		head1_set_display_start(startadd,colour_depth1);
254
255		/* set the timing */
256//		head1_set_timing(target);
257
258		//fixme: shut-off the videoPLL if it exists...
259	}
260
261	/* update driver's mode store */
262	si->dm = target;
263
264	/* update FIFO data fetching according to mode */
265//	nv_crtc_update_fifo();
266//	if (si->ps.secondary_head) nv_crtc2_update_fifo();
267
268	/* set up acceleration for this mode */
269	/* note:
270	 * Maybe later we can forget about non-DMA mode (depends on 3D acceleration
271	 * attempts). */
272//no acc support for G8x yet!
273if (si->ps.card_arch < NV50A)
274{
275	nv_acc_init_dma();
276}
277	/* set up overlay unit for this mode */
278//	nv_bes_init();
279
280	/* note freemem range */
281	/* first free adress follows hardcursor and workspace */
282	si->engine.threeD.mem_low = si->fbc.bytes_per_row * si->dm.virtual_height;
283	if (si->settings.hardcursor) si->engine.threeD.mem_low += 2048;
284	/* last free adress is end-of-ram minus max space needed for overlay bitmaps */
285	//fixme possible:
286	//if overlay buffers are allocated subtract buffersize from mem_high;
287	//only allocate overlay buffers if 3D is not in use. (block overlay during 3D)
288	si->engine.threeD.mem_high = si->ps.memory_size - 1;
289	/* Keep some extra distance as a workaround for certain bugs (see
290	 * DriverInterface.h for an explanation). */
291	si->engine.threeD.mem_high -= NV40_PLUS_OFFSET;
292
293	/* restore screen(s) output state(s) */
294//	SET_DPMS_MODE(si->dpms_flags);
295
296	/* enable interrupts using the kernel driver */
297	//fixme:
298	//add head2 once we use one driver instance 'per head' (instead of 'per card')
299//	head1_interrupt_enable(true);
300
301	/* make sure a possible 3D add-on will re-initialize itself by signalling ready */
302	si->engine.threeD.mode_changing = false;
303
304	/* optimize memory-access if needed */
305//	head1_mem_priority(colour_depth1);
306
307	/* Tune RAM CAS-latency if needed. Must be done *here*! */
308//	nv_set_cas_latency();
309
310	LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0));
311
312	return B_OK;
313}
314
315/*
316	Set which pixel of the virtual frame buffer will show up in the
317	top left corner of the display device.  Used for page-flipping
318	games and virtual desktops.
319*/
320status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start) {
321	uint8 colour_depth;
322	uint32 startadd,startadd_right;
323
324	LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start));
325
326	/* nVidia cards support pixelprecise panning on both heads in all modes:
327	 * No stepping granularity needed! */
328
329	/* determine bits used for the colordepth */
330	switch(si->dm.space)
331	{
332	case B_CMAP8:
333		colour_depth=8;
334		break;
335	case B_RGB15_LITTLE:
336	case B_RGB16_LITTLE:
337		colour_depth=16;
338		break;
339	case B_RGB24_LITTLE:
340		colour_depth=24;
341		break;
342	case B_RGB32_LITTLE:
343		colour_depth=32;
344		break;
345	default:
346		return B_ERROR;
347	}
348
349	/* do not run past end of display */
350	switch (si->dm.flags & DUALHEAD_BITS)
351	{
352	case DUALHEAD_ON:
353	case DUALHEAD_SWITCH:
354		if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width)
355			return B_ERROR;
356		break;
357	default:
358		if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width)
359			return B_ERROR;
360		break;
361	}
362	if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height)
363		return B_ERROR;
364
365	/* everybody remember where we parked... */
366	si->dm.h_display_start = h_display_start;
367	si->dm.v_display_start = v_display_start;
368
369	/* actually set the registers */
370	//fixme: seperate both heads: we need a secondary si->fbc!
371	startadd = v_display_start * si->fbc.bytes_per_row;
372	startadd += h_display_start * (colour_depth >> 3);
373	startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
374	startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3);
375
376	/* disable interrupts using the kernel driver */
377	head1_interrupt_enable(false);
378	if (si->ps.secondary_head) head2_interrupt_enable(false);
379
380	switch (si->dm.flags & DUALHEAD_BITS)
381	{
382		case DUALHEAD_ON:
383		case DUALHEAD_SWITCH:
384			head1_set_display_start(startadd,colour_depth);
385			head2_set_display_start(startadd_right,colour_depth);
386			break;
387		case DUALHEAD_OFF:
388			head1_set_display_start(startadd,colour_depth);
389			break;
390		case DUALHEAD_CLONE:
391			head1_set_display_start(startadd,colour_depth);
392			head2_set_display_start(startadd,colour_depth);
393			break;
394	}
395
396	//fixme:
397	//add head2 once we use one driver instance 'per head' (instead of 'per card')
398	head1_interrupt_enable(true);
399
400	return B_OK;
401}
402
403/* Set the indexed color palette */
404void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) {
405	int i;
406	uint8 *r,*g,*b;
407
408	/* Protect gamma correction when not in CMAP8 */
409	if (si->dm.space != B_CMAP8) return;
410
411	r=si->color_data;
412	g=r+256;
413	b=g+256;
414
415	i=first;
416	while (count--)
417	{
418		r[i]=*color_data++;
419		g[i]=*color_data++;
420		b[i]=*color_data++;
421		i++;
422	}
423	head1_palette(r,g,b);
424	if (si->dm.flags & DUALHEAD_BITS) head2_palette(r,g,b);
425}
426
427/* Put the display into one of the Display Power Management modes. */
428status_t SET_DPMS_MODE(uint32 dpms_flags)
429{
430	bool display, h1h, h1v, h2h, h2v, do_p1, do_p2;
431
432	/* disable interrupts using the kernel driver */
433	head1_interrupt_enable(false);
434	if (si->ps.secondary_head) head2_interrupt_enable(false);
435
436	LOG(4,("SET_DPMS_MODE: $%08x\n", dpms_flags));
437
438	/* note current DPMS state for our reference */
439	si->dpms_flags = dpms_flags;
440
441	/* preset: DPMS for panels should be executed */
442	do_p1 = do_p2 = true;
443
444	/* determine signals to send to head(s) */
445	display = h1h = h1v = h2h = h2v = true;
446	switch(dpms_flags)
447	{
448	case B_DPMS_ON:	/* H: on, V: on, display on */
449		break;
450	case B_DPMS_STAND_BY:
451		display = h1h = h2h = false;
452		break;
453	case B_DPMS_SUSPEND:
454		display = h1v = h2v = false;
455		break;
456	case B_DPMS_OFF: /* H: off, V: off, display off */
457		display = h1h = h1v = h2h = h2v = false;
458		break;
459	default:
460		LOG(8,("SET: Invalid DPMS settings $%08x\n", dpms_flags));
461		//fixme:
462		//add head2 once we use one driver instance 'per head' (instead of 'per card')
463		head1_interrupt_enable(true);
464
465		return B_ERROR;
466	}
467
468	/* issue actual DPMS commands as far as applicable */
469	head1_dpms(display, h1h, h1v, do_p1);
470	if ((si->ps.secondary_head) && (si->dm.flags & DUALHEAD_BITS))
471		head2_dpms(display, h2h, h2v, do_p2);
472
473	//fixme:
474	//add head2 once we use one driver instance 'per head' (instead of 'per card')
475	head1_interrupt_enable(true);
476
477	return B_OK;
478}
479
480/* Report device DPMS capabilities */
481uint32 DPMS_CAPABILITIES(void)
482{
483	return 	(B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF);
484}
485
486/* Return the current DPMS mode */
487uint32 DPMS_MODE(void)
488{
489	return si->dpms_flags;
490}
491