2 Copyright 2007-2008 Haiku, Inc. All rights reserved.
3 Distributed under the terms of the MIT license.
11 #include <create_display_modes.h> // common accelerant header file
17 InitCrtcTimingValues(const DisplayModeEx
& mode
, int horzScaleFactor
, uint8 crtc
[],
18 uint8
& cr3b
, uint8
& cr3c
, uint8
& cr5d
, uint8
& cr5e
)
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.
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.
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
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
43 // CRTC Controller values
47 crtc
[0x02] = hBlank_s
;
48 crtc
[0x03] = (hBlank_e
& 0x1f) | 0x80;
50 crtc
[0x05] = ((hSync_e
& 0x1f) | ((hBlank_e
& 0x20) << 2));
52 crtc
[0x07] = (((vTotal
& 0x100) >> 8)
53 | ((vDisp_e
& 0x100) >> 7)
54 | ((vSync_s
& 0x100) >> 6)
55 | ((vBlank_s
& 0x100) >> 5)
57 | ((vTotal
& 0x200) >> 4)
58 | ((vDisp_e
& 0x200) >> 3)
59 | ((vSync_s
& 0x200) >> 2));
62 crtc
[0x09] = ((vBlank_s
& 0x200) >> 4) | 0x40;
70 crtc
[0x11] = (vSync_e
& 0x0f) | 0x20;
72 crtc
[0x13] = mode
.bytesPerRow
/ 8;
74 crtc
[0x15] = vBlank_s
;
75 crtc
[0x16] = vBlank_e
;
79 int i
= ((hTotal
& 0x100) >> 8) |
80 ((hDisp_e
& 0x100) >> 7) |
81 ((hBlank_s
& 0x100) >> 6) |
82 ((hSync_s
& 0x100) >> 4);
84 if (hSync_e
- hSync_s
> 64)
85 i
|= 0x08; // add another 64 DCLKs to blank pulse width
87 if (hSync_e
- hSync_s
> 32)
88 i
|= 0x20; // add another 32 DCLKs to hsync pulse width
90 int j
= (crtc
[0] + ((i
& 0x01) << 8) + crtc
[4] + ((i
& 0x10) << 4) + 1) / 2;
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;
96 j
= crtc
[0] + ((i
& 0x01) << 8) + 1;
100 i
|= (j
& 0x100) >> 2;
101 cr3c
= (crtc
[0] + ((i
& 0x01) << 8)) / 2;
104 cr5e
= ((vTotal
& 0x400) >> 10) |
105 ((vDisp_e
& 0x400) >> 9) |
106 ((vBlank_s
& 0x400) >> 8) |
107 ((vSync_s
& 0x400) >> 6) | 0x40;
112 FindDisplayMode(int width
, int height
, int refreshRate
, uint32 colorDepth
)
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.
120 // If successful, return a pointer to the selected display_mode object;
123 display_mode
* selectedMode
= NULL
;
124 uint32 modeCount
= gInfo
.sharedInfo
->modeCount
;
126 for (uint32 j
= 0; j
< modeCount
; j
++) {
127 display_mode
& mode
= gInfo
.modeList
[j
];
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
;
137 if (mode
.space
== colorDepth
)
150 IsThereEnoughFBMemory(const display_mode
* mode
, uint32 bitsPerPixel
)
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.
155 uint32 maxWidth
= mode
->virtual_width
;
156 if (mode
->timing
.h_display
> maxWidth
)
157 maxWidth
= mode
->timing
.h_display
;
159 uint32 maxHeight
= mode
->virtual_height
;
160 if (mode
->timing
.v_display
> maxHeight
)
161 maxHeight
= mode
->timing
.v_display
;
163 uint32 bytesPerPixel
= (bitsPerPixel
+ 7) / 8;
165 return (maxWidth
* maxHeight
* bytesPerPixel
< gInfo
.sharedInfo
->maxFrameBufferSize
);
171 IsModeUsable(const display_mode
* mode
)
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.
177 // Return true if the mode is usable.
179 SharedInfo
& si
= *gInfo
.sharedInfo
;
181 uint32 maxPixelClock
;
183 if ( ! gInfo
.GetColorSpaceParams(mode
->space
, bitsPerPixel
, maxPixelClock
))
186 // Is there enough frame buffer memory to handle the mode?
188 if ( ! IsThereEnoughFBMemory(mode
, bitsPerPixel
))
191 if (mode
->timing
.pixel_clock
> maxPixelClock
)
194 // Is the color space supported?
196 bool bColorSpaceSupported
= false;
197 for (uint32 j
= 0; j
< si
.colorSpaceCount
; j
++) {
198 if (mode
->space
== uint32(si
.colorSpaces
[j
])) {
199 bColorSpaceSupported
= true;
204 if ( ! bColorSpaceSupported
)
207 // Reject modes with a width of 640 and a height < 480 since they do not
208 // work properly with the S3 chipsets.
210 if (mode
->timing
.h_display
== 640 && mode
->timing
.v_display
< 480)
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
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
)) {
229 CreateModeList( bool (*checkMode
)(const display_mode
* mode
),
230 bool (*getEdid
)(edid1_info
& edidInfo
))
232 SharedInfo
& si
= *gInfo
.sharedInfo
;
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.
241 si
.bHaveEDID
= false;
243 if (si
.displayType
!= MT_LCD
) {
245 si
.bHaveEDID
= getEdid(si
.edidInfo
);
247 if ( ! si
.bHaveEDID
) {
249 ged
.magic
= S3_PRIVATE_DATA_MAGIC
;
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
);
257 edid_decode(&si
.edidInfo
, &ged
.rawEdid
); // decode & save EDID info
264 #ifdef ENABLE_DEBUG_TRACE
265 edid_dump(&(si
.edidInfo
));
268 TRACE("CreateModeList(); Unable to get EDID info\n");
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
);
282 return listArea
; // listArea has error code
284 si
.modeArea
= gInfo
.modeListArea
= listArea
;
285 si
.modeCount
= count
;
286 gInfo
.modeList
= list
;
294 ProposeDisplayMode(display_mode
*target
, const display_mode
*low
,
295 const display_mode
*high
)
297 (void)low
; // avoid compiler warning for unused arg
298 (void)high
; // avoid compiler warning for unused arg
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
);
304 // Search the mode list for the specified mode.
306 uint32 modeCount
= gInfo
.sharedInfo
->modeCount
;
308 for (uint32 j
= 0; j
< modeCount
; j
++) {
309 display_mode
& mode
= gInfo
.modeList
[j
];
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
317 return B_BAD_VALUE
; // mode not found in list
323 SetDisplayMode(display_mode
* pMode
)
325 // First validate the mode, then call a function to set the registers.
327 TRACE("SetDisplayMode() begin\n");
329 SharedInfo
& si
= *gInfo
.sharedInfo
;
331 (display_mode
&)mode
= *pMode
;
333 uint32 maxPixelClock
;
334 if ( ! gInfo
.GetColorSpaceParams(mode
.space
, mode
.bpp
, maxPixelClock
))
337 if (ProposeDisplayMode(&mode
, pMode
, pMode
) != B_OK
)
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
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;
354 int bytesPerPixel
= (mode
.bpp
+ 7) / 8;
355 mode
.bytesPerRow
= mode
.timing
.h_display
* bytesPerPixel
;
357 // Is there enough frame buffer memory for this mode?
359 if ( ! IsThereEnoughFBMemory(&mode
, mode
.bpp
))
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
);
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
,
371 mode
.timing
.v_display
,
372 mode
.timing
.v_sync_start
, mode
.timing
.v_sync_end
,
373 mode
.timing
.v_total
);
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
) ? '+' : '-');
382 if ( ! gInfo
.SetDisplayMode(mode
))
385 si
.displayMode
= mode
;
387 TRACE("SetDisplayMode() done\n");
394 MoveDisplay(uint16 horizontalStart
, uint16 verticalStart
)
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.
400 DisplayModeEx
& mode
= gInfo
.sharedInfo
->displayMode
;
402 if (mode
.timing
.h_display
+ horizontalStart
> mode
.virtual_width
403 || mode
.timing
.v_display
+ verticalStart
> mode
.virtual_height
)
406 mode
.h_display_start
= horizontalStart
;
407 mode
.v_display_start
= verticalStart
;
409 gInfo
.AdjustFrame(mode
);
415 AccelerantModeCount(void)
417 // Return the number of display modes in the mode list.
419 return gInfo
.sharedInfo
->modeCount
;
424 GetModeList(display_mode
* dmList
)
426 // Copy the list of supported video modes to the location pointed at
429 memcpy(dmList
, gInfo
.modeList
, gInfo
.sharedInfo
->modeCount
* sizeof(display_mode
));
435 GetDisplayMode(display_mode
* current_mode
)
437 *current_mode
= gInfo
.sharedInfo
->displayMode
; // return current display mode
443 GetFrameBufferConfig(frame_buffer_config
* pFBC
)
445 SharedInfo
& si
= *gInfo
.sharedInfo
;
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
;
457 GetPixelClockLimits(display_mode
* mode
, uint32
* low
, uint32
* high
)
459 // Return the maximum and minium pixel clock limits for the specified mode.
462 uint32 maxPixelClock
;
464 if ( ! gInfo
.GetColorSpaceParams(mode
->space
, bitsPerPixel
, maxPixelClock
))
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
)
478 *high
= maxPixelClock
;
485 GetTimingConstraints(display_timing_constraints
*constraints
)
487 (void)constraints
; // avoid compiler warning for unused arg
494 GetPreferredDisplayMode(display_mode
* preferredMode
)
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.
499 SharedInfo
& si
= *gInfo
.sharedInfo
;
501 if (si
.displayType
== MT_LCD
) {
502 display_mode
* mode
= FindDisplayMode(si
.panelX
, si
.panelY
, 60, 0);
505 *preferredMode
= *mode
;
518 GetEdidInfo(void* info
, size_t size
, uint32
* _version
)
520 SharedInfo
& si
= *gInfo
.sharedInfo
;
525 if (size
< sizeof(struct edid1_info
))
526 return B_BUFFER_OVERFLOW
;
528 memcpy(info
, &si
.edidInfo
, sizeof(struct edid1_info
));
529 *_version
= EDID_VERSION_1
;