BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / accelerants / ati / mode.cpp
blobdd101165d0c0872716fb20274ec22b3efe8736eb
1 /*
2 Copyright 2007-2009 Haiku, Inc. All rights reserved.
3 Distributed under the terms of the MIT license.
5 Authors:
6 Gerald Zajac 2007-2009
7 */
9 #include "accelerant.h"
11 #include <string.h>
12 #include <unistd.h>
14 #include <create_display_modes.h> // common accelerant header file
17 static display_mode*
18 FindDisplayMode(int width, int height, int refreshRate, uint32 colorDepth)
20 // Search the mode list for the mode with specified width, height,
21 // refresh rate, and color depth. If argument colorDepth is zero, this
22 // function will search for a mode satisfying the other 3 arguments, and
23 // if more than one matching mode is found, the one with the greatest color
24 // depth will be selected.
26 // If successful, return a pointer to the selected display_mode object;
27 // else return NULL.
29 display_mode* selectedMode = NULL;
30 uint32 modeCount = gInfo.sharedInfo->modeCount;
32 for (uint32 j = 0; j < modeCount; j++) {
33 display_mode& mode = gInfo.modeList[j];
35 if (mode.timing.h_display == width && mode.timing.v_display == height) {
36 int modeRefreshRate = int(((mode.timing.pixel_clock * 1000.0 /
37 mode.timing.h_total) / mode.timing.v_total) + 0.5);
38 if (modeRefreshRate == refreshRate) {
39 if (colorDepth == 0) {
40 if (selectedMode == NULL || mode.space > selectedMode->space)
41 selectedMode = &mode;
42 } else {
43 if (mode.space == colorDepth)
44 return &mode;
50 return selectedMode;
55 static bool
56 IsThereEnoughFBMemory(const display_mode* mode, uint32 bitsPerPixel)
58 // Test if there is enough Frame Buffer memory for the mode and color depth
59 // specified by the caller, and return true if there is sufficient memory.
61 uint32 maxWidth = mode->virtual_width;
62 if (mode->timing.h_display > maxWidth)
63 maxWidth = mode->timing.h_display;
65 uint32 maxHeight = mode->virtual_height;
66 if (mode->timing.v_display > maxHeight)
67 maxHeight = mode->timing.v_display;
69 uint32 bytesPerPixel = (bitsPerPixel + 7) / 8;
71 return (maxWidth * maxHeight * bytesPerPixel < gInfo.sharedInfo->maxFrameBufferSize);
76 uint16
77 GetVesaModeNumber(const display_mode& mode, uint8 bitsPerPixel)
79 // Search VESA mode table for a matching mode, and return the VESA mode
80 // number if a match is found; else return 0 if no match is found.
82 SharedInfo& si = *gInfo.sharedInfo;
84 VesaMode* vesaModeTable = (VesaMode*)((uint8*)&si + si.vesaModeTableOffset);
86 for (uint32 j = 0; j < si.vesaModeCount; j++) {
87 VesaMode& vesaMode = vesaModeTable[j];
89 if (vesaMode.width == mode.timing.h_display
90 && vesaMode.height == mode.timing.v_display
91 && vesaMode.bitsPerPixel == bitsPerPixel)
92 return vesaMode.mode;
95 return 0; // matching VESA mode not found
100 bool
101 IsModeUsable(const display_mode* mode)
103 // Test if the display mode is usable by the current video chip. That is,
104 // does the chip have enough memory for the mode and is the pixel clock
105 // within the chips allowable range, etc.
107 // Return true if the mode is usable.
109 SharedInfo& si = *gInfo.sharedInfo;
110 uint8 bitsPerPixel;
111 uint32 maxPixelClock;
113 if (!gInfo.GetColorSpaceParams(mode->space, bitsPerPixel, maxPixelClock))
114 return false;
116 // Is there enough frame buffer memory to handle the mode?
118 if (!IsThereEnoughFBMemory(mode, bitsPerPixel))
119 return false;
121 if (si.displayType == MT_VGA) {
122 // Test if mode is usable for a chip that is connected to a monitor
123 // via an analog VGA connector.
125 if (mode->timing.pixel_clock > maxPixelClock)
126 return false;
128 // Is the color space supported?
130 bool colorSpaceSupported = false;
131 for (uint32 j = 0; j < si.colorSpaceCount; j++) {
132 if (mode->space == uint32(si.colorSpaces[j])) {
133 colorSpaceSupported = true;
134 break;
138 if (!colorSpaceSupported)
139 return false;
141 // Reject modes with a width of 640 and a height < 480 since they do not
142 // work properly with the ATI chipsets.
144 if (mode->timing.h_display == 640 && mode->timing.v_display < 480)
145 return false;
146 } else {
147 // Test if mode is usable for a chip that is connected to a laptop LCD
148 // display or a monitor via a DVI interface.
150 // If chip is a Mach64 Mobility chip exclude 640x350 resolution since
151 // that resolution can not be set without a failure in the VESA set mode
152 // function.
154 if (si.chipType == MACH64_MOBILITY && mode->timing.h_display == 640
155 && mode->timing.v_display == 350)
156 return false;
158 // Search VESA mode table for matching mode.
160 if (GetVesaModeNumber(*mode, bitsPerPixel) == 0)
161 return false;
164 return true;
168 status_t
169 CreateModeList(bool (*checkMode)(const display_mode* mode))
171 SharedInfo& si = *gInfo.sharedInfo;
173 // Obtain EDID info which is needed for for building the mode list.
174 // However, if a laptop's LCD display is active, bypass getting the EDID
175 // info since it is not needed, and if the external display supports only
176 // resolutions smaller than the size of the laptop LCD display, it would
177 // unnecessarily restrict size of the resolutions that could be set for
178 // laptop LCD display.
180 si.bHaveEDID = false;
182 if (si.displayType == MT_VGA && !si.bHaveEDID) {
183 edid1_raw rawEdid; // raw EDID info to obtain
185 if (ioctl(gInfo.deviceFileDesc, ATI_GET_EDID, &rawEdid,
186 sizeof(rawEdid)) == B_OK) {
187 if (rawEdid.version.version != 1 || rawEdid.version.revision > 4) {
188 TRACE("CreateModeList(); EDID version %d.%d out of range\n",
189 rawEdid.version.version, rawEdid.version.revision);
190 } else {
191 edid_decode(&si.edidInfo, &rawEdid); // decode & save EDID info
192 si.bHaveEDID = true;
196 if (si.bHaveEDID) {
197 #ifdef ENABLE_DEBUG_TRACE
198 edid_dump(&(si.edidInfo));
199 #endif
200 } else {
201 TRACE("CreateModeList(); Unable to get EDID info\n");
205 display_mode* list;
206 uint32 count = 0;
207 area_id listArea;
209 listArea = create_display_modes("ATI modes",
210 si.bHaveEDID ? &si.edidInfo : NULL,
211 NULL, 0, si.colorSpaces, si.colorSpaceCount,
212 (check_display_mode_hook)checkMode, &list, &count);
214 if (listArea < 0)
215 return listArea; // listArea has error code
217 si.modeArea = gInfo.modeListArea = listArea;
218 si.modeCount = count;
219 gInfo.modeList = list;
221 return B_OK;
226 status_t
227 ProposeDisplayMode(display_mode *target, const display_mode *low,
228 const display_mode *high)
230 (void)low; // avoid compiler warning for unused arg
231 (void)high; // avoid compiler warning for unused arg
233 TRACE("ProposeDisplayMode() %dx%d, pixel clock: %d kHz, space: 0x%X\n",
234 target->timing.h_display, target->timing.v_display,
235 target->timing.pixel_clock, target->space);
237 // Search the mode list for the specified mode.
239 uint32 modeCount = gInfo.sharedInfo->modeCount;
241 for (uint32 j = 0; j < modeCount; j++) {
242 display_mode& mode = gInfo.modeList[j];
244 if (target->timing.h_display == mode.timing.h_display
245 && target->timing.v_display == mode.timing.v_display
246 && target->space == mode.space)
247 return B_OK; // mode found in list
250 return B_BAD_VALUE; // mode not found in list
254 status_t
255 SetDisplayMode(display_mode* pMode)
257 // First validate the mode, then call a function to set the registers.
259 TRACE("SetDisplayMode() begin\n");
261 SharedInfo& si = *gInfo.sharedInfo;
262 DisplayModeEx mode;
263 (display_mode&)mode = *pMode;
265 uint32 maxPixelClock;
266 if ( ! gInfo.GetColorSpaceParams(mode.space, mode.bitsPerPixel, maxPixelClock))
267 return B_BAD_VALUE;
269 if (ProposeDisplayMode(&mode, pMode, pMode) != B_OK)
270 return B_BAD_VALUE;
272 int bytesPerPixel = (mode.bitsPerPixel + 7) / 8;
273 mode.bytesPerRow = mode.timing.h_display * bytesPerPixel;
275 // Is there enough frame buffer memory for this mode?
277 if ( ! IsThereEnoughFBMemory(&mode, mode.bitsPerPixel))
278 return B_NO_MEMORY;
280 TRACE("Set display mode: %dx%d virtual size: %dx%d color depth: %d bits/pixel\n",
281 mode.timing.h_display, mode.timing.v_display,
282 mode.virtual_width, mode.virtual_height, mode.bitsPerPixel);
284 if (si.displayType == MT_VGA) {
285 TRACE(" mode timing: %d %d %d %d %d %d %d %d %d\n",
286 mode.timing.pixel_clock,
287 mode.timing.h_display,
288 mode.timing.h_sync_start, mode.timing.h_sync_end,
289 mode.timing.h_total,
290 mode.timing.v_display,
291 mode.timing.v_sync_start, mode.timing.v_sync_end,
292 mode.timing.v_total);
294 TRACE(" mode hFreq: %.1f kHz vFreq: %.1f Hz %chSync %cvSync\n",
295 double(mode.timing.pixel_clock) / mode.timing.h_total,
296 ((double(mode.timing.pixel_clock) / mode.timing.h_total) * 1000.0)
297 / mode.timing.v_total,
298 (mode.timing.flags & B_POSITIVE_HSYNC) ? '+' : '-',
299 (mode.timing.flags & B_POSITIVE_VSYNC) ? '+' : '-');
302 status_t status = gInfo.SetDisplayMode(mode);
303 if (status != B_OK) {
304 TRACE("SetDisplayMode() failed; status 0x%x\n", status);
305 return status;
308 si.displayMode = mode;
310 TRACE("SetDisplayMode() done\n");
311 return B_OK;
316 status_t
317 MoveDisplay(uint16 horizontalStart, uint16 verticalStart)
319 // Set which pixel of the virtual frame buffer will show up in the
320 // top left corner of the display device. Used for page-flipping
321 // games and virtual desktops.
323 DisplayModeEx& mode = gInfo.sharedInfo->displayMode;
325 if (mode.timing.h_display + horizontalStart > mode.virtual_width
326 || mode.timing.v_display + verticalStart > mode.virtual_height)
327 return B_ERROR;
329 mode.h_display_start = horizontalStart;
330 mode.v_display_start = verticalStart;
332 gInfo.AdjustFrame(mode);
333 return B_OK;
337 uint32
338 AccelerantModeCount(void)
340 // Return the number of display modes in the mode list.
342 return gInfo.sharedInfo->modeCount;
346 status_t
347 GetModeList(display_mode* dmList)
349 // Copy the list of supported video modes to the location pointed at
350 // by dmList.
352 memcpy(dmList, gInfo.modeList, gInfo.sharedInfo->modeCount * sizeof(display_mode));
353 return B_OK;
357 status_t
358 GetDisplayMode(display_mode* current_mode)
360 *current_mode = gInfo.sharedInfo->displayMode; // return current display mode
361 return B_OK;
365 status_t
366 GetFrameBufferConfig(frame_buffer_config* pFBC)
368 SharedInfo& si = *gInfo.sharedInfo;
370 pFBC->frame_buffer = (void*)((addr_t)si.videoMemAddr + si.frameBufferOffset);
371 pFBC->frame_buffer_dma = (void*)((addr_t)si.videoMemPCI + si.frameBufferOffset);
372 uint32 bytesPerPixel = (si.displayMode.bitsPerPixel + 7) / 8;
373 pFBC->bytes_per_row = si.displayMode.virtual_width * bytesPerPixel;
375 return B_OK;
379 status_t
380 GetPixelClockLimits(display_mode* mode, uint32* low, uint32* high)
382 // Return the maximum and minium pixel clock limits for the specified mode.
384 uint8 bitsPerPixel;
385 uint32 maxPixelClock;
387 if ( ! gInfo.GetColorSpaceParams(mode->space, bitsPerPixel, maxPixelClock))
388 return B_ERROR;
390 if (low != NULL) {
391 // lower limit of about 48Hz vertical refresh
392 uint32 totalClocks = (uint32)mode->timing.h_total * (uint32)mode->timing.v_total;
393 uint32 lowClock = (totalClocks * 48L) / 1000L;
394 if (lowClock > maxPixelClock)
395 return B_ERROR;
397 *low = lowClock;
400 if (high != NULL)
401 *high = maxPixelClock;
403 return B_OK;
407 status_t
408 GetTimingConstraints(display_timing_constraints *constraints)
410 (void)constraints; // avoid compiler warning for unused arg
412 return B_ERROR;
416 status_t
417 GetPreferredDisplayMode(display_mode* preferredMode)
419 // If the chip is connected to a laptop LCD panel, find the mode with
420 // matching width and height, 60 Hz refresh rate, and greatest color depth.
422 SharedInfo& si = *gInfo.sharedInfo;
424 if (si.displayType == MT_LAPTOP) {
425 display_mode* mode = FindDisplayMode(si.panelX, si.panelY, 60, 0);
427 if (mode != NULL) {
428 *preferredMode = *mode;
429 return B_OK;
433 return B_ERROR;
437 status_t
438 GetEdidInfo(void* info, size_t size, uint32* _version)
440 SharedInfo& si = *gInfo.sharedInfo;
442 if ( ! si.bHaveEDID)
443 return B_ERROR;
445 if (size < sizeof(struct edid1_info))
446 return B_BUFFER_OVERFLOW;
448 memcpy(info, &si.edidInfo, sizeof(struct edid1_info));
449 *_version = EDID_VERSION_1;
450 return B_OK;