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
InitCrtcTimingValues(const DisplayModeEx & mode,int horzScaleFactor,uint8 crtc[],uint8 & cr3b,uint8 & cr3c,uint8 & cr5d,uint8 & cr5e)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*
FindDisplayMode(int width,int height,int refreshRate,uint32 colorDepth)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
IsThereEnoughFBMemory(const display_mode * mode,uint32 bitsPerPixel)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
IsModeUsable(const display_mode * mode)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
CreateModeList(bool (* checkMode)(const display_mode * mode),bool (* getEdid)(edid1_info & edidInfo))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
ProposeDisplayMode(display_mode * target,const display_mode * low,const display_mode * high)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
SetDisplayMode(display_mode * pMode)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
MoveDisplay(uint16 horizontalStart,uint16 verticalStart)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
AccelerantModeCount(void)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
GetModeList(display_mode * dmList)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
GetDisplayMode(display_mode * current_mode)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
GetFrameBufferConfig(frame_buffer_config * pFBC)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
GetPixelClockLimits(display_mode * mode,uint32 * low,uint32 * high)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
GetTimingConstraints(display_timing_constraints * constraints)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
GetPreferredDisplayMode(display_mode * preferredMode)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
GetEdidInfo(void * info,size_t size,uint32 * _version)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