1 /* 2 Copyright (c) 2002-2004, Thomas Kurschel 3 4 5 Part of Radeon accelerant 6 7 Everything concerning getting/testing display modes 8 */ 9 10 #include "radeon_accelerant.h" 11 #include "generic.h" 12 #include <string.h> 13 #include "GlobalData.h" 14 15 #include "crtc_regs.h" 16 #include "utils.h" 17 #include "set_mode.h" 18 19 // standard mode list 20 // all drivers contain this list - this should really be moved to 21 // something like the screen preferences panel 22 23 #define T_POSITIVE_SYNC (B_POSITIVE_HSYNC | B_POSITIVE_VSYNC) 24 #define MODE_FLAGS (B_8_BIT_DAC | B_HARDWARE_CURSOR | B_PARALLEL_ACCESS | B_DPMS | B_SUPPORTS_OVERLAYS) 25 //#define MODE_COUNT (sizeof (mode_list) / sizeof (display_mode)) 26 27 static const display_mode base_mode_list[] = { 28 // test for PAL 29 //{ { 25175, 640, 656, 752, 816, 480, 490, 492, 625, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(640X480X8.Z1) */ 30 // test for NTSC 31 //{ { 43956, 800, 824, 952, 992, 600, 632, 635, 740, T_POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(800X600X8.Z1) */ 32 33 { { 25175, 640, 656, 752, 800, 480, 490, 492, 525, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(640X480X8.Z1) */ 34 { { 27500, 640, 672, 768, 864, 480, 488, 494, 530, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* 640X480X60Hz */ 35 { { 30500, 640, 672, 768, 864, 480, 517, 523, 588, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* SVGA_640X480X60HzNI */ 36 { { 31500, 640, 664, 704, 832, 480, 489, 492, 520, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(640X480X8.Z1) */ 37 { { 31500, 640, 656, 720, 840, 480, 481, 484, 500, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(640X480X8.Z1) */ 38 { { 36000, 640, 696, 752, 832, 480, 481, 484, 509, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(640X480X8.Z1) */ 39 { { 25175, 640, 656, 752, 800, 400, 412, 414, 449, B_POSITIVE_VSYNC}, B_CMAP8, 640, 400, 0, 0, MODE_FLAGS}, /* 640x400 - www.epanorama.net/documents/pc/vga_timing.html) */ 40 { { 25175, 640, 656, 752, 800, 350, 387, 389, 449, B_POSITIVE_HSYNC}, B_CMAP8, 640, 350, 0, 0, MODE_FLAGS}, /* 640x350 - www.epanorama.net/documents/pc/vga_timing.html) */ 41 42 // NTSC non-isometric resolution (isometric resolution is 640x480) 43 { { 26720, 720, 736, 808, 896, 480, 481, 484, 497, B_POSITIVE_VSYNC}, B_CMAP8, 720, 480, 0, 0, MODE_FLAGS}, /* 720x480@60Hz according to GMTF */ 44 45 // PAL resolutions 46 { { 26570, 720, 736, 808, 896, 576, 577, 580, 593, B_POSITIVE_VSYNC}, B_CMAP8, 720, 576, 0, 0, MODE_FLAGS}, /* 720x576@50Hz according to GMTF */ 47 { { 28460, 768, 784, 864, 960, 576, 577, 580, 593, B_POSITIVE_VSYNC}, B_CMAP8, 768, 576, 0, 0, MODE_FLAGS}, /* 768x576@50Hz according to GMTF */ 48 49 { { 38100, 800, 832, 960, 1088, 600, 602, 606, 620, 0}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* SVGA_800X600X56HzNI */ 50 { { 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) */ 51 { { 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) */ 52 { { 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) */ 53 { { 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) */ 54 { { 65000, 1024, 1048, 1184, 1344, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1024X768X8.Z1) */ 55 { { 75000, 1024, 1048, 1184, 1328, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(1024X768X8.Z1) */ 56 { { 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) */ 57 { { 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) */ 58 { { 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) */ 59 { { 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) */ 60 { { 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) */ 61 62 { { 108000, 1280, 1376, 1488, 1800, 960, 961, 964, 1000, T_POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X960X8.Z1) - not in Be's list */ 63 { { 148500, 1280, 1344, 1504, 1728, 960, 961, 964, 1011, T_POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1280X960X8.Z1) - not in Be's list */ 64 65 { { 147100, 1680, 1784, 1968, 2256, 1050, 1051, 1054, 1087, T_POSITIVE_SYNC}, B_CMAP8, 1680, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1680X1050) */ 66 67 { { 108000, 1280, 1328, 1440, 1688, 1024, 1025, 1028, 1066, T_POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X1024X8.Z1) */ 68 { { 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) */ 69 { { 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) */ 70 { { 122600, 1400, 1488, 1640, 1880, 1050, 1051, 1054, 1087, T_POSITIVE_SYNC}, B_CMAP8, 1400, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1400X1050) */ 71 { { 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) */ 72 { { 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) */ 73 { { 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) */ 74 { { 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) */ 75 { { 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) */ 76 { { 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) */ 77 // widescreen resolutions, 16:10 78 { { 31300, 800, 848, 928, 1008, 500, 501, 504, 518, T_POSITIVE_SYNC}, B_CMAP8, 800, 500, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(800X500) */ 79 { { 52800, 1024, 1072, 1176, 1328, 640, 641, 644, 663, T_POSITIVE_SYNC}, B_CMAP8, 1024, 640, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1024X640) */ 80 { { 80135, 1280, 1344, 1480, 1680, 768, 769, 772, 795, T_POSITIVE_SYNC}, B_CMAP8, 1280, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X768) */ 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 { { 106500, 1440, 1520, 1672, 1904, 900, 901, 904, 932, T_POSITIVE_SYNC}, B_CMAP8, 1440, 900, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1440X900) */ 83 { { 147100, 1680, 1784, 1968, 2256, 1050, 1051, 1054, 1087, T_POSITIVE_SYNC}, B_CMAP8, 1680, 1050, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1680X1050) */ 84 /* 16:10 panel mode; 2.304M pixels */ 85 { { 160000, 1920, 2010, 2060, 2110, 1200, 1202, 1208, 1235, T_POSITIVE_SYNC}, B_CMAP8, 1920, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1920X1200) */ 86 // widescreen resolutions, 16:9 87 { { 74520, 1280, 1368, 1424, 1656, 720, 724, 730, 750, T_POSITIVE_SYNC}, B_CMAP8, 1280, 720, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X720) */ 88 }; 89 90 91 // convert Be colour space to Radeon data type 92 // returns true, if supported colour space 93 // space - Be colour space 94 // format - (out) Radeon data type 95 // bpp - (out) bytes per pixel 96 bool Radeon_GetFormat( int space, int *format, int *bpp ) 97 { 98 switch( space ) { 99 /*case 4: format = 1; bytpp = 0; break;*/ 100 case B_CMAP8: *format = 2; *bpp = 1; break; 101 case B_RGB15_LITTLE: *format = 3; *bpp = 2; break; /* 555 */ 102 case B_RGB16_LITTLE: *format = 4; *bpp = 2; break; /* 565 */ 103 case B_RGB24_LITTLE: *format = 5; *bpp = 3; break; /* RGB */ 104 case B_RGB32_LITTLE: *format = 6; *bpp = 4; break; /* xRGB */ 105 default: 106 SHOW_ERROR( 1, "Unsupported color space (%d)", space ); 107 return false; 108 } 109 110 return true; 111 } 112 113 114 // macros to convert between register values and pixels 115 #define H_DISPLAY_2REG( a ) ((a) / 8 - 1) 116 #define H_DISPLAY_2PIX( a ) (((a) + 1) * 8) 117 #define H_TOTAL_2REG( a ) ((a) / 8 - 1) 118 #define H_TOTAL_2PIX( a ) (((a) + 1) * 8) 119 #define H_SSTART_2REG( a ) ((a) - 8 + h_sync_fudge) 120 #define H_SSTART_2PIX( a ) ((a) + 8 - h_sync_fudge) 121 #define H_SWID_2REG( a ) ((a) / 8) 122 #define H_SWID_2PIX( a ) ((a) * 8) 123 124 #define V_2REG( a ) ((a) - 1) 125 #define V_2PIX( a ) ((a) + 1) 126 127 /* 128 Validate a target display mode is both 129 a) a valid display mode for this device and 130 b) falls between the contraints imposed by "low" and "high" 131 132 If the mode is not (or cannot) be made valid for this device, return B_ERROR. 133 If a valid mode can be constructed, but it does not fall within the limits, 134 return B_BAD_VALUE. 135 If the mode is both valid AND falls within the limits, return B_OK. 136 */ 137 static status_t 138 Radeon_ProposeDisplayMode(shared_info *si, crtc_info *crtc, 139 general_pll_info *pll, display_mode *target, 140 const display_mode *low, const display_mode *high) 141 { 142 status_t result = B_OK; 143 144 uint64 target_refresh; 145 bool want_same_width, want_same_height; 146 int format, bpp; 147 uint32 row_bytes; 148 int eff_virtual_width; 149 fp_info *flatpanel = &si->flatpanels[crtc->flatpanel_port]; 150 151 SHOW_FLOW( 4, "CRTC %d, DVI %d", (crtc == &si->crtc[0]) ? 0 : 1, crtc->flatpanel_port ); 152 SHOW_FLOW( 4, "X %d, virtX %d", target->timing.h_display, target->virtual_width); 153 SHOW_FLOW( 4, "fpRes %dx%d", flatpanel->panel_xres, flatpanel->panel_yres); 154 155 if (target->timing.h_total * target->timing.v_total == 0) 156 return B_BAD_VALUE; 157 158 // save refresh rate - we want to leave this (artifical) value untouched 159 // don't use floating point, we are in kernel mode 160 target_refresh = 161 (((uint64)target->timing.pixel_clock * 1000) << FIX_SHIFT) / 162 ((uint64)target->timing.h_total * target->timing.v_total); 163 164 want_same_width = target->timing.h_display == target->virtual_width; 165 want_same_height = target->timing.v_display == target->virtual_height; 166 167 if( !Radeon_GetFormat( target->space, &format, &bpp )) 168 return B_ERROR; 169 170 // for flat panels, check maximum resolution; 171 // all the other tricks (like fixed resolution and resulting scaling) 172 // are done automagically by set_display_mode 173 if( (crtc->chosen_displays & (dd_lvds | dd_dvi)) != 0 ) { 174 if( target->timing.h_display > flatpanel->panel_xres ) 175 target->timing.h_display = flatpanel->panel_xres; 176 177 if( target->timing.v_display > flatpanel->panel_yres ) 178 target->timing.v_display = flatpanel->panel_yres; 179 } 180 181 // for secondary flat panels there is no RMX unit for 182 // scaling up lower resolutions. Until we can do centered timings 183 // we need to disable the screen unless it is the native resolution. 184 // if the DVI input has a scaler we would need to know about it somehow... 185 if( (crtc->chosen_displays & dd_dvi_ext) != 0 ) { 186 SHOW_FLOW0( 4, "external (secondary) DVI cannot support non-native resolutions" ); 187 if( ( target->timing.h_display != flatpanel->panel_xres ) || 188 ( target->timing.v_display != flatpanel->panel_yres ) ) 189 return B_ERROR; 190 } 191 192 /* 193 // the TV-Out encoder can "only" handle up to 1024x768 194 if( (head->chosen_displays & (dd_ctv | dd_stv)) != 0 ) { 195 if( target->timing.h_display > 1024 ) 196 target->timing.h_display = 1024; 197 198 if( target->timing.v_display > 768 ) 199 target->timing.v_display = 768; 200 } 201 */ 202 203 // validate horizontal timings 204 { 205 int h_sync_fudge, h_display, h_sync_start, h_sync_wid, h_total; 206 207 h_display = target->timing.h_display; 208 h_sync_fudge = Radeon_GetHSyncFudge( crtc, format ); 209 h_sync_start = target->timing.h_sync_start; 210 h_sync_wid = target->timing.h_sync_end - target->timing.h_sync_start; 211 h_total = target->timing.h_total; 212 213 // make sure, display is not too small 214 // (I reckon Radeon doesn't care, but your monitor probably does) 215 if( h_display < 320 ) 216 h_display = 320; 217 // apply hardware restrictions 218 // as h_display is the smallest register, it's always possible 219 // to adjust other values to keep them in supported range 220 if( h_display > H_DISPLAY_2PIX( RADEON_CRTC_H_DISP >> RADEON_CRTC_H_DISP_SHIFT ) ) 221 h_display = H_DISPLAY_2PIX( RADEON_CRTC_H_DISP >> RADEON_CRTC_H_DISP_SHIFT ); 222 // round properly 223 h_display = H_DISPLAY_2PIX( H_DISPLAY_2REG( h_display )); 224 225 // ensure minimum time before sync 226 if( h_sync_start < h_display + 2*8 ) 227 h_sync_start = h_display + 2*8; 228 // sync has wider range than display are, so we won't collide there, 229 // but total width has same range as sync start, so leave some space 230 if( h_sync_start > H_SSTART_2PIX( RADEON_CRTC_H_SYNC_STRT_CHAR | RADEON_CRTC_H_SYNC_STRT_PIX ) - 4*8 ) 231 h_sync_start = H_SSTART_2PIX( RADEON_CRTC_H_SYNC_STRT_CHAR | RADEON_CRTC_H_SYNC_STRT_PIX ) - 4*8; 232 233 // ensure minimum sync length 234 if( h_sync_wid < H_SWID_2PIX( 3 )) 235 h_sync_wid = H_SWID_2PIX( 3 ); 236 // allowed range is quite small, so make sure sync isn't too long 237 if( h_sync_wid > H_SWID_2PIX( RADEON_CRTC_H_SYNC_WID >> RADEON_CRTC_H_SYNC_WID_SHIFT ) ) 238 h_sync_wid = H_SWID_2PIX( RADEON_CRTC_H_SYNC_WID >> RADEON_CRTC_H_SYNC_WID_SHIFT ); 239 // round properly 240 h_sync_wid = H_SWID_2PIX( H_SWID_2REG( h_sync_wid )); 241 242 // last but not least adapt total width 243 // "+7" is needed for rounding up: sync_start isn't rounded, but h_total is 244 if( h_total < h_sync_start + h_sync_wid + 1*8 + 7 ) 245 h_total = h_sync_start + h_sync_wid + 1*8 + 7; 246 // we may get a too long total width; this can only happen 247 // because sync is too long, so truncate sync accordingly 248 if( h_total > H_TOTAL_2PIX( RADEON_CRTC_H_TOTAL ) ) { 249 h_total = H_TOTAL_2PIX( RADEON_CRTC_H_TOTAL ); 250 h_sync_wid = min( h_sync_wid, h_total - h_sync_start ); 251 h_sync_wid = H_SWID_2PIX( H_SWID_2REG( h_sync_wid )); 252 } 253 // round properly 254 h_total = H_TOTAL_2PIX( H_TOTAL_2REG( h_total )); 255 256 target->timing.h_display = h_display; 257 target->timing.h_sync_start = h_sync_start; 258 target->timing.h_sync_end = h_sync_start + h_sync_wid; 259 target->timing.h_total = h_total; 260 } 261 262 // did we fall out of one of the limits? 263 if( target->timing.h_display < low->timing.h_display || 264 target->timing.h_display > high->timing.h_display || 265 target->timing.h_sync_start < low->timing.h_sync_start || 266 target->timing.h_sync_start > high->timing.h_sync_start || 267 target->timing.h_sync_end < low->timing.h_sync_end || 268 target->timing.h_sync_end > high->timing.h_sync_end || 269 target->timing.h_total < low->timing.h_total || 270 target->timing.h_total > high->timing.h_total) 271 { 272 SHOW_FLOW0( 4, "out of horizontal limits" ); 273 result = B_BAD_VALUE; 274 } 275 276 // validate vertical timings 277 { 278 int v_display, v_sync_start, v_sync_wid, v_total; 279 280 v_display = target->timing.v_display; 281 v_sync_start = target->timing.v_sync_start; 282 v_sync_wid = target->timing.v_sync_end - target->timing.v_sync_start; 283 v_total = target->timing.v_total; 284 285 // apply a reasonable minimal height to make monitor happy 286 if( v_display < 200 ) 287 v_display = 200; 288 // apply limits but make sure we have enough lines left for blank and sync 289 if( v_display > V_2PIX(RADEON_CRTC_V_DISP >> RADEON_CRTC_V_DISP_SHIFT) - 5) 290 v_display = V_2PIX(RADEON_CRTC_V_DISP >> RADEON_CRTC_V_DISP_SHIFT) - 5; 291 292 // leave at least one line before sync 293 // (some flat panel have zero gap here; probably, this leads to 294 // the infamous bright line at top of screen) 295 if( v_sync_start < v_display + 1 ) 296 v_sync_start = v_display + 1; 297 // apply hardware limit and leave some lines for sync 298 if( v_sync_start > V_2PIX(RADEON_CRTC_V_SYNC_STRT) - 4) 299 v_sync_start = V_2PIX(RADEON_CRTC_V_SYNC_STRT) - 4; 300 301 // don't make sync too short 302 if( v_sync_wid < 2 ) 303 v_sync_wid = 2; 304 // sync width is quite restricted 305 if( v_sync_wid > (RADEON_CRTC_V_SYNC_WID >> RADEON_CRTC_V_SYNC_WID_SHIFT)) 306 v_sync_wid = (RADEON_CRTC_V_SYNC_WID >> RADEON_CRTC_V_SYNC_WID_SHIFT); 307 308 // leave a gap of at least 1 line 309 if( v_total < v_sync_start + v_sync_wid + 1 ) 310 v_total = v_sync_start + v_sync_wid + 1; 311 // if too long, truncate it and adapt sync len 312 if( v_total > V_2PIX( RADEON_CRTC_V_TOTAL ) ) { 313 v_total = V_2PIX( RADEON_CRTC_V_TOTAL ); 314 v_sync_wid = min( v_sync_wid, v_total - v_sync_start - 4 ); 315 } 316 317 target->timing.v_display = v_display; 318 target->timing.v_sync_start = v_sync_start; 319 target->timing.v_sync_end = v_sync_start + v_sync_wid; 320 target->timing.v_total = v_total; 321 } 322 323 // did we fall out of one of the limits? 324 if( target->timing.v_display < low->timing.v_display || 325 target->timing.v_display > high->timing.v_display || 326 target->timing.v_sync_start < low->timing.v_sync_start || 327 target->timing.v_sync_start > high->timing.h_sync_start || 328 target->timing.v_sync_end < low->timing.v_sync_end || 329 target->timing.v_sync_end > high->timing.v_sync_end || 330 target->timing.v_total < low->timing.v_total || 331 target->timing.v_total > high->timing.v_total ) 332 { 333 SHOW_FLOW0( 4, "out of vertical limits" ); 334 result = B_BAD_VALUE; 335 } 336 337 // restore whished refresh rate 338 target->timing.pixel_clock = 339 ((uint64)target_refresh / 1000 * target->timing.h_total * target->timing.v_total + FIX_SCALE / 2) 340 >> FIX_SHIFT; 341 342 // apply PLL restrictions 343 if( target->timing.pixel_clock / 10 > pll->max_pll_freq || 344 target->timing.pixel_clock / 10 * 12 < pll->min_pll_freq ) 345 { 346 SHOW_ERROR( 4, "pixel_clock (%ld) out of range (%d, %d)", target->timing.pixel_clock, 347 pll->max_pll_freq * 10, pll->min_pll_freq / 12 ); 348 return B_ERROR; 349 } 350 351 // make sure virtual_size > visible_size 352 // additionally, restore virtual_size == visible_size if it was so on entry 353 if ((target->timing.h_display > target->virtual_width) || want_same_width) 354 target->virtual_width = target->timing.h_display; 355 if ((target->timing.v_display > target->virtual_height) || want_same_height) 356 target->virtual_height = target->timing.v_display; 357 358 // TBD: limit is taken from XFree86 359 // this is probably a CRTC limit; don't know about the accelerator limit (if any) 360 // h_display can be at most 512*8, so we don't risk h_virtual < h_display 361 // after applying this restriction 362 if (target->virtual_width > 1024*8) 363 target->virtual_width = 1024*8; 364 365 if (target->virtual_width < low->virtual_width || 366 target->virtual_width > high->virtual_width ) 367 { 368 SHOW_FLOW0( 4, "out of virtual horizontal limits" ); 369 result = B_BAD_VALUE; 370 } 371 372 // we may have to use a larger virtual width - 373 // take care of that when calculating memory consumption 374 eff_virtual_width = Radeon_RoundVWidth( target->virtual_height, bpp ); 375 376 // calculate rowbytes after we've nailed the virtual width 377 row_bytes = eff_virtual_width * bpp; 378 379 // if we haven't enough memory, reduce virtual height 380 // (some programs create back buffers by asking for a huge 381 // virtual screen; they actually want to know what is possible 382 // to adjust the number of back buffers according to amount 383 // of graphics memory) 384 385 // careful about additionally required memory: 386 // 1024 bytes are needed for hardware cursor 387 if ((row_bytes * target->virtual_height) > si->memory[mt_local].size - 1024 ) 388 target->virtual_height = (si->memory[mt_local].size - 1024) / row_bytes; 389 390 // make sure we haven't shrunk virtual height too much 391 if (target->virtual_height < target->timing.v_display) { 392 SHOW_ERROR( 4, "not enough memory for this mode (could show only %d of %d lines)", 393 target->virtual_height, target->timing.v_display ); 394 return B_ERROR; 395 } 396 397 if (target->virtual_height < low->virtual_height || 398 target->virtual_height > high->virtual_height ) 399 { 400 SHOW_FLOW0( 4, "out of virtual vertical limits" ); 401 result = B_BAD_VALUE; 402 } 403 404 // we ignore flags - in the sample driver, they did the same, 405 // so why bother? 406 return result; 407 } 408 409 // public function: return number of display modes returned by get_mode_list 410 uint32 ACCELERANT_MODE_COUNT( void ) 411 { 412 return ai->si->mode_count; 413 } 414 415 // public function: get list of standard display modes 416 // dm - modes are copied to here (to be allocated by caller) 417 status_t GET_MODE_LIST( display_mode *dm ) 418 { 419 memcpy( dm, ai->mode_list, ai->si->mode_count * sizeof(display_mode) ); 420 421 return B_OK; 422 } 423 424 425 static const color_space spaces[4] = { 426 B_CMAP8, B_RGB15_LITTLE, B_RGB16_LITTLE, B_RGB32_LITTLE 427 }; 428 429 // if given mode is possible on this card, add it to standard mode list 430 // mode - mode to add (colourspace is ignored but replaced 431 // by each officially supported colour space in turn) 432 // ignore_timing - don't care if timing has to be modified to make mode valid 433 // (used for fp modes - we just want their resolution) 434 static void checkAndAddMode( accelerator_info *ai, const display_mode *mode, bool ignore_timing ) 435 { 436 shared_info *si = ai->si; 437 uint i; 438 display_mode low, high; 439 uint32 pix_clk_range; 440 display_mode *dst; 441 442 if( ignore_timing ) { 443 // for fp modes: don't add mode if its resolution is already in official mode list 444 for( i = 0; i < si->mode_count; ++i ) { 445 if( ai->mode_list[i].timing.h_display == mode->timing.h_display && 446 ai->mode_list[i].timing.v_display == mode->timing.v_display && 447 ai->mode_list[i].virtual_width == mode->virtual_width && 448 ai->mode_list[i].virtual_height == mode->virtual_height ) 449 return; 450 } 451 } 452 453 // set ranges for acceptable values 454 low = high = *mode; 455 456 // range is 6.25% of default clock: arbitrarily picked 457 pix_clk_range = low.timing.pixel_clock >> 5; 458 low.timing.pixel_clock -= pix_clk_range; 459 high.timing.pixel_clock += pix_clk_range; 460 461 if( ignore_timing ) { 462 low.timing.h_total = 0; 463 low.timing.h_sync_start = 0; 464 low.timing.h_sync_end = 0; 465 low.timing.v_total = 0; 466 low.timing.v_sync_start = 0; 467 low.timing.v_sync_end = 0; 468 high.timing.h_total = 0xffff; 469 high.timing.h_sync_start = 0xffff; 470 high.timing.h_sync_end = 0xffff; 471 high.timing.v_total = 0xffff; 472 high.timing.v_sync_start = 0xffff; 473 high.timing.v_sync_end = 0xffff; 474 } 475 476 dst = &ai->mode_list[si->mode_count]; 477 478 // iterator through all colour spaces 479 for( i = 0; i < (sizeof(spaces) / sizeof(color_space)); i++ ) { 480 // check whether first port can handle it 481 *dst = *mode; 482 dst->space = low.space = high.space = spaces[i]; 483 484 if( Radeon_ProposeDisplayMode( si, &si->crtc[0], 485 &si->pll, dst, &low, &high ) == B_OK ) 486 { 487 si->mode_count++; 488 ++dst; 489 490 } else { 491 // it can't, so try second port 492 *dst = *mode; 493 dst->space = spaces[i]; 494 495 if( Radeon_ProposeDisplayMode( si, &si->crtc[1], 496 &si->pll, dst, &low, &high ) == B_OK ) 497 { 498 si->mode_count++; 499 ++dst; 500 501 } else 502 SHOW_FLOW( 4, "%ld, %ld not supported", dst->virtual_width, dst->virtual_height ); 503 } 504 } 505 } 506 507 508 // add display mode including span mode variations to offical list 509 static void checkAndAddMultiMode( accelerator_info *ai, const display_mode *mode, 510 bool ignore_timing ) 511 { 512 display_mode wide_mode; 513 514 SHOW_FLOW( 4, "%ld, %ld", mode->virtual_width, mode->virtual_height ); 515 516 // plain mode 517 checkAndAddMode( ai, mode, ignore_timing ); 518 519 // double width mode 520 wide_mode = *mode; 521 wide_mode.virtual_width *= 2; 522 wide_mode.flags |= B_SCROLL; 523 checkAndAddMode( ai, &wide_mode, ignore_timing ); 524 525 // double height mode 526 wide_mode = *mode; 527 wide_mode.virtual_height *= 2; 528 wide_mode.flags |= B_SCROLL; 529 checkAndAddMode( ai, &wide_mode, ignore_timing ); 530 } 531 532 // add display mode of flat panel to official list 533 static void addFPMode( accelerator_info *ai ) 534 { 535 shared_info *si = ai->si; 536 537 fp_info *fp_info = &si->flatpanels[0]; //todo fix the hardcoding what about ext dvi? 538 539 if( (ai->vc->connected_displays & (dd_dvi | dd_dvi_ext | dd_lvds)) != 0 ) { 540 display_mode mode; 541 SHOW_FLOW0( 2, "" ); 542 mode.virtual_width = mode.timing.h_display = fp_info->panel_xres; 543 mode.virtual_height = mode.timing.v_display = fp_info->panel_yres; 544 545 mode.timing.h_total = mode.timing.h_display + fp_info->h_blank; 546 mode.timing.h_sync_start = mode.timing.h_display + fp_info->h_over_plus; 547 mode.timing.h_sync_end = mode.timing.h_sync_start + fp_info->h_sync_width; 548 mode.timing.v_total = mode.timing.v_display + fp_info->v_blank; 549 mode.timing.v_sync_start = mode.timing.v_display + fp_info->v_over_plus; 550 mode.timing.v_sync_end = mode.timing.v_sync_start + fp_info->v_sync_width; 551 552 mode.timing.pixel_clock = fp_info->dot_clock; 553 554 // if we have no pixel clock, assume 60 Hz 555 // (as we don't program PLL in this case, it doesn't matter 556 // if it's wrong, we just want this resolution in the mode list) 557 if( mode.timing.pixel_clock == 0 ) { 558 // devide by 1000 as clock is in kHz 559 mode.timing.pixel_clock = 560 ((uint32)mode.timing.h_total * mode.timing.v_total * 60) / 1000; 561 } 562 563 mode.flags = MODE_FLAGS; 564 mode.h_display_start = 0; 565 mode.v_display_start = 0; 566 567 SHOW_FLOW( 2, "H: %4d %4d %4d %4d (v=%4d)", 568 mode.timing.h_display, mode.timing.h_sync_start, 569 mode.timing.h_sync_end, mode.timing.h_total, mode.virtual_width ); 570 SHOW_FLOW( 2, "V: %4d %4d %4d %4d (h=%4d)", 571 mode.timing.v_display, mode.timing.v_sync_start, 572 mode.timing.v_sync_end, mode.timing.v_total, mode.virtual_height ); 573 SHOW_FLOW( 2, "clk: %ld", mode.timing.pixel_clock ); 574 575 // flat panels seem to have strange timings; 576 // as we ignore user-supplied timing for FPs anyway, 577 // the mode can (and usually has to) be modified to be 578 // used for normal CRTs 579 checkAndAddMultiMode( ai, &mode, true ); 580 } 581 } 582 583 // create list of officially supported modes 584 status_t Radeon_CreateModeList( shared_info *si ) 585 { 586 size_t max_size; 587 uint i; 588 uint max_num_modes; 589 590 // maximum number of official modes: 591 // (predefined-modes + fp-modes) * number-of-colour-spaces * number-of-(non)-span-modes 592 max_num_modes = ((sizeof( base_mode_list ) / sizeof( base_mode_list[0] ) + 1) * 4 * 3); 593 594 max_size = (max_num_modes * sizeof(display_mode) + (B_PAGE_SIZE-1)) & ~(B_PAGE_SIZE-1); 595 596 si->mode_list_area = create_area("Radeon accelerant mode info", 597 (void **)&ai->mode_list, B_ANY_ADDRESS, 598 max_size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); 599 600 if( si->mode_list_area < B_OK ) 601 return si->mode_list_area; 602 603 si->mode_count = 0; 604 605 // check standard modes 606 for( i = 0; i < sizeof( base_mode_list ) / sizeof( base_mode_list[0] ); i++ ) 607 checkAndAddMultiMode( ai, &base_mode_list[i], false ); 608 609 // plus fp mode 610 addFPMode( ai ); 611 612 // as we've created the list ourself, we don't clone it 613 ai->mode_list_area = si->mode_list_area; 614 615 return B_OK; 616 } 617 618 619 //! public function: wraps for internal propose_display_mode 620 status_t 621 PROPOSE_DISPLAY_MODE(display_mode *target, const display_mode *low, 622 const display_mode *high) 623 { 624 virtual_card *vc = ai->vc; 625 shared_info *si = ai->si; 626 status_t result1, result2; 627 bool isTunneled; 628 status_t result; 629 display_mode tmp_target; 630 631 // check whether we got a tunneled settings command 632 result = Radeon_CheckMultiMonTunnel( vc, target, low, high, &isTunneled ); 633 if( isTunneled ) 634 return result; 635 636 // check how many heads are needed by target mode 637 tmp_target = *target; 638 Radeon_DetectMultiMode( vc, &tmp_target ); 639 640 // before checking multi-monitor mode, we must define a monitor signal routing 641 // TBD: this may be called a bit too frequently if someone scans available modes 642 // via successive Propose_Display_Mode; though this doesn't do any _real_ harm 643 // it leads to annoying distortions on screen!! 644 Radeon_DetectDisplays( ai); 645 Radeon_SetupDefaultMonitorRouting( 646 ai, Radeon_DifferentPorts( &tmp_target ), vc->use_laptop_panel ); 647 648 // transform to multi-screen mode first 649 Radeon_DetectMultiMode( vc, target ); 650 Radeon_VerifyMultiMode( vc, si, target ); 651 652 SHOW_FLOW0( 2, "wished:" ); 653 SHOW_FLOW( 2, "H: %4d %4d %4d %4d (v=%4d)", 654 target->timing.h_display, target->timing.h_sync_start, 655 target->timing.h_sync_end, target->timing.h_total, target->virtual_width ); 656 SHOW_FLOW( 2, "V: %4d %4d %4d %4d (h=%4d)", 657 target->timing.v_display, target->timing.v_sync_start, 658 target->timing.v_sync_end, target->timing.v_total, target->virtual_height ); 659 SHOW_FLOW( 2, "clk: %ld", target->timing.pixel_clock ); 660 661 // we must assure that each ProposeMode call doesn't tweak the mode in 662 // a way that it cannot be handled by the other port anymore 663 result1 = Radeon_ProposeDisplayMode( si, &si->crtc[0], 664 &si->pll, target, low, high ); 665 666 if( result1 == B_ERROR ) 667 return B_ERROR; 668 669 if( Radeon_NeedsSecondPort( target )) { 670 // if both ports are used, make sure both can handle mode 671 result2 = Radeon_ProposeDisplayMode( si, &si->crtc[1], 672 &si->pll, target, low, high ); 673 674 if( result2 == B_ERROR ) 675 return B_ERROR; 676 } else { 677 result2 = B_OK; 678 } 679 680 SHOW_INFO0( 2, "got:" ); 681 SHOW_INFO( 2, "H: %4d %4d %4d %4d (v=%4d)", 682 target->timing.h_display, target->timing.h_sync_start, 683 target->timing.h_sync_end, target->timing.h_total, target->virtual_width ); 684 SHOW_INFO( 2, "V: %4d %4d %4d %4d (h=%4d)", 685 target->timing.v_display, target->timing.v_sync_start, 686 target->timing.v_sync_end, target->timing.v_total, target->virtual_height ); 687 SHOW_INFO( 2, "clk: %ld", target->timing.pixel_clock ); 688 689 Radeon_HideMultiMode( vc, target ); 690 691 if( result1 == B_OK && result2 == B_OK ) 692 return B_OK; 693 else 694 return B_BAD_VALUE; 695 } 696