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-9/2005
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 = VIA_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);
282
283		if (status==B_ERROR)
284			LOG(8,("CRTC: error setting pixel clock (internal DAC)\n"));
285
286		/* set the colour depth for CRTC1 and the DAC */
287		/* first set the colordepth */
288		head1_depth(colour_mode);
289		/* then(!) program the PAL (<8bit colordepth does not support 8bit PAL) */
290		head1_mode(colour_mode,1.0);
291
292		/* set the display pitch */
293		head1_set_display_pitch();
294
295		/* tell the card what memory to display */
296		head1_set_display_start(startadd,colour_depth1);
297
298		/* set the timing */
299		head1_set_timing(target);
300
301		//fixme: shut-off the videoPLL if it exists...
302	}
303
304	/* update driver's mode store */
305	si->dm = target;
306
307	/* turn screen one on */
308	head1_dpms(display, h, v);
309	/* turn screen two on if a dualhead mode is active */
310//	if (target.flags & DUALHEAD_BITS) head2_dpms(display,h,v);
311
312	/* set up acceleration for this mode */
313//	eng_acc_init();
314	/* set up overlay unit for this mode */
315	eng_bes_init();
316
317	LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0));
318
319	/* enable interrupts using the kernel driver */
320	interrupt_enable(true);
321
322	/* optimize memory-access if needed */
323//	head1_mem_priority(colour_depth1);
324
325	/* Tune RAM CAS-latency if needed. Must be done *here*! */
326	eng_set_cas_latency();
327
328	return B_OK;
329}
330
331/*
332	Set which pixel of the virtual frame buffer will show up in the
333	top left corner of the display device.  Used for page-flipping
334	games and virtual desktops.
335*/
336status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start) {
337	uint8 colour_depth;
338	uint32 startadd,startadd_right;
339
340	LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start));
341
342	/* VIA CRTC1 handles multiples of 8 for 8bit, 4 for 16bit, 2 for 32 bit
343	   VIA CRTC2 is yet unknown...
344     */
345
346	/* reset lower bits, don't return an error! */
347	if (si->dm.flags & DUALHEAD_BITS)
348	{
349		//fixme for VIA...
350		switch(si->dm.space)
351		{
352		case B_RGB16_LITTLE:
353			colour_depth=16;
354			h_display_start &= ~0x1f;
355			break;
356		case B_RGB32_LITTLE:
357			colour_depth=32;
358			h_display_start &= ~0x0f;
359			break;
360		default:
361			LOG(8,("SET:Invalid DH colour depth 0x%08x, should never happen\n", si->dm.space));
362			return B_ERROR;
363		}
364	}
365	else
366	{
367		switch(si->dm.space)
368		{
369		case B_CMAP8:
370			colour_depth=8;
371			h_display_start &= ~0x07;
372			break;
373		case B_RGB15_LITTLE: case B_RGB16_LITTLE:
374			colour_depth=16;
375			h_display_start &= ~0x03;
376			break;
377		case B_RGB32_LITTLE:
378			colour_depth=32;
379			h_display_start &= ~0x01;
380			break;
381		default:
382			return B_ERROR;
383		}
384	}
385
386	/* do not run past end of display */
387	switch (si->dm.flags & DUALHEAD_BITS)
388	{
389	case DUALHEAD_ON:
390	case DUALHEAD_SWITCH:
391		if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width)
392			return B_ERROR;
393		break;
394	default:
395		if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width)
396			return B_ERROR;
397		break;
398	}
399	if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height)
400		return B_ERROR;
401
402	/* everybody remember where we parked... */
403	si->dm.h_display_start = h_display_start;
404	si->dm.v_display_start = v_display_start;
405
406	/* actually set the registers */
407	//fixme: seperate both heads: we need a secondary si->fbc!
408	startadd = v_display_start * si->fbc.bytes_per_row;
409	startadd += h_display_start * (colour_depth >> 3);
410	startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
411	startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3);
412
413	interrupt_enable(false);
414
415	switch (si->dm.flags & DUALHEAD_BITS)
416	{
417		case DUALHEAD_ON:
418		case DUALHEAD_SWITCH:
419			head1_set_display_start(startadd,colour_depth);
420//			head2_set_display_start(startadd_right,colour_depth);
421			break;
422		case DUALHEAD_OFF:
423			head1_set_display_start(startadd,colour_depth);
424			break;
425		case DUALHEAD_CLONE:
426			head1_set_display_start(startadd,colour_depth);
427//			head2_set_display_start(startadd,colour_depth);
428			break;
429	}
430
431	interrupt_enable(true);
432	return B_OK;
433}
434
435/* Set the indexed color palette */
436void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) {
437	int i;
438	uint8 *r,*g,*b;
439
440	/* Protect gamma correction when not in CMAP8 */
441	if (si->dm.space != B_CMAP8) return;
442
443	r=si->color_data;
444	g=r+256;
445	b=g+256;
446
447	i=first;
448	while (count--)
449	{
450		r[i]=*color_data++;
451		g[i]=*color_data++;
452		b[i]=*color_data++;
453		i++;
454	}
455	head1_palette(r,g,b);
456//	if (si->dm.flags & DUALHEAD_BITS) head2_palette(r,g,b);
457}
458
459/* Put the display into one of the Display Power Management modes. */
460status_t SET_DPMS_MODE(uint32 dpms_flags) {
461	interrupt_enable(false);
462
463	LOG(4,("SET_DPMS_MODE: 0x%08x\n", dpms_flags));
464
465#if 0
466	if (si->dm.flags & DUALHEAD_BITS) /*dualhead*/
467	{
468		switch(dpms_flags)
469		{
470		case B_DPMS_ON:	/* H: on, V: on, display on */
471			head1_dpms(true, true, true);
472			if (si->ps.secondary_head) head2_dpms(true, true, true);
473			break;
474		case B_DPMS_STAND_BY:
475			head1_dpms(false, false, true);
476			if (si->ps.secondary_head) head2_dpms(false, false, true);
477			break;
478		case B_DPMS_SUSPEND:
479			head1_dpms(false, true, false);
480			if (si->ps.secondary_head) head2_dpms(false, true, false);
481			break;
482		case B_DPMS_OFF: /* H: off, V: off, display off */
483			head1_dpms(false, false, false);
484			if (si->ps.secondary_head) head2_dpms(false, false, false);
485			break;
486		default:
487			LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags));
488			interrupt_enable(true);
489			return B_ERROR;
490		}
491	} else /* singlehead */
492#endif
493	{
494		switch(dpms_flags)
495		{
496		case B_DPMS_ON:	/* H: on, V: on, display on */
497			head1_dpms(true, true, true);
498			break;
499		case B_DPMS_STAND_BY:
500			head1_dpms(false, false, true);
501			break;
502		case B_DPMS_SUSPEND:
503			head1_dpms(false, true, false);
504			break;
505		case B_DPMS_OFF: /* H: off, V: off, display off */
506			head1_dpms(false, false, false);
507			break;
508		default:
509			LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags));
510			interrupt_enable(true);
511			return B_ERROR;
512		}
513	}
514	interrupt_enable(true);
515	return B_OK;
516}
517
518/* Report device DPMS capabilities */
519uint32 DPMS_CAPABILITIES(void) {
520	return 	(B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF);
521}
522
523/* Return the current DPMS mode */
524uint32 DPMS_MODE(void) {
525	bool display, h, v;
526
527	interrupt_enable(false);
528	head1_dpms_fetch(&display, &h, &v);
529
530	interrupt_enable(true);
531
532	if (display && h && v)
533		return B_DPMS_ON;
534	else if(v)
535		return B_DPMS_STAND_BY;
536	else if(h)
537		return B_DPMS_SUSPEND;
538	else
539		return B_DPMS_OFF;
540}
541