xref: /haiku/src/add-ons/accelerants/ati/mode.cpp (revision 08acbdd994bd426267992146a5f0569b2c801fbf)
1 /*
2 	Copyright 2007-2009 Haiku, Inc.  All rights reserved.
3 	Distributed under the terms of the MIT license.
4 
5 	Authors:
6 	Gerald Zajac 2007-2009
7 */
8 
9 #include "accelerant.h"
10 
11 #include <string.h>
12 #include <unistd.h>
13 
14 #include <create_display_modes.h>		// common accelerant header file
15 
16 
17 static display_mode*
FindDisplayMode(int width,int height,int refreshRate,uint32 colorDepth)18 FindDisplayMode(int width, int height, int refreshRate, uint32 colorDepth)
19 {
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.
25 	//
26 	// If successful, return a pointer to the selected display_mode object;
27 	// else return NULL.
28 
29 	display_mode* selectedMode = NULL;
30 	uint32 modeCount = gInfo.sharedInfo->modeCount;
31 
32 	for (uint32 j = 0; j < modeCount; j++) {
33 		display_mode& mode = gInfo.modeList[j];
34 
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;
45 				}
46 			}
47 		}
48 	}
49 
50 	return selectedMode;
51 }
52 
53 
54 
55 static bool
IsThereEnoughFBMemory(const display_mode * mode,uint32 bitsPerPixel)56 IsThereEnoughFBMemory(const display_mode* mode, uint32 bitsPerPixel)
57 {
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.
60 
61 	uint32 maxWidth = mode->virtual_width;
62 	if (mode->timing.h_display > maxWidth)
63 		maxWidth = mode->timing.h_display;
64 
65 	uint32 maxHeight = mode->virtual_height;
66 	if (mode->timing.v_display > maxHeight)
67 		maxHeight = mode->timing.v_display;
68 
69 	uint32 bytesPerPixel = (bitsPerPixel + 7) / 8;
70 
71 	return (maxWidth * maxHeight * bytesPerPixel < gInfo.sharedInfo->maxFrameBufferSize);
72 }
73 
74 
75 
76 uint16
GetVesaModeNumber(const display_mode & mode,uint8 bitsPerPixel)77 GetVesaModeNumber(const display_mode& mode, uint8 bitsPerPixel)
78 {
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.
81 
82 	SharedInfo& si = *gInfo.sharedInfo;
83 
84 	VesaMode* vesaModeTable = (VesaMode*)((uint8*)&si + si.vesaModeTableOffset);
85 
86 	for (uint32 j = 0; j < si.vesaModeCount; j++) {
87 		VesaMode& vesaMode = vesaModeTable[j];
88 
89 		if (vesaMode.width == mode.timing.h_display
90 			&& vesaMode.height == mode.timing.v_display
91 			&& vesaMode.bitsPerPixel == bitsPerPixel)
92 			return vesaMode.mode;
93 	}
94 
95 	return 0;		// matching VESA mode not found
96 }
97 
98 
99 
100 bool
IsModeUsable(const display_mode * mode)101 IsModeUsable(const display_mode* mode)
102 {
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.
106 	//
107 	// Return true if the mode is usable.
108 
109 	SharedInfo& si = *gInfo.sharedInfo;
110 	uint8 bitsPerPixel;
111 	uint32 maxPixelClock;
112 
113 	if (!gInfo.GetColorSpaceParams(mode->space, bitsPerPixel, maxPixelClock))
114 		return false;
115 
116 	// Is there enough frame buffer memory to handle the mode?
117 
118 	if (!IsThereEnoughFBMemory(mode, bitsPerPixel))
119 		return false;
120 
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.
124 
125 		if (mode->timing.pixel_clock > maxPixelClock)
126 			return false;
127 
128 		// Is the color space supported?
129 
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;
135 			}
136 		}
137 
138 		if (!colorSpaceSupported)
139 			return false;
140 
141 		// Reject modes with a width of 640 and a height < 480 since they do not
142 		// work properly with the ATI chipsets.
143 
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.
149 
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.
153 
154 		if (si.chipType == MACH64_MOBILITY && mode->timing.h_display == 640
155 				&& mode->timing.v_display == 350)
156 			return false;
157 
158 		// Search VESA mode table for matching mode.
159 
160 		if (GetVesaModeNumber(*mode, bitsPerPixel) == 0)
161 			return false;
162 	}
163 
164 	return true;
165 }
166 
167 
168 status_t
CreateModeList(bool (* checkMode)(const display_mode * mode))169 CreateModeList(bool (*checkMode)(const display_mode* mode))
170 {
171 	SharedInfo& si = *gInfo.sharedInfo;
172 
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.
179 
180 	si.bHaveEDID = false;
181 
182 	if (si.displayType == MT_VGA && !si.bHaveEDID) {
183 		edid1_raw rawEdid;	// raw EDID info to obtain
184 
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;
193 			}
194 		}
195 
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");
202 		}
203 	}
204 
205 	display_mode* list;
206 	uint32 count = 0;
207 	area_id listArea;
208 
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);
213 
214 	if (listArea < 0)
215 		return listArea;		// listArea has error code
216 
217 	si.modeArea = gInfo.modeListArea = listArea;
218 	si.modeCount = count;
219 	gInfo.modeList = list;
220 
221 	return B_OK;
222 }
223 
224 
225 
226 status_t
ProposeDisplayMode(display_mode * target,const display_mode * low,const display_mode * high)227 ProposeDisplayMode(display_mode *target, const display_mode *low,
228 	const display_mode *high)
229 {
230 	(void)low;		// avoid compiler warning for unused arg
231 	(void)high;		// avoid compiler warning for unused arg
232 
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);
236 
237 	// Search the mode list for the specified mode.
238 
239 	uint32 modeCount = gInfo.sharedInfo->modeCount;
240 
241 	for (uint32 j = 0; j < modeCount; j++) {
242 		display_mode& mode = gInfo.modeList[j];
243 
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
248 	}
249 
250 	return B_BAD_VALUE;		// mode not found in list
251 }
252 
253 
254 status_t
SetDisplayMode(display_mode * pMode)255 SetDisplayMode(display_mode* pMode)
256 {
257 	// First validate the mode, then call a function to set the registers.
258 
259 	TRACE("SetDisplayMode() begin\n");
260 
261 	SharedInfo& si = *gInfo.sharedInfo;
262 	DisplayModeEx mode;
263 	(display_mode&)mode = *pMode;
264 
265 	uint32 maxPixelClock;
266 	if ( ! gInfo.GetColorSpaceParams(mode.space, mode.bitsPerPixel, maxPixelClock))
267 		return B_BAD_VALUE;
268 
269 	if (ProposeDisplayMode(&mode, pMode, pMode) != B_OK)
270 		return B_BAD_VALUE;
271 
272 	int bytesPerPixel = (mode.bitsPerPixel + 7) / 8;
273 	mode.bytesPerRow = mode.timing.h_display * bytesPerPixel;
274 
275 	// Is there enough frame buffer memory for this mode?
276 
277 	if ( ! IsThereEnoughFBMemory(&mode, mode.bitsPerPixel))
278 		return B_NO_MEMORY;
279 
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);
283 
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);
293 
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) ? '+' : '-');
300 	}
301 
302 	status_t status = gInfo.SetDisplayMode(mode);
303 	if (status != B_OK) {
304 		TRACE("SetDisplayMode() failed;  status 0x%x\n", status);
305 		return status;
306 	}
307 
308 	si.displayMode = mode;
309 
310 	TRACE("SetDisplayMode() done\n");
311 	return B_OK;
312 }
313 
314 
315 
316 status_t
MoveDisplay(uint16 horizontalStart,uint16 verticalStart)317 MoveDisplay(uint16 horizontalStart, uint16 verticalStart)
318 {
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.
322 
323 	DisplayModeEx& mode = gInfo.sharedInfo->displayMode;
324 
325 	if (mode.timing.h_display + horizontalStart > mode.virtual_width
326 		|| mode.timing.v_display + verticalStart > mode.virtual_height)
327 		return B_ERROR;
328 
329 	mode.h_display_start = horizontalStart;
330 	mode.v_display_start = verticalStart;
331 
332 	gInfo.AdjustFrame(mode);
333 	return B_OK;
334 }
335 
336 
337 uint32
AccelerantModeCount(void)338 AccelerantModeCount(void)
339 {
340 	// Return the number of display modes in the mode list.
341 
342 	return gInfo.sharedInfo->modeCount;
343 }
344 
345 
346 status_t
GetModeList(display_mode * dmList)347 GetModeList(display_mode* dmList)
348 {
349 	// Copy the list of supported video modes to the location pointed at
350 	// by dmList.
351 
352 	memcpy(dmList, gInfo.modeList, gInfo.sharedInfo->modeCount * sizeof(display_mode));
353 	return B_OK;
354 }
355 
356 
357 status_t
GetDisplayMode(display_mode * current_mode)358 GetDisplayMode(display_mode* current_mode)
359 {
360 	*current_mode = gInfo.sharedInfo->displayMode;	// return current display mode
361 	return B_OK;
362 }
363 
364 
365 status_t
GetFrameBufferConfig(frame_buffer_config * pFBC)366 GetFrameBufferConfig(frame_buffer_config* pFBC)
367 {
368 	SharedInfo& si = *gInfo.sharedInfo;
369 
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;
374 
375 	return B_OK;
376 }
377 
378 
379 status_t
GetPixelClockLimits(display_mode * mode,uint32 * low,uint32 * high)380 GetPixelClockLimits(display_mode* mode, uint32* low, uint32* high)
381 {
382 	// Return the maximum and minium pixel clock limits for the specified mode.
383 
384 	uint8 bitsPerPixel;
385 	uint32 maxPixelClock;
386 
387 	if ( ! gInfo.GetColorSpaceParams(mode->space, bitsPerPixel, maxPixelClock))
388 		return B_ERROR;
389 
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;
396 
397 		*low = lowClock;
398 	}
399 
400 	if (high != NULL)
401 		*high = maxPixelClock;
402 
403 	return B_OK;
404 }
405 
406 
407 status_t
GetTimingConstraints(display_timing_constraints * constraints)408 GetTimingConstraints(display_timing_constraints *constraints)
409 {
410 	(void)constraints;		// avoid compiler warning for unused arg
411 
412 	return B_ERROR;
413 }
414 
415 
416 status_t
GetPreferredDisplayMode(display_mode * preferredMode)417 GetPreferredDisplayMode(display_mode* preferredMode)
418 {
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.
421 
422 	SharedInfo& si = *gInfo.sharedInfo;
423 
424 	if (si.displayType == MT_LAPTOP) {
425 		display_mode* mode = FindDisplayMode(si.panelX, si.panelY, 60, 0);
426 
427 		if (mode != NULL) {
428 			*preferredMode = *mode;
429 			return B_OK;
430 		}
431 	}
432 
433 	return B_ERROR;
434 }
435 
436 
437 status_t
GetEdidInfo(void * info,size_t size,uint32 * _version)438 GetEdidInfo(void* info, size_t size, uint32* _version)
439 {
440 	SharedInfo& si = *gInfo.sharedInfo;
441 
442 	if ( ! si.bHaveEDID)
443 		return B_ERROR;
444 
445 	if (size < sizeof(struct edid1_info))
446 		return B_BUFFER_OVERFLOW;
447 
448 	memcpy(info, &si.edidInfo, sizeof(struct edid1_info));
449 	*_version = EDID_VERSION_1;
450 	return B_OK;
451 }
452