vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / accelerants / nvidia / ProposeDisplayMode.c
blobedd69180ee6e558669bb19b5ad10c9011e539c0a
1 /*
2 Copyright 1999, Be Incorporated. All Rights Reserved.
3 This file may be used under the terms of the Be Sample Code License.
5 Other authors for NV driver:
6 Mark Watson,
7 Rudolf Cornelissen 9/2002-1/2016
8 */
10 #define MODULE_BIT 0x00400000
12 #include "acc_std.h"
14 #define T_POSITIVE_SYNC (B_POSITIVE_HSYNC | B_POSITIVE_VSYNC)
15 /* mode flags will be setup as status info by PROPOSEMODE! */
16 #define MODE_FLAGS 0
17 #define MODE_COUNT (sizeof (mode_list) / sizeof (display_mode))
19 /* Standard VESA modes,
20 * plus panel specific resolution modes which are internally modified during run-time depending on the requirements of the actual
21 * panel connected. The modes as listed here, should timing-wise be as compatible with analog (CRT) monitors as can be... */
22 //fixme: if EDID monitor found create list via common EDID code...
23 static const display_mode mode_list[] = {
24 /* 4:3 modes; 307.2k pixels */
25 { { 25175, 640, 656, 752, 800, 480, 490, 492, 525, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(640X480X8.Z1) */
26 { { 27500, 640, 672, 768, 864, 480, 488, 494, 530, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* 640X480X60Hz */
27 { { 30500, 640, 672, 768, 864, 480, 517, 523, 588, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* SVGA_640X480X60HzNI */
28 { { 31500, 640, 664, 704, 832, 480, 489, 492, 520, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(640X480X8.Z1) */
29 { { 31500, 640, 656, 720, 840, 480, 481, 484, 500, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(640X480X8.Z1) */
30 { { 36000, 640, 696, 752, 832, 480, 481, 484, 509, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(640X480X8.Z1) */
31 /* 4:3 modes; 480k pixels */
32 { { 36000, 800, 824, 896, 1024, 600, 601, 603, 625, 0}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@56Hz_(800X600) from Be, Inc. driver + XFree86 */
33 { { 38100, 800, 832, 960, 1088, 600, 602, 606, 620, 0}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* SVGA_800X600X56HzNI */
34 { { 40000, 800, 840, 968, 1056, 600, 601, 605, 628, T_POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(800X600X8.Z1) + XFree86 */
35 { { 49500, 800, 816, 896, 1056, 600, 601, 604, 625, T_POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(800X600X8.Z1) + XFree86 */
36 { { 50000, 800, 856, 976, 1040, 600, 637, 643, 666, T_POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(800X600X8.Z1) + XFree86 */
37 { { 56250, 800, 832, 896, 1048, 600, 601, 604, 631, T_POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(800X600X8.Z1) + XFree86 */
38 /* 4:3 modes; 786.432k pixels */
39 { { 65000, 1024, 1048, 1184, 1344, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1024X768X8.Z1) + XFree86 */
40 { { 75000, 1024, 1048, 1184, 1328, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(1024X768X8.Z1) + XFree86 */
41 { { 78750, 1024, 1040, 1136, 1312, 768, 769, 772, 800, T_POSITIVE_SYNC}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1024X768X8.Z1) + XFree86 */
42 { { 94500, 1024, 1072, 1168, 1376, 768, 769, 772, 808, T_POSITIVE_SYNC}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1024X768X8.Z1) + XFree86 */
43 /* 4:3 modes; 995.328k pixels */
44 { { 94200, 1152, 1184, 1280, 1472, 864, 865, 868, 914, T_POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70Hz_(1152X864X8.Z1) */
45 { { 97800, 1152, 1216, 1344, 1552, 864, 865, 868, 900, T_POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70Hz_(1152X864X8.Z1) */
46 { { 108000, 1152, 1216, 1344, 1600, 864, 865, 868, 900, T_POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1152X864X8.Z1) + XFree86 */
47 { { 121500, 1152, 1216, 1344, 1568, 864, 865, 868, 911, T_POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1152X864X8.Z1) */
48 /* 5:4 modes; 1.311M pixels */
49 { { 108000, 1280, 1328, 1440, 1688, 1024, 1025, 1028, 1066, T_POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X1024) from Be, Inc. driver + XFree86 */
50 { { 135000, 1280, 1296, 1440, 1688, 1024, 1025, 1028, 1066, T_POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1280X1024X8.Z1) + XFree86 */
51 { { 157500, 1280, 1344, 1504, 1728, 1024, 1025, 1028, 1072, T_POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1280X1024X8.Z1) + XFree86 */
52 /* 4:3 panel mode; 1.47M pixels */
53 { { 122600, 1400, 1488, 1640, 1880, 1050, 1051, 1054, 1087, T_POSITIVE_SYNC}, B_CMAP8, 1400, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1400X1050) */
54 /* 4:3 modes; 1.92M pixels */
55 { { 162000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1600X1200X8.Z1) + XFree86 */
56 /* identical lines to above one, apart from refreshrate.. */
57 { { 175500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@65Hz_(1600X1200X8.Z1) + XFree86 */
58 { { 189000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70Hz_(1600X1200X8.Z1) + XFree86 */
59 { { 202500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1600X1200X8.Z1) + XFree86 */
60 { { 216000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@80Hz_(1600X1200X8.Z1) */
61 { { 229500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1600X1200X8.Z1) + XFree86 */
62 /* end identical lines. */
63 /* 4:3 modes; 2.408M pixels */
64 { { 204750, 1792, 1920, 2120, 2448, 1344, 1345, 1348, 1394, B_POSITIVE_VSYNC}, B_CMAP8, 1792, 1344, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1792X1344) from Be, Inc. driver + XFree86 */
65 { { 261000, 1792, 1888, 2104, 2456, 1344, 1345, 1348, 1417, B_POSITIVE_VSYNC}, B_CMAP8, 1792, 1344, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1792X1344) from Be, Inc. driver + XFree86 */
66 /* 4:3 modes; 2.584M pixels */
67 { { 218250, 1856, 1952, 2176, 2528, 1392, 1393, 1396, 1439, B_POSITIVE_VSYNC}, B_CMAP8, 1856, 1392, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1856X1392) from Be, Inc. driver + XFree86 */
68 { { 288000, 1856, 1984, 2208, 2560, 1392, 1393, 1396, 1500, B_POSITIVE_VSYNC}, B_CMAP8, 1856, 1392, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1856X1392) from Be, Inc. driver + XFree86 */
69 /* 4:3 modes; 2.765M pixels */
70 { { 234000, 1920, 2048, 2256, 2600, 1440, 1441, 1444, 1500, B_POSITIVE_VSYNC}, B_CMAP8, 1920, 1440, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1920X1440) from Be, Inc. driver + XFree86 */
71 { { 297000, 1920, 2064, 2288, 2640, 1440, 1441, 1444, 1500, B_POSITIVE_VSYNC}, B_CMAP8, 1920, 1440, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1920X1440) from Be, Inc. driver + XFree86 */
72 /* 4:3 modes; 3.146M pixels */
73 { { 266950, 2048, 2200, 2424, 2800, 1536, 1537, 1540, 1589, B_POSITIVE_VSYNC}, B_CMAP8, 2048, 1536, 0, 0, MODE_FLAGS}, /* From XFree86 posting @60Hz + XFree86 */
74 /* 16:10 panel mode; 400k pixels */
75 { { 31300, 800, 848, 928, 1008, 500, 501, 504, 518, T_POSITIVE_SYNC}, B_CMAP8, 800, 500, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(800X500) */
76 /* 16:10 panel mode; 655.36k pixels */
77 { { 52800, 1024, 1072, 1176, 1328, 640, 641, 644, 663, T_POSITIVE_SYNC}, B_CMAP8, 1024, 640, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1024X640) */
78 /* 16:10 panel-TV mode; 983.04k pixels */
79 { { 80135, 1280, 1344, 1480, 1680, 768, 769, 772, 795, T_POSITIVE_SYNC}, B_CMAP8, 1280, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X768) */
80 /* 16:10 panel mode; 1.024M pixels */
81 { { 83500, 1280, 1344, 1480, 1680, 800, 801, 804, 828, T_POSITIVE_SYNC}, B_CMAP8, 1280, 800, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X800) */
82 /* 16:10 panel mode; 1.296M pixels */
83 { { 106500, 1440, 1520, 1672, 1904, 900, 901, 904, 932, T_POSITIVE_SYNC}, B_CMAP8, 1440, 900, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1440X900) */
84 /* 16:10 panel mode; 1.764M pixels */
85 { { 147100, 1680, 1784, 1968, 2256, 1050, 1051, 1054, 1087, T_POSITIVE_SYNC}, B_CMAP8, 1680, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1680X1050) */
86 /* 16:10 panel mode; 2.304M pixels */
87 { { 193160, 1920, 2048, 2256, 2592, 1200, 1201, 1204, 1242, T_POSITIVE_SYNC}, B_CMAP8, 1920, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1920X1200) */
88 //{ { 160000, 1920, 2010, 2060, 2110, 1200, 1202, 1208, 1235, T_POSITIVE_SYNC}, B_CMAP8, 1920, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1920X1200) */
89 /* 16:9 panel mode; 1280x720 (HDTV 1280x720p) */
90 { { 74520, 1280, 1368, 1424, 1656, 720, 724, 730, 750, T_POSITIVE_SYNC}, B_CMAP8, 1280, 720, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X720) */
91 /* 16:9 panel mode; 1366x768 (HDTV '1280x720p')
92 note: horizontal CRTC timing must be a multiple of 8! (hardware restriction) */
93 { { 85500, 1368, 1440, 1576, 1792, 768, 771, 774, 798, T_POSITIVE_SYNC}, B_CMAP8, 1368, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1366X768) */
94 /* 16:9 panel mode; 1920x1080 (HDTV 1920x1080p) */
95 { { 148500, 1920, 2008, 2052, 2200, 1080, 1084, 1089, 1125, T_POSITIVE_SYNC}, B_CMAP8, 1920, 1080, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1920X1080) */
99 // transform official mode to internal, multi-screen mode enhanced mode
100 static void Haiku_DetectTranslateMultiMode(display_mode *mode)
102 mode->flags &= ~DUALHEAD_BITS;
104 if( mode->virtual_width == 2 * mode->timing.h_display ) {
105 LOG(4, ("Haiku: horizontal combine mode\n"));
106 if (si->Haiku_switch_head)
107 mode->flags |= DUALHEAD_SWITCH;
108 else
109 mode->flags |= DUALHEAD_ON;
110 } else if( mode->virtual_height == 2 * mode->timing.v_display ) {
111 LOG(4, ("Haiku: vertical combine mode not supported\n"));
112 } else {
113 /* unfortunately Haiku's screenprefs panel does not support single head output explicitly, so we'd better
114 activate both heads always to (probably) mimic Radeon driver behaviour (for which this app was adapted) */
115 /* note please:
116 - this will have a big downside on hardware for which both heads have different resolution cababilities (i.e. Matrox);
117 - also (on laptops) this will shorten battery life a bit of course.. */
118 mode->flags |= DUALHEAD_CLONE;
123 // check and execute tunnel settings command
124 static status_t Haiku_CheckMultiMonTunnel(display_mode *mode, const display_mode *low, const display_mode *high, bool *isTunneled )
126 if( (mode->timing.flags & RADEON_MODE_MULTIMON_REQUEST) != 0 &&
127 (mode->timing.flags & RADEON_MODE_MULTIMON_REPLY) == 0 )
129 mode->timing.flags &= ~RADEON_MODE_MULTIMON_REQUEST;
130 mode->timing.flags |= RADEON_MODE_MULTIMON_REPLY;
132 // still process request, just in case someone set this flag
133 // combination by mistake
135 *isTunneled = true;
136 return B_OK;
139 // check magic params
140 if( mode->space != 0 || low->space != 0 || high->space != 0
141 || low->virtual_width != 0xffff || low->virtual_height != 0xffff
142 || high->virtual_width != 0 || high->virtual_height != 0
143 || mode->timing.pixel_clock != 0
144 || low->timing.pixel_clock != 'TKTK' || high->timing.pixel_clock != 'KTKT' )
146 *isTunneled = false;
147 return B_OK;
150 *isTunneled = true;
152 /* enable Haiku special handling */
153 if (!si->haiku_prefs_used)
154 LOG(4, ("PROPOSEMODE: Haiku screenprefs tunnel detected.\n"));
155 si->haiku_prefs_used = true;
157 /* note please:
158 Haiku ScreenPrefs does not issue a SetMode command after changing these settings (per se), but relies on
159 driver switching outputs directly. These settings are not dependant on workspace there, and are not part of
160 the mode in the Radeon driver. In the Matrox and nVidia drivers they are though. So we need SetMode
161 to be issued. (yes: sometimes I switch monitors when I switch workspace.. ;-)
162 Also the RADEON driver saves these settings to a file so it remembers these after reboots. We don't atm.
163 If the mode as proposed by the driver would be saved by the ScreenPrefs panel, we would 'remember' it over
164 reboots though (via app_server settings).. */
166 switch( mode->h_display_start ) {
167 case ms_swap:
168 switch( mode->v_display_start ) {
169 case 0:
170 if (si->Haiku_switch_head)
171 mode->timing.flags = 1;
172 else
173 mode->timing.flags = 0;
174 LOG(4, ("Haiku: tunnel access target=swap, command=get, value=%u\n", mode->timing.flags));
175 return B_OK;
176 case 1:
177 si->Haiku_switch_head = mode->timing.flags != 0;
178 LOG(4, ("Haiku: tunnel access target=swap, command=set, value=%u\n", mode->timing.flags));
179 /* Haiku's screenprefs panel expects us to directly set the mode.. (but it should do that itself) */
180 SET_DISPLAY_MODE(&si->dm);
181 return B_OK;
183 break;
185 case ms_use_laptop_panel:
186 LOG(4, ("Haiku: tunnel access target=usepanel, command=%s, value=%u\n",
187 (mode->v_display_start == 1) ? "set" : "get", mode->timing.flags));
188 // we refuse this setting (makes no sense for us: laptop panel is treated as normal screen)
189 return B_ERROR;
190 break;
192 case ms_tv_standard:
193 LOG(4, ("Haiku: tunnel access target=tvstandard, command=%s, value=%u\n",
194 (mode->v_display_start == 1) ? "set" : "get", mode->timing.flags));
195 // let's forget about this for now..
196 return B_ERROR;
197 break;
200 LOG(4, ("Haiku: unhandled tunnel access target=$%x, command=%u, value=%u\n",
201 mode->h_display_start, mode->v_display_start, mode->timing.flags));
203 return B_ERROR;
208 Check mode is between low and high limits.
209 Returns:
210 B_OK - found one
211 B_BAD_VALUE - mode can be made, but outside limits
212 B_ERROR - not possible
214 /* BOUNDS WARNING:
215 * BeOS (tested R5.0.3PE) is failing BWindowScreen.SetFrameBuffer() if PROPOSEMODE
216 * returns B_BAD_VALUE. It's called by the OS with target, low and high set to
217 * have the same settings for BWindowScreen!
218 * Which means we should not return B_BAD_VALUE on anything except for deviations on:
219 * display_mode.virtual_width;
220 * display_mode.virtual_height;
221 * display_mode.timing.h_display;
222 * display_mode.timing.v_display;
224 /* Note:
225 * The target mode should be modified to correspond to the mode as it can be made. */
226 status_t
227 PROPOSE_DISPLAY_MODE(display_mode *target, const display_mode *low, const display_mode *high)
229 status_t status = B_OK;
230 float pix_clock_found, target_aspect;
231 uint8 m,n,p, bpp;
232 status_t result;
233 uint32 max_vclk, row_bytes, mem_reservation;
234 bool acc_mode;
235 double target_refresh = ((double)target->timing.pixel_clock * 1000.0)
236 / ((double)target->timing.h_total * (double)target->timing.v_total);
237 bool want_same_width = target->timing.h_display == target->virtual_width;
238 bool want_same_height = target->timing.v_display == target->virtual_height;
239 bool isTunneled = false;
240 // check whether we got a tunneled settings command
241 result = Haiku_CheckMultiMonTunnel(target, low, high, &isTunneled);
242 if (isTunneled)
243 return result;
245 /* since we (might be) called by the Haiku ScreenPrefs panel, check for special modes and translate
246 them from (for us) non-native modes to native modes (as used in DualheadSetup by Mark Watson).
247 These 'native modes' survive system reboots by the way, at least when set using DualheadSetup. */
249 /* note please: apparantly Haiku Screenprefs does not save the modes as set by the driver, but as originally requested
250 by the ScreenPrefs panel. Modifications done by the driver are therefore not saved.
251 It would be nice if the screenprefs panel in Haiku would save the modified modeflags (after proposemode or setmode),
252 that would also make the driver remember switched heads (now it's a temporary setting). */
254 if (si->haiku_prefs_used) {
255 // check how many heads are needed by target mode
256 Haiku_DetectTranslateMultiMode(target);
259 LOG(1, ("PROPOSEMODE: (ENTER) requested virtual_width %d, virtual_height %d\n",
260 target->virtual_width, target->virtual_height));
262 /*find a nearby valid timing from that given*/
263 result = head1_validate_timing(&target->timing.h_display,
264 &target->timing.h_sync_start, &target->timing.h_sync_end,
265 &target->timing.h_total, &target->timing.v_display,
266 &target->timing.v_sync_start, &target->timing.v_sync_end,
267 &target->timing.v_total);
268 if (result == B_ERROR) {
269 LOG(4, ("PROPOSEMODE: could not validate timing, aborted.\n"));
270 return result;
273 /* disable aspect checks for a requested TVout mode when mode is TVout capable */
274 if (!si->ps.tvout
275 || !(BT_check_tvmode(*target) && (target->flags & TV_BITS))) {
276 /* check if all connected output devices can display the requested mode's aspect.
277 * assuming 16:10 screens can display non-WS modes, but cannot (correctly) display 16:9 modes;
278 * assuming 16:9 screens can display non-WS modes, and can display 16:10 modes. */
279 /* calculate display mode aspect */
280 target_aspect = (target->timing.h_display / ((float)target->timing.v_display));
281 /* NOTE:
282 * allow 0.10 difference so 5:4 aspect panels will be able to use 4:3 aspect modes! */
283 switch (si->ps.monitors) {
284 case 0: /* no monitor found at all */
285 /* if forcing widescreen type was requested don't block mode */
286 if (target_aspect > 1.34 && !si->settings.force_ws) {
287 LOG(4, ("PROPOSEMODE: not all output devices can display widescreen modes, aborted.\n"));
288 return B_ERROR;
290 break;
291 case CRTC1_TMDS: /* digital panel on head 1, nothing on head 2 */
292 case CRTC1_VGA: /* analog connected screen on head 1, nothing on head 2 */
293 if (si->ps.crtc1_screen.aspect < (target_aspect - 0.10)) {
294 LOG(4, ("PROPOSEMODE: screen at crtc1 is not widescreen (enough) type, aborted.\n"));
295 return B_ERROR;
297 break;
298 case CRTC2_TMDS: /* nothing on head 1, digital panel on head 2 */
299 case CRTC2_VGA: /* analog connected screen on head 2, nothing on head 1 */
300 if (si->ps.crtc2_screen.aspect < (target_aspect - 0.10)) {
301 LOG(4, ("PROPOSEMODE: screen at crtc2 is not widescreen (enough) type, aborted.\n"));
302 return B_ERROR;
304 break;
305 case CRTC1_TMDS | CRTC2_TMDS: /* digital panels on both heads */
306 case CRTC1_VGA | CRTC2_VGA: /* analog connected screens on both heads */
307 case CRTC1_TMDS | CRTC2_VGA: /* digital panel on head 1, analog connected screen on head 2 */
308 case CRTC1_VGA | CRTC2_TMDS: /* analog connected screen on head 1, digital panel on head 2 */
309 default: /* more than two screens connected (illegal setup) */
310 if ((si->ps.crtc1_screen.aspect < (target_aspect - 0.10)) ||
311 (si->ps.crtc2_screen.aspect < (target_aspect - 0.10))) {
312 LOG(4, ("PROPOSEMODE: not all connected screens are widescreen (enough) type, aborted.\n"));
313 return B_ERROR;
315 break;
319 /* only limit modelist if user did not explicitly block this via nv.settings
320 (because of errors in monitor's EDID information returned) */
321 if (si->settings.check_edid) {
322 /* check if screen(s) can display the requested resolution (if we have it's EDID info)
323 note:
324 allowing 2 pixels more for horizontal display for the 1366 mode, since multiples of 8
325 are required for the CRTCs horizontal timing programming) */
326 if (si->ps.crtc1_screen.have_native_edid) {
327 if ((target->timing.h_display - 2) > si->ps.crtc1_screen.timing.h_display
328 || target->timing.v_display > si->ps.crtc1_screen.timing.v_display) {
329 LOG(4, ("PROPOSEMODE: screen at crtc1 can't display requested resolution, aborted.\n"));
330 return B_ERROR;
333 if (si->ps.crtc2_screen.have_native_edid) {
334 if ((target->timing.h_display - 2) > si->ps.crtc2_screen.timing.h_display
335 || target->timing.v_display > si->ps.crtc2_screen.timing.v_display) {
336 LOG(4, ("PROPOSEMODE: screen at crtc2 can't display requested resolution, aborted.\n"));
337 return B_ERROR;
342 /* validate display vs. virtual */
343 if (target->timing.h_display > target->virtual_width || want_same_width)
344 target->virtual_width = target->timing.h_display;
345 if (target->timing.v_display > target->virtual_height || want_same_height)
346 target->virtual_height = target->timing.v_display;
348 /* nail virtual size and 'subsequently' calculate rowbytes */
349 result = nv_general_validate_pic_size(target, &row_bytes, &acc_mode);
350 if (result == B_ERROR) {
351 LOG(4, ("PROPOSEMODE: could not validate virtual picture size, aborted.\n"));
352 return result;
355 /* check if virtual_width is still within the requested limits */
356 if (target->virtual_width < low->virtual_width
357 || target->virtual_width > high->virtual_width) {
358 status = B_BAD_VALUE;
359 LOG(4, ("PROPOSEMODE: WARNING: virtual_width deviates too much\n"));
362 /* check if timing found is within the requested horizontal limits */
363 if (target->timing.h_display < low->timing.h_display
364 || target->timing.h_display > high->timing.h_display
365 || target->timing.h_sync_start < low->timing.h_sync_start
366 || target->timing.h_sync_start > high->timing.h_sync_start
367 || target->timing.h_sync_end < low->timing.h_sync_end
368 || target->timing.h_sync_end > high->timing.h_sync_end
369 || target->timing.h_total < low->timing.h_total
370 || target->timing.h_total > high->timing.h_total) {
371 /* BWindowScreen workaround: we accept everything except h_display deviations */
372 if (target->timing.h_display < low->timing.h_display
373 || target->timing.h_display > high->timing.h_display)
374 status = B_BAD_VALUE;
376 LOG(4, ("PROPOSEMODE: WARNING: horizontal timing deviates too much\n"));
379 /* check if timing found is within the requested vertical limits */
380 if (target->timing.v_display < low->timing.v_display
381 || target->timing.v_display > high->timing.v_display
382 || target->timing.v_sync_start < low->timing.v_sync_start
383 || target->timing.v_sync_start > high->timing.v_sync_start
384 || target->timing.v_sync_end < low->timing.v_sync_end
385 || target->timing.v_sync_end > high->timing.v_sync_end
386 || target->timing.v_total < low->timing.v_total
387 || target->timing.v_total > high->timing.v_total) {
388 /* BWindowScreen workaround: we accept everything except v_display deviations */
389 if (target->timing.v_display < low->timing.v_display
390 || target->timing.v_display > high->timing.v_display)
391 status = B_BAD_VALUE;
393 LOG(4, ("PROPOSEMODE: WARNING: vertical timing deviates too much\n"));
396 /* adjust pixelclock for possible timing modifications done above */
397 target->timing.pixel_clock = target_refresh * ((double)target->timing.h_total)
398 * ((double)target->timing.v_total) / 1000.0;
400 /* Now find the nearest valid pixelclock we actually can setup for the target mode,
401 * this also makes sure we don't generate more pixel bandwidth than the device can handle */
402 /* calculate settings, but do not actually test anything (that costs too much time!) */
403 result = head1_pix_pll_find(*target, &pix_clock_found, &m, &n, &p, 0);
404 /* update the target mode */
405 target->timing.pixel_clock = pix_clock_found * 1000;
407 /* note if we fell outside the limits */
408 if (target->timing.pixel_clock < low->timing.pixel_clock
409 || target->timing.pixel_clock > high->timing.pixel_clock) {
410 /* BWindowScreen workaround: we accept deviations <= 1Mhz */
411 if (target->timing.pixel_clock < low->timing.pixel_clock - 1000
412 || target->timing.pixel_clock > high->timing.pixel_clock + 1000)
413 status = B_BAD_VALUE;
415 LOG(4, ("PROPOSEMODE: WARNING: pixelclock deviates too much\n"));
418 mem_reservation = 0;
419 /* checkout space needed for hardcursor (if any) */
420 if (si->settings.hardcursor)
421 mem_reservation = 2048;
423 /* Reserve extra space as a workaround for certain bugs (see DriverInterface.h
424 * for an explanation). */
425 if (si->ps.card_arch < NV40A)
426 mem_reservation += PRE_NV40_OFFSET;
427 else
428 mem_reservation += NV40_PLUS_OFFSET;
430 /* memory requirement for frame buffer */
431 if (row_bytes * target->virtual_height > si->ps.memory_size - mem_reservation) {
432 target->virtual_height = (si->ps.memory_size - mem_reservation) / row_bytes;
434 if (target->virtual_height < target->timing.v_display) {
435 LOG(4,("PROPOSEMODE: not enough memory for current mode, aborted.\n"));
436 return B_ERROR;
439 LOG(4,("PROPOSEMODE: validated virtual_width %d, virtual_height %d pixels\n",
440 target->virtual_width, target->virtual_height));
442 if (target->virtual_height < low->virtual_height
443 || target->virtual_height > high->virtual_height) {
444 status = B_BAD_VALUE;
445 LOG(4, ("PROPOSEMODE: WARNING: virtual_height deviates too much\n"));
448 /* setup status flags */
449 LOG(1, ("PROPOSEMODE: initial modeflags: $%08x\n", target->flags));
450 /* preset to singlehead card without TVout, no overlay support and no hardcursor.
451 * also advice system that app_server and acc engine may touch the framebuffer
452 * simultaneously (fixed). */
453 target->flags &=
454 ~(DUALHEAD_CAPABLE | TV_CAPABLE | B_SUPPORTS_OVERLAYS | B_HARDWARE_CURSOR | B_IO_FB_NA);
455 /* we always allow parallel access (fixed), the DAC is always in 'enhanced'
456 * mode (fixed), and all modes support DPMS (fixed);
457 * We support scrolling and panning in every mode, so we 'send a signal' to
458 * BWindowScreen.CanControlFrameBuffer() by setting B_SCROLL. */
459 /* BTW: B_PARALLEL_ACCESS in combination with a hardcursor enables
460 * BDirectWindow windowed modes. */
461 target->flags |= (B_PARALLEL_ACCESS | B_8_BIT_DAC | B_DPMS | B_SCROLL);
463 /* determine the 'would be' max. pixelclock for the second DAC for the current videomode if dualhead were activated */
464 switch (target->space) {
465 case B_CMAP8:
466 max_vclk = si->ps.max_dac2_clock_8;
467 bpp = 1;
468 break;
469 case B_RGB15_LITTLE:
470 case B_RGB16_LITTLE:
471 max_vclk = si->ps.max_dac2_clock_16;
472 bpp = 2;
473 break;
474 case B_RGB24_LITTLE:
475 max_vclk = si->ps.max_dac2_clock_24;
476 bpp = 3;
477 break;
478 case B_RGB32_LITTLE:
479 max_vclk = si->ps.max_dac2_clock_32dh;
480 bpp = 4;
481 break;
482 default:
483 /* use fail-safe value */
484 max_vclk = si->ps.max_dac2_clock_32dh;
485 bpp = 4;
486 break;
489 /* set DUALHEAD_CAPABLE if suitable */
490 //fixme: update for independant secondary head use! (reserve fixed memory then)
491 if (si->ps.secondary_head && target->timing.pixel_clock <= (max_vclk * 1000)) {
492 switch (target->flags & DUALHEAD_BITS) {
493 case DUALHEAD_ON:
494 case DUALHEAD_SWITCH:
495 if (si->ps.memory_size - mem_reservation
496 >= row_bytes * target->virtual_height
497 && (uint16)(row_bytes / bpp) >= target->timing.h_display * 2)
498 target->flags |= DUALHEAD_CAPABLE;
499 break;
500 case DUALHEAD_CLONE:
501 if (si->ps.memory_size - mem_reservation
502 >= row_bytes * target->virtual_height)
503 target->flags |= DUALHEAD_CAPABLE;
504 break;
505 case DUALHEAD_OFF:
506 if (si->ps.memory_size - mem_reservation
507 >= row_bytes * target->virtual_height * 2)
508 target->flags |= DUALHEAD_CAPABLE;
509 break;
513 /* if not dualhead capable card clear dualhead flags */
514 if (!(target->flags & DUALHEAD_CAPABLE))
515 target->flags &= ~DUALHEAD_BITS;
517 /* set TV_CAPABLE if suitable: pixelclock is not important (defined by TVstandard) */
518 if (si->ps.tvout && BT_check_tvmode(*target))
519 target->flags |= TV_CAPABLE;
521 /* if not TVout capable card clear TVout flags */
522 if (!(target->flags & TV_CAPABLE))
523 target->flags &= ~TV_BITS;
525 /* make sure TV head assignment is sane */
526 if (target->flags & TV_BITS) {
527 if (!si->ps.secondary_head)
528 target->flags |= TV_PRIMARY;
529 else if ((target->flags & DUALHEAD_BITS) == DUALHEAD_OFF)
530 target->flags |= TV_PRIMARY;
531 } else
532 target->flags &= ~TV_PRIMARY;
534 /* set HARDWARE_CURSOR mode if suitable */
535 if (si->settings.hardcursor)
536 target->flags |= B_HARDWARE_CURSOR;
538 /* set SUPPORTS_OVERLAYS if suitable */
539 if (si->ps.card_type <= NV40 || si->ps.card_type == NV45)
540 target->flags |= B_SUPPORTS_OVERLAYS;
542 LOG(1, ("PROPOSEMODE: validated modeflags: $%08x\n", target->flags));
544 /* overrule timing command flags to be (fixed) blank_pedestal = 0.0IRE,
545 * progressive scan (fixed), and sync_on_green not avaible. */
546 target->timing.flags &= ~(B_BLANK_PEDESTAL | B_TIMING_INTERLACED | B_SYNC_ON_GREEN);
547 /* The HSYNC and VSYNC command flags are actually executed by the driver. */
549 if (status == B_OK)
550 LOG(4, ("PROPOSEMODE: completed successfully.\n"));
551 else
552 LOG(4, ("PROPOSEMODE: mode can be made, but outside given limits.\n"));
553 return status;
558 Return the number of modes this device will return from GET_MODE_LIST().
559 This is precalculated in create_mode_list (called from InitAccelerant stuff)
561 uint32
562 ACCELERANT_MODE_COUNT(void)
564 LOG(1, ("ACCELERANT_MODE_COUNT: the modelist contains %d modes\n",si->mode_count));
565 return si->mode_count;
569 /*! Copy the list of guaranteed supported video modes to the location provided.
571 status_t
572 GET_MODE_LIST(display_mode *dm)
574 LOG(1, ("GET_MODE_LIST: exporting the modelist created before.\n"));
576 memcpy(dm, my_mode_list, si->mode_count * sizeof(display_mode));
577 return B_OK;
581 static void checkAndAddMode(const display_mode *src, display_mode *dst)
583 uint32 j, pix_clk_range;
584 display_mode low, high;
585 color_space spaces[4] = {B_RGB32_LITTLE, B_RGB16_LITTLE, B_RGB15_LITTLE, B_CMAP8};
587 /* set ranges for acceptable values */
588 low = high = *src;
589 /* range is 6.25% of default clock: arbitrarily picked */
590 pix_clk_range = low.timing.pixel_clock >> 5;
591 low.timing.pixel_clock -= pix_clk_range;
592 high.timing.pixel_clock += pix_clk_range;
593 /* 'some cards need wider virtual widths for certain modes':
594 * Not true. They might need a wider pitch, but this is _not_ reflected in
595 * virtual_width, but in fbc.bytes_per_row. */
596 //So disable next line:
597 //high.virtual_width = 4096;
598 /* do it once for each depth we want to support */
599 for (j = 0; j < (sizeof(spaces) / sizeof(color_space)); j++) {
600 /* set target values */
601 dst[si->mode_count] = *src;
602 /* poke the specific space */
603 dst[si->mode_count].space = low.space = high.space = spaces[j];
604 /* ask for a compatible mode */
605 /* We have to check for B_OK, because otherwise the pix_clk_range
606 * won't be taken into account!! */
607 //So don't do this:
608 //if (PROPOSE_DISPLAY_MODE(dst, &low, &high) != B_ERROR) {
609 //Instead, do this:
610 if (PROPOSE_DISPLAY_MODE(&dst[si->mode_count], &low, &high) == B_OK) {
611 /* count it, so move on to next mode */
612 si->mode_count++;
618 /*! Create a list of display_modes to pass back to the caller.
620 status_t
621 create_mode_list(void)
623 size_t max_size;
624 uint32 i;
625 const display_mode *src;
626 display_mode *dst;
627 display_mode custom_mode;
629 /* figure out how big the list could be (4 colorspaces, 3 virtual types per mode), and adjust up to nearest multiple of B_PAGE_SIZE */
630 max_size = (((MODE_COUNT * 4 * 3) * sizeof(display_mode)) + (B_PAGE_SIZE-1)) & ~(B_PAGE_SIZE-1);
632 /* create an area to hold the info */
633 si->mode_area = my_mode_list_area = create_area("NV accelerant mode info",
634 (void **)&my_mode_list, B_ANY_ADDRESS, max_size, B_NO_LOCK,
635 B_READ_AREA | B_WRITE_AREA);
636 if (my_mode_list_area < B_OK)
637 return my_mode_list_area;
639 /* walk through our predefined list and see which modes fit this device */
640 src = mode_list;
641 dst = my_mode_list;
642 si->mode_count = 0;
643 for (i = 0; i < MODE_COUNT; i++) {
644 // standard mode
645 /* unfortunately Haiku's screenprefs panel does not support single head output explicitly, so we'd better
646 activate both heads always to (probably) mimic Radeon driver behaviour (for which this app was adapted) */
647 /* note please:
648 - this will have a big downside on hardware for which both heads have different resolution cababilities (i.e. Matrox);
649 - also (on laptops) this will shorten battery life a bit of course.. */
650 custom_mode = *src;
651 custom_mode.flags |= DUALHEAD_CLONE;
652 checkAndAddMode(&custom_mode, dst);
653 // double width mode for Haiku ScreenPrefs panel
654 /* note please: These modes should not be added. Instead the mode.flags should be used during setting screen as these will
655 automatically generate the needed other properties of the mode. Besides, virtual size is meant to be used for
656 pan&scan modes (viewports), i.e. for certain games.
657 The currently used method will fail programs that are meant to work pan@scan if the virtual width (or height) are
658 exactly twice the view area. */
659 custom_mode = *src;
660 custom_mode.virtual_width *= 2;
661 custom_mode.flags |= DUALHEAD_ON;
662 checkAndAddMode(&custom_mode, dst);
663 // we don't support double height modes
664 /* advance to next mode */
665 src++;
668 return B_OK;