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
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	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
85status_t
86CreateModeList(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
136status_t
137ProposeDisplayMode(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
164status_t
165SetDisplayMode(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
224status_t
225MoveDisplay(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
245uint32
246AccelerantModeCount(void)
247{
248	// Return the number of display modes in the mode list.
249
250	return gInfo.sharedInfo->modeCount;
251}
252
253
254status_t
255GetModeList(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
266status_t
267GetDisplayMode(display_mode* current_mode)
268{
269	*current_mode = gInfo.sharedInfo->displayMode; // current display mode
270	return B_OK;
271}
272
273
274status_t
275GetFrameBufferConfig(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
288status_t
289GetPixelClockLimits(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__
318status_t
319GetEdidInfo(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