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