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
17static bool
18IsThereEnoughFBMemory(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
38bool
39IsModeUsable(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
91status_t
92CreateModeList(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
131status_t
132ProposeDisplayMode(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
168status_t
169SetDisplayMode(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
227status_t
228MoveDisplay(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
248uint32
249AccelerantModeCount(void)
250{
251	// Return the number of display modes in the mode list.
252
253	return gInfo.sharedInfo->modeCount;
254}
255
256
257status_t
258GetModeList(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
269status_t
270GetDisplayMode(display_mode* current_mode)
271{
272	*current_mode = gInfo.sharedInfo->displayMode;	// return current display mode
273	return B_OK;
274}
275
276
277status_t
278GetFrameBufferConfig(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
293status_t
294GetPixelClockLimits(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
333status_t
334GetEdidInfo(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