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-12/2015
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 %ukHz\n",  target.timing.pixel_clock));
45	LOG(1, ("SETMODE: requested virtual_width %u, virtual_height %u\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	/* disable TVout if supported */
66	if (si->ps.tvout) BT_stop_tvout();
67
68	/* turn off screen(s) _after_ TVout is disabled (if applicable) */
69	head1_dpms(false, false, false, true);
70	if (si->ps.secondary_head) head2_dpms(false, false, false, true);
71	if (si->ps.tvout) BT_dpms(false);
72
73	/*where in framebuffer the screen is (should this be dependant on previous MOVEDISPLAY?)*/
74	startadd = (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
75
76	/* calculate and set new mode bytes_per_row */
77	nv_general_validate_pic_size (&target, &si->fbc.bytes_per_row, &si->acc_mode);
78
79	/*Perform the very long mode switch!*/
80	if (target.flags & DUALHEAD_BITS) /*if some dualhead mode*/
81	{
82		uint8 colour_depth2 = colour_depth1;
83
84		/* init display mode for secondary head */
85		display_mode target2 = target;
86
87		LOG(1,("SETMODE: setting DUALHEAD mode\n"));
88
89		/* validate flags for secondary TVout */
90		//fixme: remove or block on autodetect fail. (is now shutoff)
91		if ((0) && (target2.flags & TV_BITS))
92		{
93			target.flags &= ~TV_BITS;//still needed for some routines...
94			target2.flags &= ~TV_BITS;
95			LOG(1,("SETMODE: blocking TVout: no TVout cable connected!\n"));
96		}
97
98		/* detect which connectors have a CRT connected */
99		//fixme: 'hot-plugging' for analog monitors removed: remove code as well;
100		//or make it work with digital panels connected as well.
101//		crt1 = nv_dac_crt_connected();
102//		crt2 = nv_dac2_crt_connected();
103		/* connect outputs 'straight-through' */
104//		if (crt1)
105//		{
106			/* connector1 is used as primary output */
107//			cross = false;
108//		}
109//		else
110//		{
111//			if (crt2)
112				/* connector2 is used as primary output */
113//				cross = true;
114//			else
115				/* no CRT detected: assume connector1 is used as primary output */
116//				cross = false;
117//		}
118		/* set output connectors assignment if possible */
119		if ((target.flags & DUALHEAD_BITS) == DUALHEAD_SWITCH)
120			/* invert output assignment in switch mode */
121			nv_general_head_select(true);
122		else
123			nv_general_head_select(false);
124
125		/* set the pixel clock PLL(s) */
126		LOG(8,("SETMODE: target clock %dkHz\n",target.timing.pixel_clock));
127		if (head1_set_pix_pll(target) == B_ERROR)
128			LOG(8,("SETMODE: error setting pixel clock (internal DAC)\n"));
129
130		LOG(8,("SETMODE: target2 clock %dkHz\n",target2.timing.pixel_clock));
131		if (head2_set_pix_pll(target2) == B_ERROR)
132			LOG(8,("SETMODE: error setting pixel clock (DAC2)\n"));
133
134		/*set the colour depth for CRTC1 and the DAC */
135		switch(target.space)
136		{
137		case B_CMAP8:
138			colour_depth1 =  8;
139			head1_mode(BPP8, 1.0);
140			head1_depth(BPP8);
141			break;
142		case B_RGB15_LITTLE:
143			colour_depth1 = 16;
144			head1_mode(BPP15, 1.0);
145			head1_depth(BPP15);
146			break;
147		case B_RGB16_LITTLE:
148			colour_depth1 = 16;
149			head1_mode(BPP16, 1.0);
150			head1_depth(BPP16);
151			break;
152		case B_RGB32_LITTLE:
153			colour_depth1 = 32;
154			head1_mode(BPP32, 1.0);
155			head1_depth(BPP32);
156			break;
157		}
158		/*set the colour depth for CRTC2 and DAC2 */
159		switch(target2.space)
160		{
161		case B_CMAP8:
162			colour_depth2 =  8;
163			head2_mode(BPP8, 1.0);
164			head2_depth(BPP8);
165			break;
166		case B_RGB15_LITTLE:
167			colour_depth2 = 16;
168			head2_mode(BPP15, 1.0);
169			head2_depth(BPP15);
170			break;
171		case B_RGB16_LITTLE:
172			colour_depth2 = 16;
173			head2_mode(BPP16, 1.0);
174			head2_depth(BPP16);
175			break;
176		case B_RGB32_LITTLE:
177			colour_depth2 = 32;
178			head2_mode(BPP32, 1.0);
179			head2_depth(BPP32);
180			break;
181		}
182
183		/* check if we are doing interlaced TVout mode */
184		//fixme: we don't support interlaced mode?
185		si->interlaced_tv_mode = false;
186
187		/*set the display(s) pitches*/
188		head1_set_display_pitch ();
189		//fixme: seperate for real dualhead modes:
190		//we need a secondary si->fbc!
191		head2_set_display_pitch ();
192
193		/*work out where the "right" screen starts*/
194		startadd_right = startadd + (target.timing.h_display * (colour_depth1 >> 3));
195
196		/* Tell card what memory to display */
197		switch (target.flags & DUALHEAD_BITS)
198		{
199		case DUALHEAD_ON:
200		case DUALHEAD_SWITCH:
201			head1_set_display_start(startadd,colour_depth1);
202			head2_set_display_start(startadd_right,colour_depth2);
203			break;
204		case DUALHEAD_CLONE:
205			head1_set_display_start(startadd,colour_depth1);
206			head2_set_display_start(startadd,colour_depth2);
207			break;
208		}
209
210		/* set the timing */
211		head1_set_timing(target);
212		head2_set_timing(target2);
213
214		/* TVout support: program TVout encoder and modify CRTC timing */
215		if (si->ps.tvout && (target2.flags & TV_BITS)) BT_setmode(target2);
216	}
217	else /* single head mode */
218	{
219		int colour_mode = BPP32;
220
221		/* connect output */
222		if (si->ps.secondary_head)
223		{
224			/* detect which connectors have a CRT connected */
225			//fixme: 'hot-plugging' for analog monitors removed: remove code as well;
226			//or make it work with digital panels connected as well.
227//			crt1 = nv_dac_crt_connected();
228//			crt2 = nv_dac2_crt_connected();
229			/* connect outputs 'straight-through' */
230//			if (crt1)
231//			{
232				/* connector1 is used as primary output */
233//				cross = false;
234//			}
235//			else
236//			{
237//				if (crt2)
238					/* connector2 is used as primary output */
239//					cross = true;
240//				else
241					/* no CRT detected: assume connector1 is used as primary output */
242//					cross = false;
243//			}
244			/* set output connectors assignment if possible */
245			nv_general_head_select(false);
246		}
247
248		switch(target.space)
249		{
250		case B_CMAP8:        colour_depth1 =  8; colour_mode = BPP8;  break;
251		case B_RGB15_LITTLE: colour_depth1 = 16; colour_mode = BPP15; break;
252		case B_RGB16_LITTLE: colour_depth1 = 16; colour_mode = BPP16; break;
253		case B_RGB32_LITTLE: colour_depth1 = 32; colour_mode = BPP32; break;
254		default:
255			LOG(8,("SETMODE: Invalid singlehead colour depth 0x%08x\n", target.space));
256			return B_ERROR;
257		}
258
259		/* set the pixel clock PLL */
260		if (head1_set_pix_pll(target) == B_ERROR)
261			LOG(8,("CRTC: error setting pixel clock (internal DAC)\n"));
262
263		/* set the colour depth for CRTC1 and the DAC */
264		/* first set the colordepth */
265		head1_depth(colour_mode);
266		/* then(!) program the PAL (<8bit colordepth does not support 8bit PAL) */
267		head1_mode(colour_mode,1.0);
268
269		/* set the display pitch */
270		head1_set_display_pitch();
271
272		/* tell the card what memory to display */
273		head1_set_display_start(startadd,colour_depth1);
274
275		/* set the timing */
276		head1_set_timing(target);
277
278		/* TVout support: program TVout encoder and modify CRTC timing */
279		if (si->ps.tvout && (target.flags & TV_BITS)) BT_setmode(target);
280
281		//fixme: shut-off the videoPLL if it exists...
282	}
283
284	/* update driver's mode store */
285	si->dm = target;
286
287	/* update FIFO data fetching according to mode */
288	nv_crtc_update_fifo();
289	if (si->ps.secondary_head) nv_crtc2_update_fifo();
290
291	/* set up acceleration for this mode */
292	/* note:
293	 * Maybe later we can forget about non-DMA mode (depends on 3D acceleration
294	 * attempts). */
295	if (!si->settings.block_acc) {
296		if (!si->settings.dma_acc)
297			nv_acc_init();
298		else
299			nv_acc_init_dma();
300	}
301
302	/* set up overlay unit for this mode */
303	nv_bes_init();
304
305	/* note freemem range */
306	/* first free adress follows hardcursor and workspace */
307	si->engine.threeD.mem_low = si->fbc.bytes_per_row * si->dm.virtual_height;
308	if (si->settings.hardcursor) si->engine.threeD.mem_low += 2048;
309	/* last free adress is end-of-ram minus max space needed for overlay bitmaps */
310	//fixme possible:
311	//if overlay buffers are allocated subtract buffersize from mem_high;
312	//only allocate overlay buffers if 3D is not in use. (block overlay during 3D)
313	si->engine.threeD.mem_high = si->ps.memory_size - 1;
314	/* Keep some extra distance as a workaround for certain bugs (see
315	 * DriverInterface.h for an explanation). */
316	if (si->ps.card_arch < NV40A)
317		si->engine.threeD.mem_high -= PRE_NV40_OFFSET;
318	else
319		si->engine.threeD.mem_high -= NV40_PLUS_OFFSET;
320
321	si->engine.threeD.mem_high -= (MAXBUFFERS * 1024 * 1024 * 2); /* see overlay.c file */
322
323	/* restore screen(s) output state(s) */
324	SET_DPMS_MODE(si->dpms_flags);
325
326	/* enable interrupts using the kernel driver */
327	//fixme:
328	//add head2 once we use one driver instance 'per head' (instead of 'per card')
329	head1_interrupt_enable(true);
330
331	/* make sure a possible 3D add-on will re-initialize itself by signalling ready */
332	si->engine.threeD.mode_changing = false;
333
334	/* optimize memory-access if needed */
335//	head1_mem_priority(colour_depth1);
336
337	/* Tune RAM CAS-latency if needed. Must be done *here*! */
338	nv_set_cas_latency();
339
340	LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0));
341
342	return B_OK;
343}
344
345/*
346	Set which pixel of the virtual frame buffer will show up in the
347	top left corner of the display device.  Used for page-flipping
348	games and virtual desktops.
349*/
350status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start) {
351	uint8 colour_depth;
352	uint32 startadd,startadd_right;
353
354	LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start));
355
356	/* nVidia cards support pixelprecise panning on both heads in all modes:
357	 * No stepping granularity needed! */
358
359	/* determine bits used for the colordepth */
360	switch(si->dm.space)
361	{
362	case B_CMAP8:
363		colour_depth=8;
364		break;
365	case B_RGB15_LITTLE:
366	case B_RGB16_LITTLE:
367		colour_depth=16;
368		break;
369	case B_RGB24_LITTLE:
370		colour_depth=24;
371		break;
372	case B_RGB32_LITTLE:
373		colour_depth=32;
374		break;
375	default:
376		return B_ERROR;
377	}
378
379	/* do not run past end of display */
380	switch (si->dm.flags & DUALHEAD_BITS)
381	{
382	case DUALHEAD_ON:
383	case DUALHEAD_SWITCH:
384		if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width)
385			return B_ERROR;
386		break;
387	default:
388		if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width)
389			return B_ERROR;
390		break;
391	}
392	if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height)
393		return B_ERROR;
394
395	/* everybody remember where we parked... */
396	si->dm.h_display_start = h_display_start;
397	si->dm.v_display_start = v_display_start;
398
399	/* actually set the registers */
400	//fixme: seperate both heads: we need a secondary si->fbc!
401	startadd = v_display_start * si->fbc.bytes_per_row;
402	startadd += h_display_start * (colour_depth >> 3);
403	startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
404	startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3);
405
406	/* disable interrupts using the kernel driver */
407	head1_interrupt_enable(false);
408	if (si->ps.secondary_head) head2_interrupt_enable(false);
409
410	switch (si->dm.flags & DUALHEAD_BITS)
411	{
412		case DUALHEAD_ON:
413		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