xref: /haiku/src/add-ons/accelerants/s3/mode.cpp (revision f75a7bf508f3156d63a14f8fd77c5e0ca4d08c42)
1 /*
2 	Copyright 2007-2008 Haiku, Inc.  All rights reserved.
3 	Distributed under the terms of the MIT license.
4 
5 	Authors:
6 	Gerald Zajac 2007-2008
7 */
8 
9 #include "accel.h"
10 
11 #include <create_display_modes.h>
12 #include <string.h>
13 
14 
15 void
16 InitCrtcTimingValues(const DisplayModeEx& mode, int horzScaleFactor, uint8 crtc[],
17 					 uint8& cr3b, uint8& cr3c, uint8& cr5d, uint8& cr5e)
18 {
19 	// Initialize the timing values for CRTC registers cr00 to cr18 and cr3a,
20 	// cr3b, cr5d, and cr5e.  Note that the number following the letters 'cr'
21 	// is a hexadecimal number.  Argument crtc will contain registers cr00 to
22 	// cr18;  thus, it must contain at least 25 (0x19) elements.
23 
24 	// Normally the horizontal timing values are divided by 8;  however, some
25 	// chips require the horizontal timings to be doubled when the color depth
26 	// is 16 bpp.  The horizontal scale factor is used for this purpose.
27 
28 	int hTotal = (mode.timing.h_total * horzScaleFactor) / 8 - 5;
29 	int hDisp_e = (mode.timing.h_display * horzScaleFactor) / 8 - 1;
30 	int hSync_s = (mode.timing.h_sync_start * horzScaleFactor) / 8;
31 	int hSync_e = (mode.timing.h_sync_end * horzScaleFactor) / 8;
32 	int hBlank_s = hDisp_e;			// start of horizontal blanking
33 	int hBlank_e = hTotal;			// end of horizontal blanking
34 
35 	int vTotal = mode.timing.v_total - 2;
36 	int vDisp_e = mode.timing.v_display - 1;
37 	int vSync_s = mode.timing.v_sync_start;
38 	int vSync_e = mode.timing.v_sync_end;
39 	int vBlank_s = vDisp_e;			// start of vertical blanking
40 	int vBlank_e = vTotal;			// end of vertical blanking
41 
42 	TRACE("InitCrtcTimingValues()\n");
43 
44 	// CRTC Controller values
45 
46 	crtc[0x00] = hTotal;
47 	crtc[0x01] = hDisp_e;
48 	crtc[0x02] = hBlank_s;
49 	crtc[0x03] = (hBlank_e & 0x1f) | 0x80;
50 	crtc[0x04] = hSync_s;
51 	crtc[0x05] = ((hSync_e & 0x1f) | ((hBlank_e & 0x20) << 2));
52 	crtc[0x06] = vTotal;
53 	crtc[0x07] = (((vTotal & 0x100) >> 8)
54 		| ((vDisp_e & 0x100) >> 7)
55 		| ((vSync_s & 0x100) >> 6)
56 		| ((vBlank_s & 0x100) >> 5)
57 		| 0x10
58 		| ((vTotal & 0x200) >> 4)
59 		| ((vDisp_e & 0x200) >> 3)
60 		| ((vSync_s & 0x200) >> 2));
61 
62 	crtc[0x08] = 0x00;
63 	crtc[0x09] = ((vBlank_s & 0x200) >> 4) | 0x40;
64 	crtc[0x0a] = 0x00;
65 	crtc[0x0b] = 0x00;
66 	crtc[0x0c] = 0x00;
67 	crtc[0x0d] = 0x00;
68 	crtc[0x0e] = 0x00;
69 	crtc[0x0f] = 0x00;
70 	crtc[0x10] = vSync_s;
71 	crtc[0x11] = (vSync_e & 0x0f) | 0x20;
72 	crtc[0x12] = vDisp_e;
73 	crtc[0x13] = mode.bytesPerRow / 8;
74 	crtc[0x14] = 0x00;
75 	crtc[0x15] = vBlank_s;
76 	crtc[0x16] = vBlank_e;
77 	crtc[0x17] = 0xc3;
78 	crtc[0x18] = 0xff;
79 
80 	int i = ((hTotal & 0x100) >> 8) |
81 			((hDisp_e & 0x100) >> 7) |
82 			((hBlank_s & 0x100) >> 6) |
83 			((hSync_s & 0x100) >> 4);
84 
85 	if (hSync_e - hSync_s > 64)
86 		i |= 0x08;		// add another 64 DCLKs to blank pulse width
87 
88 	if (hSync_e - hSync_s > 32)
89 		i |= 0x20;		// add another 32 DCLKs to hsync pulse width
90 
91 	int j = (crtc[0] + ((i & 0x01) << 8) + crtc[4] + ((i & 0x10) << 4) + 1) / 2;
92 
93 	if (j - (crtc[4] + ((i & 0x10) << 4)) < 4) {
94 		if (crtc[4] + ((i & 0x10) << 4) + 4 <= crtc[0] + ((i & 0x01) << 8))
95 			j = crtc[4] + ((i & 0x10) << 4) + 4;
96 		else
97 			j = crtc[0] + ((i & 0x01) << 8) + 1;
98 	}
99 
100 	cr3b = j & 0xff;
101 	i |= (j & 0x100) >> 2;
102 	cr3c = (crtc[0] + ((i & 0x01) << 8)) / 2;
103 	cr5d = i;
104 
105 	cr5e =	((vTotal & 0x400) >> 10) |
106 			((vDisp_e & 0x400) >> 9) |
107 			((vBlank_s & 0x400) >> 8) |
108 			((vSync_s & 0x400) >> 6) | 0x40;
109 }
110 
111 
112 static display_mode*
113 FindDisplayMode(int width, int height, int refreshRate, uint32 colorDepth)
114 {
115 	// Search the mode list for the mode with specified width, height,
116 	// refresh rate, and color depth.  If argument colorDepth is zero, this
117 	// function will search for a mode satisfying the other 3 arguments, and
118 	// if more than one matching mode is found, the one with the greatest color
119 	// depth will be selected.
120 	//
121 	// If successful, return a pointer to the selected display_mode object;
122 	// else return NULL.
123 
124 	display_mode* selectedMode = NULL;
125 	uint32 modeCount = gInfo.sharedInfo->modeCount;
126 
127 	for (uint32 j = 0; j < modeCount; j++) {
128 		display_mode& mode = gInfo.modeList[j];
129 
130 		if (mode.timing.h_display == width && mode.timing.v_display == height) {
131 			int modeRefreshRate = int(((mode.timing.pixel_clock * 1000.0 /
132 					mode.timing.h_total) / mode.timing.v_total) + 0.5);
133 			if (modeRefreshRate == refreshRate) {
134 				if (colorDepth == 0) {
135 					if (selectedMode == NULL || mode.space > selectedMode->space)
136 						selectedMode = &mode;
137 				} else {
138 					if (mode.space == colorDepth)
139 						return &mode;
140 				}
141 			}
142 		}
143 	}
144 
145 	return selectedMode;
146 }
147 
148 
149 bool
150 IsModeUsable(const display_mode* mode)
151 {
152 	// Test if the display mode is usable by the current video chip.  That is,
153 	// does the chip have enough memory for the mode and is the pixel clock
154 	// within the chips allowable range, etc.
155 	//
156 	// Return true if the mode is usable.
157 
158 	SharedInfo& si = *gInfo.sharedInfo;
159 	uint32 bitsPerPixel;
160 	uint32 maxPixelClock;
161 
162 	if ( ! gInfo.GetColorSpaceParams(mode->space, bitsPerPixel, maxPixelClock))
163 		return false;
164 
165 	// Is there enough memory to handle the mode?
166 
167 	if (mode->virtual_width * mode->virtual_height * ((bitsPerPixel + 7) / 8)
168 			> si.maxFrameBufferSize)
169 		return false;
170 
171 	if (mode->timing.pixel_clock > maxPixelClock)
172 		return false;
173 
174 	// Is the color space supported?
175 
176 	bool bColorSpaceSupported = false;
177 	for (uint32 j = 0; j < si.colorSpaceCount; j++) {
178 		if (mode->space == uint32(si.colorSpaces[j])) {
179 			bColorSpaceSupported = true;
180 			break;
181 		}
182 	}
183 
184 	if ( ! bColorSpaceSupported)
185 		return false;
186 
187 	// Reject modes with a width of 640 and a height < 480 since they do not
188 	// work properly with the S3 chipsets.
189 
190 	if (mode->timing.h_display == 640 && mode->timing.v_display < 480)
191 		return false;
192 
193 	// In most mode lists, there are three entries for 640 x 480 which have a
194 	// refresh rate of 60 Hz.  The one with a pixel clock of 25175 works fine
195 	// with the S3 chips;  however, the other two with higher clock rates
196 	// cause the display to be offset down and to the right;  thus, reject them.
197 
198 	if (mode->timing.h_display == 640 && mode->timing.v_display == 480) {
199 		int modeRefreshRate = int(((mode->timing.pixel_clock * 1000.0 /
200 				mode->timing.h_total) / mode->timing.v_total) + 0.5);
201 		if (modeRefreshRate == 60 && mode->timing.pixel_clock > 26000)
202 			return false;
203 	}
204 
205 	// If the video is connected directly to an LCD display (ie, laptop
206 	// computer), restrict the display mode to resolutions where the width and
207 	// height of the mode are less than or equal to the width and height of the
208 	// LCD display.  Note that this code is not needed under Haiku since it can
209 	// get the preferred mode which should be the resolution of the LCD display.
210 
211 #ifndef __HAIKU__
212 
213 	if (si.displayType == MT_LCD && si.panelX > 0 && si.panelY > 0 &&
214 		(mode->timing.h_display > si.panelX
215 			|| mode->timing.v_display > si.panelY)) {
216 		return false;
217 	}
218 
219 #endif	// __HAIKU__
220 
221 	return true;
222 }
223 
224 
225 status_t
226 CreateModeList(bool (*checkMode)(const display_mode* mode))
227 {
228 	SharedInfo& si = *gInfo.sharedInfo;
229 	display_mode* list;
230 	uint32 count = 0;
231 	area_id listArea;
232 
233 	listArea = create_display_modes("S3 modes",
234 		si.bHaveEDID ? &si.edidInfo : NULL,
235 		NULL, 0, si.colorSpaces, si.colorSpaceCount,
236 		(check_display_mode_hook)checkMode, &list, &count);
237 
238 	if (listArea < 0)
239 		return listArea;		// listArea has error code
240 
241 	si.modeArea = gInfo.modeListArea = listArea;
242 	si.modeCount = count;
243 	gInfo.modeList = list;
244 
245 	return B_OK;
246 }
247 
248 
249 
250 status_t
251 ProposeDisplayMode(display_mode *target, const display_mode *low,
252 	const display_mode *high)
253 {
254 	(void)low;		// avoid compiler warning for unused arg
255 	(void)high;		// avoid compiler warning for unused arg
256 
257 	TRACE("ProposeDisplayMode()  clock: %d, width: %d, height: %d, space: 0x%X\n",
258 		  target->timing.pixel_clock, target->virtual_width, target->virtual_height,
259 		  target->space);
260 
261 	// Search the mode list for the specified mode.
262 
263 	uint32 modeCount = gInfo.sharedInfo->modeCount;
264 
265 	for (uint32 j = 0; j < modeCount; j++) {
266 		display_mode& mode = gInfo.modeList[j];
267 
268 		if (target->timing.h_display == mode.timing.h_display
269 			&& target->timing.v_display == mode.timing.v_display
270 			&& target->space == mode.space)
271 			return B_OK;	// mode found in list
272 	}
273 
274 	return B_BAD_VALUE;		// mode not found in list
275 }
276 
277 
278 status_t
279 SetDisplayMode(display_mode* pMode)
280 {
281 	// First validate the mode, then call a function to set the registers.
282 
283 	TRACE("SetDisplayMode() begin\n");
284 
285 	SharedInfo& si = *gInfo.sharedInfo;
286 	DisplayModeEx mode;
287 	(display_mode&)mode = *pMode;
288 
289 	uint32 maxPixelClock;
290 	if ( ! gInfo.GetColorSpaceParams(mode.space, mode.bpp, maxPixelClock))
291 		return B_ERROR;
292 
293 	mode.bytesPerRow = mode.virtual_width * ((mode.bpp + 7) / 8);	// use virtual width
294 
295 	if (ProposeDisplayMode(&mode, pMode, pMode) != B_OK)
296 		return B_ERROR;
297 
298 	// Test if there is sufficient memory for this mode.  Use the virtual width
299 	// and height for this calculation since one or both might be greater than
300 	// the actual dimensions of the mode.
301 
302 	if (mode.bytesPerRow * mode.virtual_height > si.maxFrameBufferSize)
303 		return B_ERROR;
304 
305 	TRACE("Display Mode:  width = %d, height = %d, depth = %d\n",
306 		mode.virtual_width, mode.virtual_height, mode.bpp);
307 
308 	TRACE("Mode Timing = { %d  %d %d %d %d  %d %d %d %d  0x%x }\n",
309 		mode.timing.pixel_clock,
310 		mode.timing.h_display, mode.timing.h_sync_start, mode.timing.h_sync_end,
311 		mode.timing.h_total,
312 		mode.timing.v_display, mode.timing.v_sync_start, mode.timing.v_sync_end,
313 		mode.timing.v_total, mode.timing.flags);
314 
315 	if ( ! gInfo.SetDisplayMode(mode))
316 		return B_ERROR;
317 
318 	si.displayMode = mode;
319 
320 	TRACE("SetDisplayMode() done\n");
321 	return B_OK;
322 }
323 
324 
325 
326 status_t
327 MoveDisplay(uint16 horizontalStart, uint16 verticalStart)
328 {
329 	// Set which pixel of the virtual frame buffer will show up in the
330 	// top left corner of the display device.	Used for page-flipping
331 	// games and virtual desktops.
332 
333 	DisplayModeEx& mode = gInfo.sharedInfo->displayMode;
334 
335 	if (mode.timing.h_display + horizontalStart > mode.virtual_width
336 		|| mode.timing.v_display + verticalStart > mode.virtual_height)
337 		return B_ERROR;
338 
339 	mode.h_display_start = horizontalStart;
340 	mode.v_display_start = verticalStart;
341 
342 	gInfo.AdjustFrame(mode);
343 	return B_OK;
344 }
345 
346 
347 uint32
348 AccelerantModeCount(void)
349 {
350 	// Return the number of display modes in the mode list.
351 
352 	return gInfo.sharedInfo->modeCount;
353 }
354 
355 
356 status_t
357 GetModeList(display_mode* dmList)
358 {
359 	// Copy the list of supported video modes to the location pointed at
360 	// by dmList.
361 
362 	memcpy(dmList, gInfo.modeList, gInfo.sharedInfo->modeCount * sizeof(display_mode));
363 	return B_OK;
364 }
365 
366 
367 status_t
368 GetDisplayMode(display_mode* current_mode)
369 {
370 	*current_mode = gInfo.sharedInfo->displayMode;	// return current display mode
371 	return B_OK;
372 }
373 
374 
375 status_t
376 GetFrameBufferConfig(frame_buffer_config* pFBC)
377 {
378 	SharedInfo& si = *gInfo.sharedInfo;
379 
380 	pFBC->frame_buffer = (void*)((addr_t)si.videoMemAddr + si.frameBufferOffset);
381 	pFBC->frame_buffer_dma = (void*)((addr_t)si.videoMemPCI + si.frameBufferOffset);
382 	pFBC->bytes_per_row = si.displayMode.bytesPerRow;
383 
384 	return B_OK;
385 }
386 
387 
388 status_t
389 GetPixelClockLimits(display_mode* mode, uint32* low, uint32* high)
390 {
391 	// Return the maximum and minium pixel clock limits for the specified mode.
392 
393 	uint32 bitsPerPixel;
394 	uint32 maxPixelClock;
395 
396 	if ( ! gInfo.GetColorSpaceParams(mode->space, bitsPerPixel, maxPixelClock))
397 		return B_ERROR;
398 
399 	if (low != NULL) {
400 		// lower limit of about 48Hz vertical refresh
401 		uint32 totalClocks = (uint32)mode->timing.h_total * (uint32)mode->timing.v_total;
402 		uint32 lowClock = (totalClocks * 48L) / 1000L;
403 		if (lowClock > maxPixelClock)
404 			return B_ERROR;
405 
406 		*low = lowClock;
407 	}
408 
409 	if (high != NULL)
410 		*high = maxPixelClock;
411 
412 	return B_OK;
413 }
414 
415 
416 status_t
417 GetTimingConstraints(display_timing_constraints *constraints)
418 {
419 	(void)constraints;		// avoid compiler warning for unused arg
420 
421 	TRACE("GetTimingConstraints() called\n");
422 	return B_ERROR;
423 }
424 
425 
426 status_t
427 GetPreferredDisplayMode(display_mode* preferredMode)
428 {
429 	// If the chip is connected to a laptop LCD panel, find the mode with
430 	// matching width and height, 60 Hz refresh rate, and greatest color depth.
431 
432 	TRACE("GetPreferredDisplayMode()\n");
433 
434 	SharedInfo& si = *gInfo.sharedInfo;
435 
436 	if (si.displayType == MT_LCD) {
437 		display_mode* mode = FindDisplayMode(si.panelX, si.panelY, 60, 0);
438 
439 		if (mode != NULL) {
440 			*preferredMode = *mode;
441 			return B_OK;
442 		}
443 	}
444 
445 	return B_ERROR;
446 }
447 
448 
449 
450 #ifdef __HAIKU__
451 
452 status_t
453 GetEdidInfo(void* info, size_t size, uint32* _version)
454 {
455 	TRACE("GetEdidInfo()\n");
456 
457 	SharedInfo& si = *gInfo.sharedInfo;
458 
459 	if ( ! si.bHaveEDID)
460 		return B_ERROR;
461 
462 	if (size < sizeof(struct edid1_info))
463 		return B_BUFFER_OVERFLOW;
464 
465 	memcpy(info, &si.edidInfo, sizeof(struct edid1_info));
466 	*_version = EDID_VERSION_1;
467 	return B_OK;
468 }
469 
470 #endif	// __HAIKU__
471