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