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