1 /* 2 Copyright 1999, Be Incorporated. All Rights Reserved. 3 This file may be used under the terms of the Be Sample Code License. 4 5 Other authors for NV driver: 6 Mark Watson, 7 Rudolf Cornelissen 9/2002-1/2016 8 */ 9 10 #define MODULE_BIT 0x00400000 11 12 #include "acc_std.h" 13 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)) 18 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) */ 96 }; 97 98 99 // transform official mode to internal, multi-screen mode enhanced mode 100 static void Haiku_DetectTranslateMultiMode(display_mode *mode) 101 { 102 mode->flags &= ~DUALHEAD_BITS; 103 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; 119 } 120 } 121 122 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 ) 125 { 126 if( (mode->timing.flags & RADEON_MODE_MULTIMON_REQUEST) != 0 && 127 (mode->timing.flags & RADEON_MODE_MULTIMON_REPLY) == 0 ) 128 { 129 mode->timing.flags &= ~RADEON_MODE_MULTIMON_REQUEST; 130 mode->timing.flags |= RADEON_MODE_MULTIMON_REPLY; 131 132 // still process request, just in case someone set this flag 133 // combination by mistake 134 135 *isTunneled = true; 136 return B_OK; 137 } 138 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' ) 145 { 146 *isTunneled = false; 147 return B_OK; 148 } 149 150 *isTunneled = true; 151 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; 156 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).. */ 165 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; 182 } 183 break; 184 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; 191 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; 198 } 199 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)); 202 203 return B_ERROR; 204 } 205 206 207 /*! 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 213 */ 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; 223 */ 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) 228 { 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; 244 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. */ 248 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). */ 253 254 if (si->haiku_prefs_used) { 255 // check how many heads are needed by target mode 256 Haiku_DetectTranslateMultiMode(target); 257 } 258 259 LOG(1, ("PROPOSEMODE: (ENTER) requested virtual_width %d, virtual_height %d\n", 260 target->virtual_width, target->virtual_height)); 261 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; 271 } 272 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; 289 } 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; 296 } 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; 303 } 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; 314 } 315 break; 316 } 317 } 318 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; 331 } 332 } 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; 338 } 339 } 340 } 341 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; 347 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; 353 } 354 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")); 360 } 361 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; 375 376 LOG(4, ("PROPOSEMODE: WARNING: horizontal timing deviates too much\n")); 377 } 378 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; 392 393 LOG(4, ("PROPOSEMODE: WARNING: vertical timing deviates too much\n")); 394 } 395 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; 399 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; 406 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; 414 415 LOG(4, ("PROPOSEMODE: WARNING: pixelclock deviates too much\n")); 416 } 417 418 mem_reservation = 0; 419 /* checkout space needed for hardcursor (if any) */ 420 if (si->settings.hardcursor) 421 mem_reservation = 2048; 422 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; 429 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; 433 } 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; 437 } 438 439 LOG(4,("PROPOSEMODE: validated virtual_width %d, virtual_height %d pixels\n", 440 target->virtual_width, target->virtual_height)); 441 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")); 446 } 447 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); 462 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; 487 } 488 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; 510 } 511 } 512 513 /* if not dualhead capable card clear dualhead flags */ 514 if (!(target->flags & DUALHEAD_CAPABLE)) 515 target->flags &= ~DUALHEAD_BITS; 516 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; 520 521 /* if not TVout capable card clear TVout flags */ 522 if (!(target->flags & TV_CAPABLE)) 523 target->flags &= ~TV_BITS; 524 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; 533 534 /* set HARDWARE_CURSOR mode if suitable */ 535 if (si->settings.hardcursor) 536 target->flags |= B_HARDWARE_CURSOR; 537 538 /* set SUPPORTS_OVERLAYS if suitable */ 539 if (si->ps.card_type <= NV40 || si->ps.card_type == NV45) 540 target->flags |= B_SUPPORTS_OVERLAYS; 541 542 LOG(1, ("PROPOSEMODE: validated modeflags: $%08x\n", target->flags)); 543 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. */ 548 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; 554 } 555 556 557 /*! 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) 560 */ 561 uint32 562 ACCELERANT_MODE_COUNT(void) 563 { 564 LOG(1, ("ACCELERANT_MODE_COUNT: the modelist contains %d modes\n",si->mode_count)); 565 return si->mode_count; 566 } 567 568 569 /*! Copy the list of guaranteed supported video modes to the location provided. 570 */ 571 status_t 572 GET_MODE_LIST(display_mode *dm) 573 { 574 LOG(1, ("GET_MODE_LIST: exporting the modelist created before.\n")); 575 576 memcpy(dm, my_mode_list, si->mode_count * sizeof(display_mode)); 577 return B_OK; 578 } 579 580 581 static void checkAndAddMode(const display_mode *src, display_mode *dst) 582 { 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}; 586 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++; 613 } 614 } 615 } 616 617 618 /*! Create a list of display_modes to pass back to the caller. 619 */ 620 status_t 621 create_mode_list(void) 622 { 623 size_t max_size; 624 uint32 i; 625 const display_mode *src; 626 display_mode *dst; 627 display_mode custom_mode; 628 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); 631 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; 638 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++; 666 } 667 668 return B_OK; 669 } 670