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