1/*
2	Copyright 1999, Be Incorporated.   All Rights Reserved.
3	This file may be used under the terms of the Be Sample Code License.
4
5	Other authors:
6	Rudolf Cornelissen 4/2003-1/2006
7*/
8
9
10#define MODULE_BIT 0x00200000
11
12#include "acc_std.h"
13
14/*
15	Enable/Disable interrupts.  Just a wrapper around the
16	ioctl() to the kernel driver.
17*/
18static void
19interrupt_enable(bool flag)
20{
21	nm_set_bool_state sbs;
22
23	if (si->ps.int_assigned) {
24		/* set the magic number so the driver knows we're for real */
25		sbs.magic = NM_PRIVATE_DATA_MAGIC;
26		sbs.do_it = flag;
27		/* contact driver and get a pointer to the registers and shared data */
28		ioctl(fd, NM_RUN_INTERRUPTS, &sbs, sizeof(sbs));
29	}
30}
31
32
33/* First validate the mode, then call lots of bit banging stuff to set the mode(s)! */
34status_t SET_DISPLAY_MODE(display_mode *mode_to_set)
35{
36	/* BOUNDS WARNING:
37	 * It's impossible to deviate whatever small amount in a display_mode if the lower
38	 * and upper limits are the same!
39	 * Besides:
40	 * BeOS (tested R5.0.3PE) is failing BWindowScreen::SetFrameBuffer() if PROPOSEMODE
41	 * returns B_BAD_VALUE!
42	 * Which means PROPOSEMODE should not return that on anything except on
43	 * deviations for:
44	 * display_mode.virtual_width;
45	 * display_mode.virtual_height;
46	 * display_mode.timing.h_display;
47	 * display_mode.timing.v_display;
48	 * So:
49	 * We don't use bounds here by making sure bounds and target are the same struct!
50	 * (See the call to PROPOSE_DISPLAY_MODE below) */
51	display_mode /*bounds,*/ target;
52
53	uint8 colour_depth = 24;
54	uint32 startadd;
55
56	/* if internal panel is active we don't touch the CRTC timing and the pixelPLL */
57	bool crt_only = true;
58
59	/* Adjust mode to valid one and fail if invalid */
60	target /*= bounds*/ = *mode_to_set;
61	/* show the mode bits */
62	LOG(1, ("SETMODE: (ENTER) initial modeflags: $%08x\n", target.flags));
63	LOG(1, ("SETMODE: requested target pixelclock %dkHz\n",  target.timing.pixel_clock));
64	LOG(1, ("SETMODE: requested virtual_width %d, virtual_height %d\n",
65										target.virtual_width, target.virtual_height));
66
67	/* See BOUNDS WARNING above... */
68	if (PROPOSE_DISPLAY_MODE(&target, &target, &target) == B_ERROR)	return B_ERROR;
69
70	/* disable interrupts using the kernel driver */
71	interrupt_enable(false);
72
73	/* make sure the card's registers are unlocked (KB output select may lock!) */
74	nm_unlock();
75
76	/* if we have the flatpanel turned on modify visible part of mode if nessesary */
77	if (nm_general_output_read() & 0x02)
78	{
79		LOG(4,("SETMODE: internal flatpanel enabled, skipping CRTC/pixelPLL setup\n"));
80		crt_only = false;
81	}
82
83	/* turn off screen(s) */
84	nm_crtc_dpms(false, false, false);
85
86	/* where in framebuffer the screen is (should this be dependant on previous MOVEDISPLAY?) */
87	startadd = (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
88
89	/* Perform the mode switch */
90	{
91		status_t status = B_OK;
92		int colour_mode = BPP24;
93
94		switch(target.space)
95		{
96		case B_CMAP8:        colour_depth =  8; colour_mode = BPP8;  break;
97		case B_RGB15_LITTLE: colour_depth = 16; colour_mode = BPP15; break;
98		case B_RGB16_LITTLE: colour_depth = 16; colour_mode = BPP16; break;
99		case B_RGB24_LITTLE: colour_depth = 24; colour_mode = BPP24; break;
100		default:
101			LOG(8,("SETMODE: Invalid colorspace $%08x\n", target.space));
102			return B_ERROR;
103		}
104
105		/* calculate and set new mode bytes_per_row */
106		nm_general_validate_pic_size (&target, &si->fbc.bytes_per_row, &si->acc_mode);
107
108		/* set the pixelclock PLL */
109		if (crt_only)
110		{
111			status = nm_dac_set_pix_pll(target);
112			if (status == B_ERROR)
113				LOG(8,("CRTC: error setting pixelclock\n"));
114		}
115
116		/* set the colour depth for CRTC1 and the DAC */
117		nm_dac_mode(colour_mode, 1.0);
118		nm_crtc_depth(colour_mode);
119
120		/* set the display pitch */
121		nm_crtc_set_display_pitch();
122
123		/* tell the card what memory to display */
124		nm_crtc_set_display_start(startadd,colour_depth);
125
126		/* enable primary analog output */
127		//fixme: choose output connector(s)
128
129		/* set the timing */
130		nm_crtc_set_timing(target, crt_only);
131
132		/* always setup centering so a KB BIOS switch to flatpanel will go OK... */
133		nm_crtc_center(target, crt_only);
134		/* program panel modeline if needed */
135		if (!crt_only) nm_crtc_prg_panel();
136	}
137
138	/* update driver's mode store */
139	si->dm = target;
140
141	/* set up acceleration for this mode */
142	nm_acc_init();
143
144	/* restore screen(s) output state(s) */
145	SET_DPMS_MODE(si->dpms_flags);
146
147	/* log currently selected output */
148	nm_general_output_select();
149
150	LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0));
151
152	/* enable interrupts using the kernel driver */
153	interrupt_enable(true);
154
155	/* Tune RAM CAS-latency if needed. Must be done *here*! */
156	nm_set_cas_latency();
157
158	return B_OK;
159}
160
161/*
162	Set which pixel of the virtual frame buffer will show up in the
163	top left corner of the display device.  Used for page-flipping
164	games and virtual desktops.
165*/
166status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start)
167{
168	uint8 colour_depth;
169	uint32 startadd;
170
171	uint16 h_display = si->dm.timing.h_display; /* local copy needed for flatpanel */
172	uint16 v_display = si->dm.timing.v_display; /* local copy needed for flatpanel */
173
174	LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start));
175
176	/* Neomagic cards support pixelprecise panning in all modes:
177	 * No stepping granularity needed! */
178
179	/* determine bits used for the colordepth */
180	switch(si->dm.space)
181	{
182	case B_CMAP8:
183		colour_depth=8;
184		break;
185	case B_RGB15_LITTLE:
186	case B_RGB16_LITTLE:
187		colour_depth=16;
188		break;
189	case B_RGB24_LITTLE:
190		colour_depth=24;
191		break;
192	default:
193		return B_ERROR;
194	}
195
196	/* if internal panel is active correct visible screensize! */
197	if (nm_general_output_read() & 0x02)
198	{
199		if (h_display > si->ps.panel_width) h_display = si->ps.panel_width;
200		if (v_display > si->ps.panel_height) v_display = si->ps.panel_height;
201	}
202
203	/* do not run past end of display */
204	if ((h_display + h_display_start) > si->dm.virtual_width)
205		return B_ERROR;
206	if ((v_display + v_display_start) > si->dm.virtual_height)
207		return B_ERROR;
208
209	/* everybody remember where we parked... */
210	si->dm.h_display_start = h_display_start;
211	si->dm.v_display_start = v_display_start;
212
213	/* actually set the registers */
214	startadd = v_display_start * si->fbc.bytes_per_row;
215	startadd += h_display_start * (colour_depth >> 3);
216	startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
217
218	interrupt_enable(false);
219
220	nm_crtc_set_display_start(startadd,colour_depth);
221
222	interrupt_enable(true);
223	return B_OK;
224}
225
226/* Set the indexed color palette. */
227void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) {
228	int i;
229	uint8 *r,*g,*b;
230
231	/* Protect gamma correction when not in CMAP8 */
232	if (si->dm.space != B_CMAP8) return;
233
234	r = si->color_data;
235	g = r + 256;
236	b = g + 256;
237
238	i = first;
239	while (count--)
240	{
241		r[i] = *color_data++;
242		g[i] = *color_data++;
243		b[i] = *color_data++;
244		i++;
245	}
246	nm_dac_palette(r,g,b, 256);
247}
248
249/* Put the display into one of the Display Power Management modes. */
250status_t SET_DPMS_MODE(uint32 dpms_flags)
251{
252	interrupt_enable(false);
253
254	LOG(4,("SET_DPMS_MODE: $%08x\n", dpms_flags));
255
256	/* note current DPMS state for our reference */
257	si->dpms_flags = dpms_flags;
258
259	switch(dpms_flags)
260	{
261	case B_DPMS_ON:	/* H: on, V: on */
262		nm_crtc_dpms(true, true , true);
263		break;
264	case B_DPMS_STAND_BY:
265		nm_crtc_dpms(false, false, true);
266		break;
267	case B_DPMS_SUSPEND:
268		nm_crtc_dpms(false, true, false);
269		break;
270	case B_DPMS_OFF: /* H: off, V: off, display off */
271		nm_crtc_dpms(false, false, false);
272		break;
273	default:
274		LOG(8,("SET_DPMS_MODE: Invalid DPMS settings) $%08x\n", dpms_flags));
275		interrupt_enable(true);
276		return B_ERROR;
277	}
278	interrupt_enable(true);
279	return B_OK;
280}
281
282/* Report device DPMS capabilities. */
283uint32 DPMS_CAPABILITIES(void)
284{
285	if (si->ps.card_type < NM2200)
286		/* MagicGraph cards don't have full DPMS support */
287		return 	B_DPMS_ON | B_DPMS_OFF;
288	else
289		/* MagicMedia cards do have full DPMS support for external monitors */
290		//fixme: checkout if this is true...
291		return 	B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF;
292}
293
294/* Return the current DPMS mode. */
295uint32 DPMS_MODE(void)
296{
297	return si->dpms_flags;
298}
299