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-4/2004
10*/
11
12#define MODULE_BIT 0x00200000
13
14#include "acc_std.h"
15
16/*
17	Enable/Disable interrupts.  Just a wrapper around the
18	ioctl() to the kernel driver.
19*/
20static void interrupt_enable(bool flag) {
21	status_t result;
22	eng_set_bool_state sbs;
23
24	/* set the magic number so the driver knows we're for real */
25	sbs.magic = SKEL_PRIVATE_DATA_MAGIC;
26	sbs.do_it = flag;
27	/* contact driver and get a pointer to the registers and shared data */
28	result = ioctl(fd, ENG_RUN_INTERRUPTS, &sbs, sizeof(sbs));
29}
30
31/* First validate the mode, then call lots of bit banging stuff to set the mode(s)! */
32status_t SET_DISPLAY_MODE(display_mode *mode_to_set)
33{
34	/* BOUNDS WARNING:
35	 * It's impossible to deviate whatever small amount in a display_mode if the lower
36	 * and upper limits are the same!
37	 * Besides:
38	 * BeOS (tested R5.0.3PE) is failing BWindowScreen::SetFrameBuffer() if PROPOSEMODE
39	 * returns B_BAD_VALUE!
40	 * Which means PROPOSEMODE should not return that on anything except on
41	 * deviations for:
42	 * display_mode.virtual_width;
43	 * display_mode.virtual_height;
44	 * display_mode.timing.h_display;
45	 * display_mode.timing.v_display;
46	 * So:
47	 * We don't use bounds here by making sure bounds and target are the same struct!
48	 * (See the call to PROPOSE_DISPLAY_MODE below) */
49	display_mode /*bounds,*/ target;
50
51	uint8 colour_depth1 = 32;
52//	status_t result;
53	uint32 startadd,startadd_right;
54//	bool display, h, v;
55//	bool crt1, crt2, cross;
56
57	/* Adjust mode to valid one and fail if invalid */
58	target /*= bounds*/ = *mode_to_set;
59	/* show the mode bits */
60	LOG(1, ("SETMODE: (ENTER) initial modeflags: $%08x\n", target.flags));
61	LOG(1, ("SETMODE: requested target pixelclock %dkHz\n",  target.timing.pixel_clock));
62	LOG(1, ("SETMODE: requested virtual_width %d, virtual_height %d\n",
63										target.virtual_width, target.virtual_height));
64
65	/* See BOUNDS WARNING above... */
66	if (PROPOSE_DISPLAY_MODE(&target, &target, &target) == B_ERROR)	return B_ERROR;
67
68	/* if not dualhead capable card clear dualhead flags */
69	if (!(target.flags & DUALHEAD_CAPABLE))
70	{
71		target.flags &= ~DUALHEAD_BITS;
72	}
73	/* if not TVout capable card clear TVout flags */
74	if (!(target.flags & TV_CAPABLE))
75	{
76		target.flags &= ~TV_BITS;
77	}
78	LOG(1, ("SETMODE: (CONT.) validated command modeflags: $%08x\n", target.flags));
79
80	/* disable interrupts using the kernel driver */
81	interrupt_enable(false);
82
83	/* find current DPMS state, then turn off screen(s) */
84//	head1_dpms_fetch(&display, &h, &v);
85//	head1_dpms(false, false, false);
86//	if (si->ps.secondary_head) head2_dpms(false, false, false);
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	eng_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		/* validate flags for secondary TVout */
105//		if ((i2c_sec_tv_adapter() != B_OK) && (target2.flags & TV_BITS))
106//		{
107//			target.flags &= ~TV_BITS;//still needed for some routines...
108//			target2.flags &= ~TV_BITS;
109//			LOG(1,("SETMODE: blocking TVout: no TVout cable connected!\n"));
110//		}
111
112		/* detect which connectors have a CRT connected */
113		//fixme: 'hot-plugging' for analog monitors removed: remove code as well;
114		//or make it work with digital panels connected as well.
115//		crt1 = eng_dac_crt_connected();
116//		crt2 = eng_dac2_crt_connected();
117		/* connect outputs 'straight-through' */
118//		if (crt1)
119//		{
120			/* connector1 is used as primary output */
121//			cross = false;
122//		}
123//		else
124//		{
125//			if (crt2)
126				/* connector2 is used as primary output */
127//				cross = true;
128//			else
129				/* no CRT detected: assume connector1 is used as primary output */
130//				cross = false;
131//		}
132		/* set output connectors assignment if possible */
133//		if ((target.flags & DUALHEAD_BITS) == DUALHEAD_SWITCH)
134			/* invert output assignment in switch mode */
135//			eng_general_head_select(true);
136//		else
137//			eng_general_head_select(false);
138
139		/* set the pixel clock PLL(s) */
140		LOG(8,("SETMODE: target clock %dkHz\n",target.timing.pixel_clock));
141//		if (head1_set_pix_pll(target) == B_ERROR)
142//			LOG(8,("SETMODE: error setting pixel clock (internal DAC)\n"));
143
144		/* we do not need to set the pixelclock here for a head that's in TVout mode */
145//		if (!(target2.flags & TV_BITS))
146//		{
147//			LOG(8,("SETMODE: target2 clock %dkHz\n",target2.timing.pixel_clock));
148//			if (head2_set_pix_pll(target2) == B_ERROR)
149//				LOG(8,("SETMODE: error setting pixel clock (DAC2)\n"));
150//		}
151
152		/*set the colour depth for CRTC1 and the DAC */
153		switch(target.space)
154		{
155		case B_CMAP8:
156			colour_depth1 =  8;
157//			head1_mode(BPP8, 1.0);
158//			head1_depth(BPP8);
159			break;
160		case B_RGB15_LITTLE:
161			colour_depth1 = 16;
162//			head1_mode(BPP15, 1.0);
163//			head1_depth(BPP15);
164			break;
165		case B_RGB16_LITTLE:
166			colour_depth1 = 16;
167//			head1_mode(BPP16, 1.0);
168//			head1_depth(BPP16);
169			break;
170		case B_RGB32_LITTLE:
171			colour_depth1 = 32;
172//			head1_mode(BPP32, 1.0);
173//			head1_depth(BPP32);
174			break;
175		}
176		/*set the colour depth for CRTC2 and DAC2 */
177		switch(target2.space)
178		{
179		case B_CMAP8:
180			colour_depth2 =  8;
181//			head2_mode(BPP8, 1.0);
182//			head2_depth(BPP8);
183			break;
184		case B_RGB15_LITTLE:
185			colour_depth2 = 16;
186//			head2_mode(BPP15, 1.0);
187//			head2_depth(BPP15);
188			break;
189		case B_RGB16_LITTLE:
190			colour_depth2 = 16;
191//			head2_mode(BPP16, 1.0);
192//			head2_depth(BPP16);
193			break;
194		case B_RGB32_LITTLE:
195			colour_depth2 = 32;
196//			head2_mode(BPP32, 1.0);
197//			head2_depth(BPP32);
198			break;
199		}
200
201		/* check if we are doing interlaced TVout mode */
202		si->interlaced_tv_mode = false;
203/*		if ((target2.flags & TV_BITS) && (si->ps.card_type >= G450))
204			si->interlaced_tv_mode = true;
205*/
206		/*set the display(s) pitches*/
207//		head1_set_display_pitch ();
208		//fixme: seperate for real dualhead modes:
209		//we need a secondary si->fbc!
210//		head2_set_display_pitch ();
211
212		/*work out where the "right" screen starts*/
213		startadd_right = startadd + (target.timing.h_display * (colour_depth1 >> 3));
214
215		/* Tell card what memory to display */
216		switch (target.flags & DUALHEAD_BITS)
217		{
218		case DUALHEAD_ON:
219		case DUALHEAD_SWITCH:
220//			head1_set_display_start(startadd,colour_depth1);
221//			head2_set_display_start(startadd_right,colour_depth2);
222			break;
223		case DUALHEAD_CLONE:
224//			head1_set_display_start(startadd,colour_depth1);
225//			head2_set_display_start(startadd,colour_depth2);
226			break;
227		}
228
229		/* set the timing */
230//		head1_set_timing(target);
231		/* we do not need to setup CRTC2 here for a head that's in TVout mode */
232//		if (!(target2.flags & TV_BITS))	result = head2_set_timing(target2);
233
234		/* TVout support: setup CRTC2 and it's pixelclock */
235//		if (si->ps.tvout && (target2.flags & TV_BITS)) maventv_init(target2);
236	}
237	else /* single head mode */
238	{
239		status_t status;
240		int colour_mode = BPP32;
241
242		/* connect output */
243		if (si->ps.secondary_head)
244		{
245			/* detect which connectors have a CRT connected */
246			//fixme: 'hot-plugging' for analog monitors removed: remove code as well;
247			//or make it work with digital panels connected as well.
248//			crt1 = eng_dac_crt_connected();
249//			crt2 = eng_dac2_crt_connected();
250			/* connect outputs 'straight-through' */
251//			if (crt1)
252//			{
253				/* connector1 is used as primary output */
254//				cross = false;
255//			}
256//			else
257//			{
258//				if (crt2)
259					/* connector2 is used as primary output */
260//					cross = true;
261//				else
262					/* no CRT detected: assume connector1 is used as primary output */
263//					cross = false;
264//			}
265			/* set output connectors assignment if possible */
266			eng_general_head_select(false);
267		}
268
269		switch(target.space)
270		{
271		case B_CMAP8:        colour_depth1 =  8; colour_mode = BPP8;  break;
272		case B_RGB15_LITTLE: colour_depth1 = 16; colour_mode = BPP15; break;
273		case B_RGB16_LITTLE: colour_depth1 = 16; colour_mode = BPP16; break;
274		case B_RGB32_LITTLE: colour_depth1 = 32; colour_mode = BPP32; break;
275		default:
276			LOG(8,("SETMODE: Invalid singlehead colour depth 0x%08x\n", target.space));
277			return B_ERROR;
278		}
279
280		/* set the pixel clock PLL */
281//		status = head1_set_pix_pll(target);
282status = B_OK;
283
284		if (status==B_ERROR)
285			LOG(8,("CRTC: error setting pixel clock (internal DAC)\n"));
286
287		/* set the colour depth for CRTC1 and the DAC */
288		/* first set the colordepth */
289//		head1_depth(colour_mode);
290		/* then(!) program the PAL (<8bit colordepth does not support 8bit PAL) */
291//		head1_mode(colour_mode,1.0);
292
293		/* set the display pitch */
294//		head1_set_display_pitch();
295
296		/* tell the card what memory to display */
297//		head1_set_display_start(startadd,colour_depth1);
298
299		/* set the timing */
300//		head1_set_timing(target);
301
302		//fixme: shut-off the videoPLL if it exists...
303	}
304
305	/* update driver's mode store */
306	si->dm = target;
307
308	/* turn screen one on */
309//	head1_dpms(display, h, v);
310	/* turn screen two on if a dualhead mode is active */
311//	if (target.flags & DUALHEAD_BITS) head2_dpms(display,h,v);
312
313	/* set up acceleration for this mode */
314//	eng_acc_init();
315	/* set up overlay unit for this mode */
316//	eng_bes_init();
317
318	LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0));
319
320	/* enable interrupts using the kernel driver */
321	interrupt_enable(true);
322
323	/* optimize memory-access if needed */
324//	head1_mem_priority(colour_depth1);
325
326	/* Tune RAM CAS-latency if needed. Must be done *here*! */
327	eng_set_cas_latency();
328
329	return B_OK;
330}
331
332/*
333	Set which pixel of the virtual frame buffer will show up in the
334	top left corner of the display device.  Used for page-flipping
335	games and virtual desktops.
336*/
337status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start) {
338	uint8 colour_depth;
339	uint32 startadd,startadd_right;
340
341	LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start));
342
343	/* nVidia cards support pixelprecise panning on both heads in all modes:
344	 * No stepping granularity needed! */
345
346	/* determine bits used for the colordepth */
347	switch(si->dm.space)
348	{
349	case B_CMAP8:
350		colour_depth=8;
351		break;
352	case B_RGB15_LITTLE:
353	case B_RGB16_LITTLE:
354		colour_depth=16;
355		break;
356	case B_RGB24_LITTLE:
357		colour_depth=24;
358		break;
359	case B_RGB32_LITTLE:
360		colour_depth=32;
361		break;
362	default:
363		return B_ERROR;
364	}
365
366	/* do not run past end of display */
367	switch (si->dm.flags & DUALHEAD_BITS)
368	{
369	case DUALHEAD_ON:
370	case DUALHEAD_SWITCH:
371		if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width)
372			return B_ERROR;
373		break;
374	default:
375		if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width)
376			return B_ERROR;
377		break;
378	}
379	if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height)
380		return B_ERROR;
381
382	/* everybody remember where we parked... */
383	si->dm.h_display_start = h_display_start;
384	si->dm.v_display_start = v_display_start;
385
386	/* actually set the registers */
387	//fixme: seperate both heads: we need a secondary si->fbc!
388	startadd = v_display_start * si->fbc.bytes_per_row;
389	startadd += h_display_start * (colour_depth >> 3);
390	startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
391	startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3);
392
393	interrupt_enable(false);
394
395	switch (si->dm.flags & DUALHEAD_BITS)
396	{
397		case DUALHEAD_ON:
398		case DUALHEAD_SWITCH:
399//			head1_set_display_start(startadd,colour_depth);
400//			head2_set_display_start(startadd_right,colour_depth);
401			break;
402		case DUALHEAD_OFF:
403//			head1_set_display_start(startadd,colour_depth);
404			break;
405		case DUALHEAD_CLONE:
406//			head1_set_display_start(startadd,colour_depth);
407//			head2_set_display_start(startadd,colour_depth);
408			break;
409	}
410
411	interrupt_enable(true);
412	return B_OK;
413}
414
415/* Set the indexed color palette */
416void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) {
417	int i;
418	uint8 *r,*g,*b;
419
420	/* Protect gamma correction when not in CMAP8 */
421	if (si->dm.space != B_CMAP8) return;
422
423	r=si->color_data;
424	g=r+256;
425	b=g+256;
426
427	i=first;
428	while (count--)
429	{
430		r[i]=*color_data++;
431		g[i]=*color_data++;
432		b[i]=*color_data++;
433		i++;
434	}
435//	head1_palette(r,g,b);
436	if (si->dm.flags & DUALHEAD_BITS) head2_palette(r,g,b);
437}
438
439/* Put the display into one of the Display Power Management modes. */
440status_t SET_DPMS_MODE(uint32 dpms_flags) {
441	interrupt_enable(false);
442
443	LOG(4,("SET_DPMS_MODE: 0x%08x\n", dpms_flags));
444
445	if (si->dm.flags & DUALHEAD_BITS) /*dualhead*/
446	{
447		switch(dpms_flags)
448		{
449		case B_DPMS_ON:	/* H: on, V: on, display on */
450//			head1_dpms(true, true, true);
451//			if (si->ps.secondary_head) head2_dpms(true, true, true);
452			break;
453		case B_DPMS_STAND_BY:
454//			head1_dpms(false, false, true);
455//			if (si->ps.secondary_head) head2_dpms(false, false, true);
456			break;
457		case B_DPMS_SUSPEND:
458//			head1_dpms(false, true, false);
459//			if (si->ps.secondary_head) head2_dpms(false, true, false);
460			break;
461		case B_DPMS_OFF: /* H: off, V: off, display off */
462//			head1_dpms(false, false, false);
463//			if (si->ps.secondary_head) head2_dpms(false, false, false);
464			break;
465		default:
466			LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags));
467			interrupt_enable(true);
468			return B_ERROR;
469		}
470	}
471	else /* singlehead */
472	{
473		switch(dpms_flags)
474		{
475		case B_DPMS_ON:	/* H: on, V: on, display on */
476//			head1_dpms(true, true, true);
477			break;
478		case B_DPMS_STAND_BY:
479//			head1_dpms(false, false, true);
480			break;
481		case B_DPMS_SUSPEND:
482//			head1_dpms(false, true, false);
483			break;
484		case B_DPMS_OFF: /* H: off, V: off, display off */
485//			head1_dpms(false, false, false);
486			break;
487		default:
488			LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags));
489			interrupt_enable(true);
490			return B_ERROR;
491		}
492	}
493	interrupt_enable(true);
494	return B_OK;
495}
496
497/* Report device DPMS capabilities */
498uint32 DPMS_CAPABILITIES(void) {
499	return 	(B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF);
500}
501
502/* Return the current DPMS mode */
503uint32 DPMS_MODE(void) {
504	bool display, h, v;
505
506	interrupt_enable(false);
507//	head1_dpms_fetch(&display, &h, &v);
508display = h = v = true;
509
510	interrupt_enable(true);
511
512	if (display && h && v)
513		return B_DPMS_ON;
514	else if(v)
515		return B_DPMS_STAND_BY;
516	else if(h)
517		return B_DPMS_SUSPEND;
518	else
519		return B_DPMS_OFF;
520}
521