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