1 /* 2 Copyright (c) 2002, Thomas Kurschel 3 4 5 Part of Radeon kernel driver 6 7 BIOS detection and retrieval of vital data 8 9 Most of this data should be gathered directly, 10 especially monitor detection should be done on 11 demand so not all monitors need to be connected 12 during boot 13 */ 14 15 #include "radeon_driver.h" 16 #include "mmio.h" 17 #include "bios_regs.h" 18 #include "config_regs.h" 19 #include "memcntrl_regs.h" 20 #include "fp_regs.h" 21 #include "crtc_regs.h" 22 #include "radeon_bios.h" 23 #include "utils.h" 24 25 #include <stdio.h> 26 #include <string.h> 27 28 static const char ati_rom_sig[] = "761295520"; 29 static const char *radeon_sig[] = { 30 "RADEON", // r100 31 "RV100", // rv100 32 "U1", // rs100 (IGP320M) 33 "M6", // mobile version of r100 34 // probably an M6P; 35 // anyway - this is the card I wrote this driver for! 36 // (perhaps ATI tries to make the card incompatible to standard drivers) 37 "P6", 38 "RV200", // rv200 39 "M7", // m7 40 "RG6", // r200 (according to spec) 41 "RS200", // rs200 42 "R200", // r200 (8500 LE) 43 "R200AGP", // Fire GL E1 44 "M9" // guess: m9 45 "RV250", // rv250 R9100 46 "V280", // RV280 R9200 47 "R300", // R300 R9500 / R9700 48 "R350", // R350 R9800 49 "R360", // R360 R9800 XT 50 "V350", // RV350 R9600 51 "V360", // RV350 R9600 XT :guess 52 }; 53 54 55 // find address of ROM; 56 // this code is really nasty as maintaining the radeon signatures 57 // is almost impossible (the signatures provided by ATI are always out-dated); 58 // further, if there is more then one card built into the computer, we 59 // may detect the wrong BIOS! 60 // we have two possible solutions: 61 // 1. use the PCI location as stored in BIOS 62 // 2. verify the IO-base address as stored in BIOS 63 // I have no clue how these values are _written_ into the BIOS, and 64 // unfortunately, every BIOS does the detection in a different way, 65 // so I'm not sure which is the _right_ way of doing it 66 static char *Radeon_FindRom( rom_info *ri ) 67 { 68 uint32 segstart; 69 uint8 *rom_base; 70 char *rom; 71 int i,j; 72 73 for( segstart = 0x000c0000; segstart < 0x000f0000; segstart += 0x00001000 ) { 74 bool found = false; 75 76 // find ROM 77 rom_base = ri->bios_ptr + segstart - 0xc0000; 78 79 if( rom_base[0] != 0x55 || rom_base[1] != 0xaa ) 80 continue; 81 82 // find signature of ATI 83 rom = rom_base; 84 85 found = false; 86 87 for( i = 0; i < 128 - strlen( ati_rom_sig ); i++ ) { 88 if( ati_rom_sig[0] == rom_base[i] ) { 89 if( strncmp(ati_rom_sig, rom_base + i, strlen( ati_rom_sig )) == 0 ) { 90 found = true; 91 break; 92 } 93 } 94 } 95 96 if( !found ) 97 continue; 98 99 // find signature of card 100 found = false; 101 102 for( i = 0; i < 512; i++ ) { 103 for( j = 0; j < sizeof( radeon_sig ) / sizeof( radeon_sig[0] ); j++ ) { 104 if( radeon_sig[j][0] == rom_base[i] ) { 105 if( strncmp( radeon_sig[j], rom_base + i, strlen( radeon_sig[j] )) == 0 ) { 106 SHOW_INFO( 2, "Signature: %s", radeon_sig[j] ); 107 found = true; 108 break; 109 } 110 } 111 } 112 } 113 114 if( !found ) 115 continue; 116 117 SHOW_INFO( 2, "found ROM @0x%lx", segstart ); 118 return rom_base; 119 } 120 121 SHOW_INFO0( 2, "no ROM found" ); 122 return NULL; 123 } 124 125 126 // PLL info is stored in ROM, probably it's too easy to replace it 127 // and thus they produce cards with different timings 128 static void Radeon_GetPLLInfo( device_info *di ) 129 { 130 uint8 *bios_header; 131 PLL_BLOCK pll, *pll_info; 132 133 bios_header = di->rom.rom_ptr + *(uint16 *)(di->rom.rom_ptr + 0x48); 134 pll_info = (PLL_BLOCK *)(di->rom.rom_ptr + *(uint16 *)(bios_header + 0x30)); 135 136 memcpy( &pll, pll_info, sizeof( pll )); 137 138 di->pll.xclk = (uint32)pll.XCLK; 139 di->pll.ref_freq = (uint32)pll.PCLK_ref_freq; 140 di->pll.ref_div = (uint32)pll.PCLK_ref_divider; 141 di->pll.min_pll_freq = pll.PCLK_min_freq; 142 di->pll.max_pll_freq = pll.PCLK_max_freq; 143 144 SHOW_INFO( 2, "ref_clk=%ld, ref_div=%ld, xclk=%ld, min_freq=%ld, max_freq=%ld from BIOS", 145 di->pll.ref_freq, di->pll.ref_div, di->pll.xclk, 146 di->pll.min_pll_freq, di->pll.max_pll_freq ); 147 } 148 149 /* 150 const char *Mon2Str[] = { 151 "N/C", 152 "CRT", 153 "CRT", 154 "Laptop flatpanel", 155 "DVI (flatpanel)", 156 "secondary DVI (flatpanel) - unsupported", 157 "Composite TV", 158 "S-Video out" 159 };*/ 160 161 /* 162 // ask BIOS what kind of monitor is connected to each port 163 static void Radeon_GetMonType( device_info *di ) 164 { 165 unsigned int tmp; 166 167 SHOW_FLOW0( 3, "" ); 168 169 di->disp_type[0] = di->disp_type[1] = dt_none; 170 171 if (di->has_crtc2) { 172 tmp = INREG( di->regs, RADEON_BIOS_4_SCRATCH ); 173 174 // ordering of "if"s is important as multiple 175 // devices can be concurrently connected to one port 176 // (like both a CRT and a TV) 177 178 // primary port 179 // having flat-panel support is most important 180 if (tmp & 0x08) 181 di->disp_type[0] = dt_dvi; 182 else if (tmp & 0x4) 183 di->disp_type[0] = dt_lvds; 184 else if (tmp & 0x200) 185 di->disp_type[0] = dt_tv_crt; 186 else if (tmp & 0x10) 187 di->disp_type[0] = dt_ctv; 188 else if (tmp & 0x20) 189 di->disp_type[0] = dt_stv; 190 191 // secondary port 192 // having TV-Out support is more important then CRT support 193 // (CRT gets signal anyway) 194 if (tmp & 0x1000) 195 di->disp_type[1] = dt_ctv; 196 else if (tmp & 0x2000) 197 di->disp_type[1] = dt_stv; 198 else if (tmp & 0x2) 199 di->disp_type[1] = dt_crt; 200 else if (tmp & 0x800) 201 di->disp_type[1] = dt_dvi_ext; 202 else if (tmp & 0x400) 203 // this is unlikely - I only know about one LVDS unit 204 di->disp_type[1] = dt_lvds; 205 } else { 206 // regular Radeon 207 // TBD: no TV-Out detection 208 di->disp_type[0] = dt_none; 209 210 tmp = INREG( di->regs, RADEON_FP_GEN_CNTL); 211 212 if( tmp & RADEON_FP_EN_TMDS ) 213 di->disp_type[0] = dt_dvi; 214 else 215 di->disp_type[0] = dt_crt; 216 } 217 218 SHOW_INFO( 1, "BIOS reports %s on primary and %s on secondary port", 219 Mon2Str[di->disp_type[0]], Mon2Str[di->disp_type[1]]); 220 221 // remove unsupported devices 222 if( di->disp_type[0] >= dt_dvi_ext ) 223 di->disp_type[0] = dt_none; 224 if( di->disp_type[1] >= dt_dvi_ext ) 225 di->disp_type[1] = dt_none; 226 227 // HACK: overlays can only be shown on first CRTC; 228 // if there's nothing on first port, connect 229 // second port to first CRTC (proper signal routing 230 // is hopefully done by BIOS) 231 if( di->has_crtc2 ) { 232 if( di->disp_type[0] == dt_none && di->disp_type[1] == dt_crt ) { 233 di->disp_type[0] = dt_crt; 234 di->disp_type[1] = dt_none; 235 } 236 } 237 238 SHOW_INFO( 1, "Effective routing: %s on primary and %s on secondary port", 239 Mon2Str[di->disp_type[0]], Mon2Str[di->disp_type[1]]); 240 } 241 */ 242 243 // get flat panel info (does only make sense for Laptops 244 // with integrated display, but looking for it doesn't hurt, 245 // who knows which strange kind of combination is out there?) 246 static bool Radeon_GetBIOSDFPInfo( device_info *di ) 247 { 248 uint8 *bios_header; 249 uint16 fpi_offset; 250 FPI_BLOCK fpi; 251 char panel_name[30]; 252 int i; 253 254 bios_header = di->rom.rom_ptr + *(uint16 *)(di->rom.rom_ptr + 0x48); 255 256 fpi_offset = *(uint16 *)(bios_header + 0x40); 257 258 if( !fpi_offset ) { 259 di->fp_info.panel_pwr_delay = 200; 260 SHOW_ERROR0( 2, "No Panel Info Table found in BIOS" ); 261 return false; 262 } 263 264 memcpy( &fpi, di->rom.rom_ptr + fpi_offset, sizeof( fpi )); 265 266 memcpy( panel_name, &fpi.name, sizeof( fpi.name ) ); 267 panel_name[sizeof( fpi.name )] = 0; 268 269 SHOW_INFO( 2, "Panel ID string: %s", panel_name ); 270 271 di->fp_info.panel_xres = fpi.panel_xres; 272 di->fp_info.panel_yres = fpi.panel_yres; 273 274 SHOW_INFO( 2, "Panel Size from BIOS: %dx%d", 275 di->fp_info.panel_xres, di->fp_info.panel_yres); 276 277 di->fp_info.panel_pwr_delay = fpi.panel_pwr_delay; 278 if( di->fp_info.panel_pwr_delay > 2000 || di->fp_info.panel_pwr_delay < 0 ) 279 di->fp_info.panel_pwr_delay = 2000; 280 281 // there might be multiple supported resolutions stored; 282 // we are looking for native resolution 283 for( i = 0; i < 20; ++i ) { 284 uint16 fpi_timing_ofs; 285 FPI_TIMING_BLOCK fpi_timing; 286 287 fpi_timing_ofs = fpi.fpi_timing_ofs[i]; 288 289 if( fpi_timing_ofs == 0 ) 290 break; 291 292 memcpy( &fpi_timing, di->rom.rom_ptr + fpi_timing_ofs, sizeof( fpi_timing )); 293 294 if( fpi_timing.panel_xres != di->fp_info.panel_xres || 295 fpi_timing.panel_yres != di->fp_info.panel_yres ) 296 continue; 297 298 di->fp_info.h_blank = (fpi_timing.h_total - fpi_timing.h_display) * 8; 299 // TBD: seems like upper four bits of hsync_start contain garbage 300 di->fp_info.h_over_plus = ((fpi_timing.h_sync_start & 0xfff) - fpi_timing.h_display - 1) * 8; 301 di->fp_info.h_sync_width = fpi_timing.h_sync_width * 8; 302 di->fp_info.v_blank = fpi_timing.v_total - fpi_timing.v_display; 303 di->fp_info.v_over_plus = (fpi_timing.v_sync & 0x7ff) - fpi_timing.v_display; 304 di->fp_info.v_sync_width = (fpi_timing.v_sync & 0xf800) >> 11; 305 di->fp_info.dot_clock = fpi_timing.dot_clock * 10; 306 return true; 307 } 308 309 SHOW_ERROR0( 2, "Radeon: couldn't get Panel Timing from BIOS" ); 310 return false; 311 } 312 313 314 // try to reverse engineer DFP specification from 315 // timing currently set up in graphics cards registers 316 // (effectively, we hope that BIOS has set it up correctly 317 // and noone has messed registers up yet; let's pray) 318 static void Radeon_RevEnvDFPSize( device_info *di ) 319 { 320 vuint8 *regs = di->regs; 321 322 di->fp_info.panel_yres = 323 ((INREG( regs, RADEON_FP_VERT_STRETCH ) & RADEON_VERT_PANEL_SIZE) 324 >> RADEON_VERT_PANEL_SIZE_SHIFT) + 1; 325 326 di->fp_info.panel_xres = 327 (((INREG( regs, RADEON_FP_HORZ_STRETCH ) & RADEON_HORZ_PANEL_SIZE) 328 >> RADEON_HORZ_PANEL_SIZE_SHIFT) + 1) * 8; 329 330 SHOW_INFO( 2, "detected panel size from registers: %dx%d", 331 di->fp_info.panel_xres, di->fp_info.panel_yres); 332 } 333 334 335 // once more for getting precise timing 336 static void Radeon_RevEnvDFPTiming( device_info *di ) 337 { 338 vuint8 *regs = di->regs; 339 uint32 r; 340 uint16 a, b; 341 342 343 r = INREG( regs, RADEON_FP_CRTC_H_TOTAL_DISP ); 344 // the magic "4" was found by trial and error and probably stems from fudge (see crtc.c) 345 a = (r & RADEON_FP_CRTC_H_TOTAL_MASK)/* + 4*/; 346 b = (r & RADEON_FP_CRTC_H_DISP_MASK) >> RADEON_FP_CRTC_H_DISP_SHIFT; 347 di->fp_info.h_blank = (a - b) * 8; 348 349 SHOW_FLOW( 2, "h_total=%d, h_disp=%d", a * 8, b * 8 ); 350 351 r = INREG( regs, RADEON_FP_H_SYNC_STRT_WID ); 352 di->fp_info.h_over_plus = 353 ((r & RADEON_FP_H_SYNC_STRT_CHAR_MASK) 354 >> RADEON_FP_H_SYNC_STRT_CHAR_SHIFT) - b/* - 1*/; 355 di->fp_info.h_over_plus *= 8; 356 di->fp_info.h_sync_width = 357 ((r & RADEON_FP_H_SYNC_WID_MASK) 358 >> RADEON_FP_H_SYNC_WID_SHIFT); 359 // TBD: this seems to be wrong 360 // (my BIOS tells 112, this calculation leads to 24!) 361 di->fp_info.h_sync_width *= 8; 362 363 r = INREG( regs, RADEON_FP_CRTC_V_TOTAL_DISP ); 364 a = (r & RADEON_FP_CRTC_V_TOTAL_MASK)/* + 1*/; 365 b = (r & RADEON_FP_CRTC_V_DISP_MASK) >> RADEON_FP_CRTC_V_DISP_SHIFT; 366 di->fp_info.v_blank = a - b; 367 368 SHOW_FLOW( 2, "v_total=%d, v_disp=%d", a, b ); 369 370 r = INREG( regs, RADEON_FP_V_SYNC_STRT_WID ); 371 di->fp_info.v_over_plus = (r & RADEON_FP_V_SYNC_STRT_MASK) - b; 372 di->fp_info.v_sync_width = ((r & RADEON_FP_V_SYNC_WID_MASK) 373 >> RADEON_FP_V_SYNC_WID_SHIFT)/* + 1*/; 374 375 // standard CRTC 376 r = INREG( regs, RADEON_CRTC_H_TOTAL_DISP ); 377 a = (r & RADEON_CRTC_H_TOTAL); 378 b = (r & RADEON_CRTC_H_DISP) >> RADEON_CRTC_H_DISP_SHIFT; 379 di->fp_info.h_blank = (a - b) * 8; 380 381 SHOW_FLOW( 2, "h_total=%d, h_disp=%d", a * 8, b * 8 ); 382 383 r = INREG( regs, RADEON_CRTC_H_SYNC_STRT_WID ); 384 di->fp_info.h_over_plus = 385 ((r & RADEON_CRTC_H_SYNC_STRT_CHAR) 386 >> RADEON_CRTC_H_SYNC_STRT_CHAR_SHIFT) - b; 387 di->fp_info.h_over_plus *= 8; 388 di->fp_info.h_sync_width = 389 ((r & RADEON_CRTC_H_SYNC_WID) 390 >> RADEON_CRTC_H_SYNC_WID_SHIFT); 391 di->fp_info.h_sync_width *= 8; 392 393 r = INREG( regs, RADEON_CRTC_V_TOTAL_DISP ); 394 a = (r & RADEON_CRTC_V_TOTAL); 395 b = (r & RADEON_CRTC_V_DISP) >> RADEON_CRTC_V_DISP_SHIFT; 396 di->fp_info.v_blank = a - b; 397 398 SHOW_FLOW( 2, "v_total=%d, v_disp=%d", a, b ); 399 400 r = INREG( regs, RADEON_CRTC_V_SYNC_STRT_WID ); 401 di->fp_info.v_over_plus = (r & RADEON_CRTC_V_SYNC_STRT) - b; 402 di->fp_info.v_sync_width = ((r & RADEON_CRTC_V_SYNC_WID) 403 >> RADEON_CRTC_V_SYNC_WID_SHIFT); 404 } 405 406 407 /* 408 // get everything in terms of monitors connected to the card 409 static void Radeon_GetBIOSMon( device_info *di ) 410 { 411 Radeon_GetMonType( di ); 412 413 // reset all Flat Panel Info; 414 // it gets filled out step by step, and this way we know what's still missing 415 memset( &di->fp_info, 0, sizeof( di->fp_info )); 416 417 // we assume that the only fp port is combined with standard port 0 418 di->fp_info.disp_type = di->disp_type[0]; 419 420 if( di->is_mobility ) { 421 // there is a flat panel - get info about it 422 Radeon_GetBIOSDFPInfo( di ); 423 424 // if BIOS doesn't know, ask the registers 425 if( di->fp_info.panel_xres == 0 || di->fp_info.panel_yres == 0) 426 Radeon_RevEnvDFPSize( di ); 427 428 if( di->fp_info.h_blank == 0 || di->fp_info.v_blank == 0) 429 Radeon_RevEnvDFPTiming( di ); 430 431 SHOW_INFO( 2, "h_disp=%d, h_blank=%d, h_over_plus=%d, h_sync_width=%d", 432 di->fp_info.panel_xres, di->fp_info.h_blank, di->fp_info.h_over_plus, di->fp_info.h_sync_width ); 433 SHOW_INFO( 2, "v_disp=%d, v_blank=%d, v_over_plus=%d, v_sync_width=%d", 434 di->fp_info.panel_yres, di->fp_info.v_blank, di->fp_info.v_over_plus, di->fp_info.v_sync_width ); 435 SHOW_INFO( 2, "pixel_clock=%d", di->fp_info.dot_clock ); 436 } 437 } 438 */ 439 440 // get info about Laptop flat panel 441 static void Radeon_GetFPData( device_info *di ) 442 { 443 // reset all Flat Panel Info; 444 // it gets filled out step by step, and this way we know what's still missing 445 memset( &di->fp_info, 0, sizeof( di->fp_info )); 446 447 // we only use BIOS for Laptop flat panels 448 if( !di->is_mobility ) 449 return; 450 451 // ask BIOS about flat panel spec 452 Radeon_GetBIOSDFPInfo( di ); 453 454 // if BIOS doesn't know, ask the registers 455 if( di->fp_info.panel_xres == 0 || di->fp_info.panel_yres == 0) 456 Radeon_RevEnvDFPSize( di ); 457 458 if( di->fp_info.h_blank == 0 || di->fp_info.v_blank == 0) 459 Radeon_RevEnvDFPTiming( di ); 460 461 SHOW_INFO( 2, "h_disp=%d, h_blank=%d, h_over_plus=%d, h_sync_width=%d", 462 di->fp_info.panel_xres, di->fp_info.h_blank, di->fp_info.h_over_plus, di->fp_info.h_sync_width ); 463 SHOW_INFO( 2, "v_disp=%d, v_blank=%d, v_over_plus=%d, v_sync_width=%d", 464 di->fp_info.panel_yres, di->fp_info.v_blank, di->fp_info.v_over_plus, di->fp_info.v_sync_width ); 465 SHOW_INFO( 2, "pixel_clock=%d", di->fp_info.dot_clock ); 466 } 467 468 // detect amount of graphics memory 469 static void Radeon_DetectRAM( device_info *di ) 470 { 471 vuint8 *regs = di->regs; 472 473 if( !di->is_igp ) 474 di->local_mem_size = INREG( regs, RADEON_CONFIG_MEMSIZE ) & RADEON_CONFIG_MEMSIZE_MASK; 475 else { 476 uint32 tom; 477 478 tom = INREG( regs, RADEON_GC_NB_TOM ); 479 di->local_mem_size = ((tom >> 16) + 1 - (tom & 0xffff)) << 16; 480 } 481 482 // some production boards of m6 will return 0 if it's 8 MB 483 if( di->local_mem_size == 0 ) 484 di->local_mem_size = 8 * 1024 *1024; 485 486 switch( INREG( regs, RADEON_MEM_SDRAM_MODE_REG ) & RADEON_MEM_CFG_TYPE_MASK ) { 487 case RADEON_MEM_CFG_SDR: 488 // SDR SGRAM (2:1) 489 strcpy(di->ram_type, "SDR SGRAM"); 490 di->ram.ml = 4; 491 di->ram.MB = 4; 492 di->ram.Trcd = 1; 493 di->ram.Trp = 2; 494 di->ram.Twr = 1; 495 di->ram.CL = 2; 496 di->ram.loop_latency = 16; 497 di->ram.Rloop = 16; 498 di->ram.Tr2w = 0; 499 break; 500 501 case RADEON_MEM_CFG_DDR: 502 // DDR SGRAM 503 strcpy(di->ram_type, "DDR SGRAM"); 504 di->ram.ml = 4; 505 di->ram.MB = 4; 506 di->ram.Trcd = 3; 507 di->ram.Trp = 3; 508 di->ram.Twr = 2; 509 di->ram.CL = 3; 510 di->ram.Tr2w = 1; 511 di->ram.loop_latency = 16; 512 di->ram.Rloop = 16; 513 break; 514 515 // only one bit, so there's no default 516 } 517 518 SHOW_INFO( 1, "%ld MB %s found", di->local_mem_size / 1024 / 1024, 519 di->ram_type ); 520 521 /* if( di->local_mem_size > 64 * 1024 * 1024 ) { 522 di->local_mem_size = 64 * 1024 * 1024; 523 524 SHOW_INFO0( 1, "restricted to 64 MB" ); 525 }*/ 526 } 527 528 529 // map and verify card's BIOS to see whether this really is a Radeon 530 // (as we need BIOS for further info we have to make sure we use the right one) 531 status_t Radeon_MapBIOS( pci_info *pcii, rom_info *ri ) 532 { 533 char buffer[100]; 534 535 sprintf(buffer, "%04X_%04X_%02X%02X%02X bios", 536 pcii->vendor_id, pcii->device_id, 537 pcii->bus, pcii->device, pcii->function); 538 539 // we only scan BIOS at legacy location in first MB; 540 // using the PCI location would improve detection, especially 541 // if multiple graphics cards are installed 542 // BUT: BeOS uses the first graphics card it finds (sorted by 543 // device name), thus you couldn't choose in BIOS which card 544 // to use; checking the legacy location ensures that the card is 545 // only detected if it's the primary card 546 ri->phys_address = 0xc0000; 547 ri->size = 0x40000; 548 549 ri->bios_area = map_physical_memory( buffer, (void *)ri->phys_address, 550 ri->size, B_ANY_KERNEL_ADDRESS, B_READ_AREA, (void **)&ri->bios_ptr ); 551 if( ri->bios_area < 0 ) 552 return ri->bios_area; 553 554 ri->rom_ptr = Radeon_FindRom( ri ); 555 556 // on success, adjust physical address to found ROM 557 if( ri->rom_ptr != NULL ) 558 ri->phys_address += ri->rom_ptr - ri->bios_ptr; 559 560 return ri->rom_ptr != NULL ? B_OK : B_ERROR; 561 } 562 563 564 // unmap card's BIOS 565 void Radeon_UnmapBIOS( rom_info *ri ) 566 { 567 delete_area( ri->bios_area ); 568 569 ri->bios_ptr = ri->rom_ptr = NULL; 570 } 571 572 573 // get everything valuable from BIOS (BIOS must be mapped) 574 status_t Radeon_ReadBIOSData( device_info *di ) 575 { 576 shared_info dummy_si; 577 status_t result = B_OK; 578 579 // give Radeon_MapDevice something to play with 580 di->si = &dummy_si; 581 582 // don't map frame buffer - we don't know its proper size yet! 583 result = Radeon_MapDevice( di, true ); 584 if( result < 0 ) 585 goto err1; 586 587 Radeon_GetPLLInfo( di ); 588 Radeon_GetFPData( di ); 589 Radeon_DetectRAM( di ); 590 591 Radeon_UnmapDevice( di ); 592 593 err1: 594 di->si = NULL; 595 596 return result; 597 } 598