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