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