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