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