xref: /haiku/src/add-ons/accelerants/3dfx/mode.cpp (revision fd9fa13896676181d9ce408f3843e96f26c592c6)
1 /*
2  * Copyright 2007-2010 Haiku, Inc.  All rights reserved.
3  * Distributed under the terms of the MIT license.
4 
5  * Authors:
6  *		Gerald Zajac
7  */
8 
9 #include "accelerant.h"
10 
11 #include <create_display_modes.h>		// common accelerant header file
12 
13 #include <string.h>
14 #include <unistd.h>
15 
16 
17 static bool
IsThereEnoughFBMemory(const display_mode * mode,uint32 bitsPerPixel)18 IsThereEnoughFBMemory(const display_mode* mode, uint32 bitsPerPixel)
19 {
20 	// Test if there is enough Frame Buffer memory for the mode and color depth
21 	// specified by the caller, and return true if there is sufficient memory.
22 
23 	uint32 maxWidth = mode->virtual_width;
24 	if (mode->timing.h_display > maxWidth)
25 		maxWidth = mode->timing.h_display;
26 
27 	uint32 maxHeight = mode->virtual_height;
28 	if (mode->timing.v_display > maxHeight)
29 		maxHeight = mode->timing.v_display;
30 
31 	uint32 bytesPerPixel = (bitsPerPixel + 7) / 8;
32 
33 	return (maxWidth * maxHeight * bytesPerPixel
34 		< gInfo.sharedInfo->maxFrameBufferSize);
35 }
36 
37 
38 bool
IsModeUsable(const display_mode * mode)39 IsModeUsable(const display_mode* mode)
40 {
41 	// Test if the display mode is usable by the current video chip.  That is,
42 	// does the chip have enough memory for the mode and is the pixel clock
43 	// within the chips allowable range, etc.
44 	//
45 	// Return true if the mode is usable.
46 
47 	SharedInfo& si = *gInfo.sharedInfo;
48 	uint8 bitsPerPixel;
49 
50 	if (!TDFX_GetColorSpaceParams(mode->space, bitsPerPixel))
51 		return false;
52 
53 	// Is there enough frame buffer memory to handle the mode?
54 
55 	if (!IsThereEnoughFBMemory(mode, bitsPerPixel))
56 		return false;
57 
58 	if (mode->timing.pixel_clock > si.maxPixelClock)
59 		return false;
60 
61 	// Is the color space supported?
62 
63 	bool colorSpaceSupported = false;
64 	for (uint32 j = 0; j < si.colorSpaceCount; j++) {
65 		if (mode->space == uint32(si.colorSpaces[j])) {
66 			colorSpaceSupported = true;
67 			break;
68 		}
69 	}
70 
71 	if (!colorSpaceSupported)
72 		return false;
73 
74 	// In clock doubled mode for 3DFX chips, widths must be divisible by 16
75 	// instead of 8.
76 
77 	if ((mode->timing.pixel_clock > si.maxPixelClock / 2)
78 			&& (mode->timing.h_display % 16) != 0)
79 		return false;
80 
81 	// Reject modes with a width of 640 and a height < 480 since they do not
82 	// work properly with the 3DFX chipsets.
83 
84 	if (mode->timing.h_display == 640 && mode->timing.v_display < 480)
85 		return false;
86 
87 	return true;
88 }
89 
90 
91 status_t
CreateModeList(bool (* checkMode)(const display_mode * mode))92 CreateModeList(bool (*checkMode)(const display_mode* mode))
93 {
94 	SharedInfo& si = *gInfo.sharedInfo;
95 
96 	// Obtain EDID info which is needed for for building the mode list.  If
97 	// function TDFX_GetEdidInfo() fails, no attempt is made to read the EDID
98 	// info from the video BIOS since that always failed when attempted during
99 	// the development of this driver.
100 
101 	si.bHaveEDID = TDFX_GetEdidInfo(si.edidInfo);
102 
103 	if (si.bHaveEDID) {
104 #ifdef ENABLE_DEBUG_TRACE
105 		edid_dump(&(si.edidInfo));
106 #endif
107 	} else {
108 		TRACE("CreateModeList(); Unable to get EDID info\n");
109 	}
110 
111 	display_mode* list;
112 	uint32 count = 0;
113 	area_id listArea;
114 
115 	listArea = create_display_modes("3DFX modes",
116 		si.bHaveEDID ? &si.edidInfo : NULL,
117 		NULL, 0, si.colorSpaces, si.colorSpaceCount,
118 		(check_display_mode_hook)checkMode, &list, &count);
119 
120 	if (listArea < 0)
121 		return listArea;		// listArea has error code
122 
123 	si.modeArea = gInfo.modeListArea = listArea;
124 	si.modeCount = count;
125 	gInfo.modeList = list;
126 
127 	return B_OK;
128 }
129 
130 
131 status_t
ProposeDisplayMode(display_mode * target,const display_mode * low,const display_mode * high)132 ProposeDisplayMode(display_mode* target, const display_mode* low,
133 	const display_mode* high)
134 {
135 	(void)low;		// avoid compiler warning for unused arg
136 	(void)high;		// avoid compiler warning for unused arg
137 
138 	SharedInfo& si = *gInfo.sharedInfo;
139 
140 	TRACE("ProposeDisplayMode()  %dx%d, pixel clock: %d kHz, space: 0x%X\n",
141 		target->timing.h_display, target->timing.v_display,
142 		target->timing.pixel_clock, target->space);
143 
144 	// In clock doubled mode for 3DFX chips, widths must be divisible by 16
145 	// instead of 8.
146 
147 	if ((target->timing.pixel_clock > si.maxPixelClock / 2)
148 			&& (target->timing.h_display % 16) != 0)
149 		return B_BAD_VALUE;
150 
151 	// Search the mode list for the specified mode.
152 
153 	uint32 modeCount = si.modeCount;
154 
155 	for (uint32 j = 0; j < modeCount; j++) {
156 		display_mode& mode = gInfo.modeList[j];
157 
158 		if (target->timing.h_display == mode.timing.h_display
159 			&& target->timing.v_display == mode.timing.v_display
160 			&& target->space == mode.space)
161 			return B_OK;	// mode found in list
162 	}
163 
164 	return B_BAD_VALUE;		// mode not found in list
165 }
166 
167 
168 status_t
SetDisplayMode(display_mode * pMode)169 SetDisplayMode(display_mode* pMode)
170 {
171 	// First validate the mode, then call a function to set the registers.
172 
173 	TRACE("SetDisplayMode() begin\n");
174 
175 	SharedInfo& si = *gInfo.sharedInfo;
176 	DisplayModeEx mode;
177 	(display_mode&)mode = *pMode;
178 
179 	if (!TDFX_GetColorSpaceParams(mode.space, mode.bitsPerPixel))
180 		return B_BAD_VALUE;
181 
182 	if (ProposeDisplayMode(&mode, pMode, pMode) != B_OK)
183 		return B_BAD_VALUE;
184 
185 	mode.bytesPerPixel = (mode.bitsPerPixel + 7) / 8;
186 	mode.bytesPerRow = mode.timing.h_display * mode.bytesPerPixel;
187 
188 	// Is there enough frame buffer memory for this mode?
189 
190 	if ( ! IsThereEnoughFBMemory(&mode, mode.bitsPerPixel))
191 		return B_NO_MEMORY;
192 
193 	TRACE("Set display mode: %dx%d  virtual size: %dx%d  "
194 		"color depth: %d bits/pixel\n",
195 		mode.timing.h_display, mode.timing.v_display,
196 		mode.virtual_width, mode.virtual_height, mode.bitsPerPixel);
197 
198 	TRACE("   mode timing: %d  %d %d %d %d  %d %d %d %d\n",
199 		mode.timing.pixel_clock,
200 		mode.timing.h_display,
201 		mode.timing.h_sync_start, mode.timing.h_sync_end,
202 		mode.timing.h_total,
203 		mode.timing.v_display,
204 		mode.timing.v_sync_start, mode.timing.v_sync_end,
205 		mode.timing.v_total);
206 
207 	TRACE("   mode hFreq: %.1f kHz  vFreq: %.1f Hz  %chSync %cvSync\n",
208 		double(mode.timing.pixel_clock) / mode.timing.h_total,
209 		((double(mode.timing.pixel_clock) / mode.timing.h_total) * 1000.0)
210 		/ mode.timing.v_total,
211 		(mode.timing.flags & B_POSITIVE_HSYNC) ? '+' : '-',
212 		(mode.timing.flags & B_POSITIVE_VSYNC) ? '+' : '-');
213 
214 	status_t status = TDFX_SetDisplayMode(mode);
215 	if (status != B_OK) {
216 		TRACE("SetDisplayMode() failed;  status 0x%x\n", status);
217 		return status;
218 	}
219 
220 	si.displayMode = mode;
221 
222 	TRACE("SetDisplayMode() done\n");
223 	return B_OK;
224 }
225 
226 
227 status_t
MoveDisplay(uint16 horizontalStart,uint16 verticalStart)228 MoveDisplay(uint16 horizontalStart, uint16 verticalStart)
229 {
230 	// Set which pixel of the virtual frame buffer will show up in the
231 	// top left corner of the display device.	Used for page-flipping
232 	// games and virtual desktops.
233 
234 	DisplayModeEx& mode = gInfo.sharedInfo->displayMode;
235 
236 	if (mode.timing.h_display + horizontalStart > mode.virtual_width
237 		|| mode.timing.v_display + verticalStart > mode.virtual_height)
238 		return B_ERROR;
239 
240 	mode.h_display_start = horizontalStart;
241 	mode.v_display_start = verticalStart;
242 
243 	TDFX_AdjustFrame(mode);
244 	return B_OK;
245 }
246 
247 
248 uint32
AccelerantModeCount(void)249 AccelerantModeCount(void)
250 {
251 	// Return the number of display modes in the mode list.
252 
253 	return gInfo.sharedInfo->modeCount;
254 }
255 
256 
257 status_t
GetModeList(display_mode * dmList)258 GetModeList(display_mode* dmList)
259 {
260 	// Copy the list of supported video modes to the location pointed at
261 	// by dmList.
262 
263 	memcpy(dmList, gInfo.modeList,
264 		gInfo.sharedInfo->modeCount * sizeof(display_mode));
265 	return B_OK;
266 }
267 
268 
269 status_t
GetDisplayMode(display_mode * current_mode)270 GetDisplayMode(display_mode* current_mode)
271 {
272 	*current_mode = gInfo.sharedInfo->displayMode;	// return current display mode
273 	return B_OK;
274 }
275 
276 
277 status_t
GetFrameBufferConfig(frame_buffer_config * pFBC)278 GetFrameBufferConfig(frame_buffer_config* pFBC)
279 {
280 	SharedInfo& si = *gInfo.sharedInfo;
281 
282 	pFBC->frame_buffer = (void*)((addr_t)si.videoMemAddr
283 		+ si.frameBufferOffset);
284 	pFBC->frame_buffer_dma = (void*)((addr_t)si.videoMemPCI
285 		+ si.frameBufferOffset);
286 	pFBC->bytes_per_row = si.displayMode.virtual_width
287 		* si.displayMode.bytesPerPixel;
288 
289 	return B_OK;
290 }
291 
292 
293 status_t
GetPixelClockLimits(display_mode * mode,uint32 * low,uint32 * high)294 GetPixelClockLimits(display_mode* mode, uint32* low, uint32* high)
295 {
296 	// Return the maximum and minium pixel clock limits for the specified mode.
297 
298 	SharedInfo& si = *gInfo.sharedInfo;
299 	uint8 bitsPerPixel;
300 
301 	if (!TDFX_GetColorSpaceParams(mode->space, bitsPerPixel))
302 		return B_ERROR;
303 
304 	if (low != NULL) {
305 		// lower limit of about 48Hz vertical refresh
306 		uint32 totalClocks = (uint32)mode->timing.h_total
307 							* (uint32)mode->timing.v_total;
308 		uint32 lowClock = (totalClocks * 48L) / 1000L;
309 		if (lowClock > si.maxPixelClock)
310 			return B_ERROR;
311 
312 		*low = lowClock;
313 	}
314 
315 	if (high != NULL) {
316 		// In clock doubled mode for 3DFX chips, widths must be divisible by 16
317 		// instead of 8.  If width is not divisible by 16, limit pixel clock to
318 		// half of max pixel clock since divisor is 8 if pixel clock is < half
319 		// max pixel clock.
320 
321 		if ((mode->timing.h_display % 16) != 0)
322 			*high = si.maxPixelClock / 2;
323 		else
324 			*high = si.maxPixelClock;
325 	}
326 
327 	return B_OK;
328 }
329 
330 
331 #ifdef __HAIKU__
332 
333 status_t
GetEdidInfo(void * info,size_t size,uint32 * _version)334 GetEdidInfo(void* info, size_t size, uint32* _version)
335 {
336 	SharedInfo& si = *gInfo.sharedInfo;
337 
338 	if ( ! si.bHaveEDID)
339 		return B_ERROR;
340 
341 	if (size < sizeof(struct edid1_info))
342 		return B_BUFFER_OVERFLOW;
343 
344 	memcpy(info, &si.edidInfo, sizeof(struct edid1_info));
345 	*_version = EDID_VERSION_1;
346 	return B_OK;
347 }
348 
349 #endif	// __HAIKU__
350