1 /* 2 * Copyright 2006-2008, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler, axeld@pinc-software.de 7 */ 8 9 10 #include "intel_extreme.h" 11 12 #include "AreaKeeper.h" 13 #include "driver.h" 14 #include "utility.h" 15 16 #include <unistd.h> 17 #include <stdio.h> 18 #include <string.h> 19 #include <errno.h> 20 21 #include <driver_settings.h> 22 #include <util/kernel_cpp.h> 23 24 25 #define TRACE_DEVICE 26 #ifdef TRACE_DEVICE 27 # define TRACE(x) dprintf x 28 #else 29 # define TRACE(x) ; 30 #endif 31 32 33 static void 34 init_overlay_registers(overlay_registers *registers) 35 { 36 memset(registers, 0, B_PAGE_SIZE); 37 38 registers->contrast_correction = 0x48; 39 registers->saturation_cos_correction = 0x9a; 40 // this by-passes contrast and saturation correction 41 } 42 43 44 static void 45 read_settings(size_t &memorySize, bool &hardwareCursor, bool &ignoreAllocated) 46 { 47 size_t size = 8; // 8 MB 48 hardwareCursor = false; 49 ignoreAllocated = false; 50 51 void *settings = load_driver_settings("intel_extreme"); 52 if (settings != NULL) { 53 size_t specified = 0; 54 const char *string = get_driver_parameter(settings, 55 "graphics_memory_size", "0", "0"); 56 if (string != NULL) 57 specified = atoi(string); 58 59 hardwareCursor = get_driver_boolean_parameter(settings, 60 "hardware_cursor", true, true); 61 62 ignoreAllocated = get_driver_boolean_parameter(settings, 63 "ignore_bios_allocated_memory", false, false); 64 65 unload_driver_settings(settings); 66 67 if (specified != 0) { 68 // take the next power of two 69 size_t desired = 1; 70 while (desired < specified) { 71 desired *= 2; 72 } 73 74 if (desired < 128) 75 size = desired; 76 } 77 } 78 79 memorySize = size << 20; 80 } 81 82 83 static size_t 84 determine_stolen_memory_size(intel_info &info) 85 { 86 // read stolen memory from the PCI configuration of the PCI bridge 87 uint16 memoryConfig = gPCI->read_pci_config(0, 0, 0, INTEL_GRAPHICS_MEMORY_CONTROL, 2); 88 size_t memorySize = 1 << 20; // 1 MB 89 size_t gttSize = 0; 90 91 if (info.device_type == (INTEL_TYPE_9xx | INTEL_TYPE_965)) { 92 switch (memoryConfig & i965_GTT_MASK) { 93 case i965_GTT_128K: 94 gttSize = 128 << 10; 95 break; 96 case i965_GTT_256K: 97 gttSize = 256 << 10; 98 break; 99 case i965_GTT_512K: 100 gttSize = 512 << 10; 101 break; 102 } 103 } else if (info.device_type == (INTEL_TYPE_9xx | INTEL_TYPE_G33)) { 104 switch (memoryConfig & G33_GTT_MASK) { 105 case G33_GTT_1M: 106 gttSize = 1 << 20; 107 break; 108 case G33_GTT_2M: 109 gttSize = 2 << 20; 110 break; 111 } 112 } else { 113 // older models have the GTT as large as their frame buffer mapping 114 // TODO: check if the i9xx version works with the i8xx chips as well 115 size_t frameBufferSize = 0; 116 if ((info.device_type & INTEL_TYPE_FAMILY_MASK) == INTEL_TYPE_8xx) { 117 if ((info.device_type & INTEL_TYPE_83x) != 0 118 && (memoryConfig & MEMORY_MASK) == i830_FRAME_BUFFER_64M) 119 frameBufferSize = 64 << 20; 120 else 121 frameBufferSize = 128 << 20; 122 } else if ((info.device_type & INTEL_TYPE_FAMILY_MASK) == INTEL_TYPE_9xx) 123 frameBufferSize = info.pci->u.h0.base_register_sizes[2]; 124 125 gttSize = frameBufferSize / 1024; 126 } 127 128 // TODO: test with different models! 129 130 if (info.device_type == (INTEL_TYPE_8xx | INTEL_TYPE_83x)) { 131 switch (memoryConfig & STOLEN_MEMORY_MASK) { 132 case i830_LOCAL_MEMORY_ONLY: 133 // TODO: determine its size! 134 break; 135 case i830_STOLEN_512K: 136 memorySize >>= 1; 137 break; 138 case i830_STOLEN_8M: 139 memorySize *= 8; 140 break; 141 } 142 } else if (info.device_type == (INTEL_TYPE_8xx | INTEL_TYPE_85x) 143 || (info.device_type & INTEL_TYPE_FAMILY_MASK) == INTEL_TYPE_9xx) { 144 switch (memoryConfig & STOLEN_MEMORY_MASK) { 145 case i855_STOLEN_MEMORY_4M: 146 memorySize *= 4; 147 break; 148 case i855_STOLEN_MEMORY_8M: 149 memorySize *= 8; 150 break; 151 case i855_STOLEN_MEMORY_16M: 152 memorySize *= 16; 153 break; 154 case i855_STOLEN_MEMORY_32M: 155 memorySize *= 32; 156 break; 157 case i855_STOLEN_MEMORY_48M: 158 memorySize *= 48; 159 break; 160 case i855_STOLEN_MEMORY_64M: 161 memorySize *= 64; 162 break; 163 case i855_STOLEN_MEMORY_128M: 164 memorySize *= 128; 165 break; 166 case i855_STOLEN_MEMORY_256M: 167 memorySize *= 256; 168 break; 169 } 170 } 171 172 return memorySize - gttSize - 4096; 173 } 174 175 176 static void 177 set_gtt_entry(intel_info &info, uint32 offset, uint8 *physicalAddress) 178 { 179 write32(info.gtt_base + (offset >> GTT_PAGE_SHIFT), 180 (uint32)physicalAddress | GTT_ENTRY_VALID); 181 } 182 183 184 static int32 185 release_vblank_sem(intel_info &info) 186 { 187 int32 count; 188 if (get_sem_count(info.shared_info->vblank_sem, &count) == B_OK 189 && count < 0) { 190 release_sem_etc(info.shared_info->vblank_sem, -count, 191 B_DO_NOT_RESCHEDULE); 192 return B_INVOKE_SCHEDULER; 193 } 194 195 return B_HANDLED_INTERRUPT; 196 } 197 198 199 static int32 200 intel_interrupt_handler(void *data) 201 { 202 intel_info &info = *(intel_info *)data; 203 204 uint32 identity = read16(info.registers + INTEL_INTERRUPT_IDENTITY); 205 if (identity == 0) 206 return B_UNHANDLED_INTERRUPT; 207 208 int32 handled = B_HANDLED_INTERRUPT; 209 210 if ((identity & INTERRUPT_VBLANK) != 0) { 211 handled = release_vblank_sem(info); 212 213 // make sure we'll get another one of those 214 write32(info.registers + INTEL_DISPLAY_A_PIPE_STATUS, DISPLAY_PIPE_VBLANK_STATUS); 215 } 216 217 // setting the bit clears it! 218 write16(info.registers + INTEL_INTERRUPT_IDENTITY, identity); 219 220 return handled; 221 } 222 223 224 static void 225 init_interrupt_handler(intel_info &info) 226 { 227 info.shared_info->vblank_sem = create_sem(0, "intel extreme vblank"); 228 if (info.shared_info->vblank_sem < B_OK) 229 return; 230 231 status_t status = B_OK; 232 233 // We need to change the owner of the sem to the calling team (usually the 234 // app_server), because userland apps cannot acquire kernel semaphores 235 thread_id thread = find_thread(NULL); 236 thread_info threadInfo; 237 if (get_thread_info(thread, &threadInfo) != B_OK 238 || set_sem_owner(info.shared_info->vblank_sem, threadInfo.team) != B_OK) { 239 status = B_ERROR; 240 } 241 242 if (status == B_OK 243 && info.pci->u.h0.interrupt_pin != 0x00 && info.pci->u.h0.interrupt_line != 0xff) { 244 // we've gotten an interrupt line for us to use 245 246 info.fake_interrupts = false; 247 248 status = install_io_interrupt_handler(info.pci->u.h0.interrupt_line, 249 &intel_interrupt_handler, (void *)&info, 0); 250 if (status == B_OK) { 251 // enable interrupts - we only want VBLANK interrupts 252 write16(info.registers + INTEL_INTERRUPT_ENABLED, 253 read16(info.registers + INTEL_INTERRUPT_ENABLED) | INTERRUPT_VBLANK); 254 write16(info.registers + INTEL_INTERRUPT_MASK, ~INTERRUPT_VBLANK); 255 256 write32(info.registers + INTEL_DISPLAY_A_PIPE_STATUS, 257 DISPLAY_PIPE_VBLANK_STATUS); 258 write16(info.registers + INTEL_INTERRUPT_IDENTITY, ~0); 259 } 260 } 261 if (status < B_OK) { 262 // there is no interrupt reserved for us, or we couldn't install our interrupt 263 // handler, let's fake the vblank interrupt for our clients using a timer 264 // interrupt 265 info.fake_interrupts = true; 266 267 // TODO: fake interrupts! 268 status = B_ERROR; 269 } 270 271 if (status < B_OK) { 272 delete_sem(info.shared_info->vblank_sem); 273 info.shared_info->vblank_sem = B_ERROR; 274 } 275 } 276 277 278 // #pragma mark - 279 280 281 status_t 282 intel_extreme_init(intel_info &info) 283 { 284 AreaKeeper sharedCreator; 285 info.shared_area = sharedCreator.Create("intel extreme shared info", 286 (void **)&info.shared_info, B_ANY_KERNEL_ADDRESS, 287 ROUND_TO_PAGE_SIZE(sizeof(intel_shared_info)) + 3 * B_PAGE_SIZE, 288 B_FULL_LOCK, 0); 289 if (info.shared_area < B_OK) 290 return info.shared_area; 291 292 memset((void *)info.shared_info, 0, sizeof(intel_shared_info)); 293 294 int fbIndex = 0; 295 int mmioIndex = 1; 296 if ((info.device_type & INTEL_TYPE_FAMILY_MASK) == INTEL_TYPE_9xx) { 297 // for some reason Intel saw the need to change the order of the mappings 298 // with the introduction of the i9xx family 299 mmioIndex = 0; 300 fbIndex = 2; 301 } 302 303 // evaluate driver settings, if any 304 305 bool ignoreBIOSAllocated; 306 bool hardwareCursor; 307 size_t totalSize; 308 read_settings(totalSize, hardwareCursor, ignoreBIOSAllocated); 309 310 // Determine the amount of "stolen" (ie. reserved by the BIOS) graphics memory 311 // and see if we need to allocate some more. 312 // TODO: make it allocate the memory on demand! 313 314 size_t stolenSize = ignoreBIOSAllocated ? 0 : determine_stolen_memory_size(info); 315 totalSize = max_c(totalSize, stolenSize); 316 317 dprintf(DEVICE_NAME ": detected %ld MB of stolen memory, reserving %ld MB total\n", 318 stolenSize >> 20, totalSize >> 20); 319 320 AreaKeeper additionalMemoryCreator; 321 uint8 *additionalMemory; 322 323 if (stolenSize < totalSize) { 324 // Every device should have at least 8 MB - we could also allocate them 325 // on demand only, but we're lazy here... 326 // TODO: overlay seems to have problem when the memory pages are too 327 // far spreaded - that's why we're using B_CONTIGUOUS for now. 328 info.additional_memory_area = additionalMemoryCreator.Create("intel additional memory", 329 (void **)&additionalMemory, B_ANY_KERNEL_ADDRESS, 330 totalSize - stolenSize, B_CONTIGUOUS /*B_FULL_LOCK*/, 0); 331 if (info.additional_memory_area < B_OK) 332 return info.additional_memory_area; 333 } else 334 info.additional_memory_area = B_ERROR; 335 336 // map frame buffer, try to map it write combined 337 338 AreaKeeper graphicsMapper; 339 info.graphics_memory_area = graphicsMapper.Map("intel extreme graphics memory", 340 (void *)info.pci->u.h0.base_registers[fbIndex], 341 totalSize, B_ANY_KERNEL_BLOCK_ADDRESS /*| B_MTR_WC*/, 342 B_READ_AREA | B_WRITE_AREA, (void **)&info.graphics_memory); 343 if (graphicsMapper.InitCheck() < B_OK) { 344 // try again without write combining 345 dprintf(DEVICE_NAME ": enabling write combined mode failed.\n"); 346 347 info.graphics_memory_area = graphicsMapper.Map("intel extreme graphics memory", 348 (void *)info.pci->u.h0.base_registers[fbIndex], 349 totalSize/*info.pci->u.h0.base_register_sizes[0]*/, B_ANY_KERNEL_BLOCK_ADDRESS, 350 B_READ_AREA | B_WRITE_AREA, (void **)&info.graphics_memory); 351 } 352 if (graphicsMapper.InitCheck() < B_OK) { 353 dprintf(DEVICE_NAME ": could not map frame buffer!\n"); 354 return info.graphics_memory_area; 355 } 356 357 // memory mapped I/O 358 359 AreaKeeper mmioMapper; 360 info.registers_area = mmioMapper.Map("intel extreme mmio", 361 (void *)info.pci->u.h0.base_registers[mmioIndex], 362 info.pci->u.h0.base_register_sizes[mmioIndex], 363 B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_WRITE_AREA /*0*/, (void **)&info.registers); 364 if (mmioMapper.InitCheck() < B_OK) { 365 dprintf(DEVICE_NAME ": could not map memory I/O!\n"); 366 return info.registers_area; 367 } 368 369 // make sure bus master, memory-mapped I/O, and frame buffer is enabled 370 set_pci_config(info.pci, PCI_command, 2, get_pci_config(info.pci, PCI_command, 2) 371 | PCI_command_io | PCI_command_memory | PCI_command_master); 372 373 // init graphics memory manager 374 375 AreaKeeper gttMapper; 376 info.gtt_area = -1; 377 378 if ((info.device_type & INTEL_TYPE_9xx) != 0) { 379 if ((info.device_type & INTEL_TYPE_GROUP_MASK) == INTEL_TYPE_965) { 380 info.gtt_base = info.registers + i965_GTT_BASE; 381 info.gtt_size = i965_GTT_SIZE; 382 } else { 383 info.gtt_area = gttMapper.Map("intel extreme gtt", 384 (void *)info.pci->u.h0.base_registers[3], totalSize / 1024, 385 B_ANY_KERNEL_ADDRESS, 0, (void **)&info.gtt_base); 386 if (gttMapper.InitCheck() < B_OK) { 387 dprintf(DEVICE_NAME ": could not map GTT area!\n"); 388 return info.gtt_area; 389 } 390 info.gtt_size = totalSize / 1024; 391 } 392 } else { 393 info.gtt_base = info.registers + i830_GTT_BASE; 394 info.gtt_size = i830_GTT_SIZE; 395 } 396 397 info.memory_manager = mem_init("intel extreme memory manager", 0, totalSize, 1024, 398 min_c(totalSize / 1024, 512)); 399 if (info.memory_manager == NULL) 400 return B_NO_MEMORY; 401 402 // reserve ring buffer memory (currently, this memory is placed in 403 // the graphics memory), but this could bring us problems with 404 // write combining... 405 406 ring_buffer &primary = info.shared_info->primary_ring_buffer; 407 if (mem_alloc(info.memory_manager, 4 * B_PAGE_SIZE, &info, 408 &primary.handle, &primary.offset) == B_OK) { 409 primary.register_base = INTEL_PRIMARY_RING_BUFFER; 410 primary.size = 4 * B_PAGE_SIZE; 411 primary.base = info.graphics_memory + primary.offset; 412 } 413 414 ring_buffer &secondary = info.shared_info->secondary_ring_buffer; 415 if (mem_alloc(info.memory_manager, B_PAGE_SIZE, &info, 416 &secondary.handle, &secondary.offset) == B_OK) { 417 secondary.register_base = INTEL_SECONDARY_RING_BUFFER_0; 418 secondary.size = B_PAGE_SIZE; 419 secondary.base = info.graphics_memory + secondary.offset; 420 } 421 422 // Fix some problems on certain chips (taken from X driver) 423 // TODO: clean this up 424 if (info.pci->device_id == 0x2a02 || info.pci->device_id == 0x2a12) { 425 dprintf("i965GM/i965GME quirk\n"); 426 write32(info.registers + 0x6204, (1L << 29)); 427 } else { 428 dprintf("i965 quirk\n"); 429 write32(info.registers + 0x6204, (1L << 29) | (1L << 23)); 430 } 431 write32(info.registers + 0x7408, 0x10); 432 433 // no errors, so keep areas and mappings 434 sharedCreator.Detach(); 435 additionalMemoryCreator.Detach(); 436 graphicsMapper.Detach(); 437 mmioMapper.Detach(); 438 gttMapper.Detach(); 439 440 info.shared_info->graphics_memory_area = info.graphics_memory_area; 441 info.shared_info->registers_area = info.registers_area; 442 info.shared_info->graphics_memory = info.graphics_memory; 443 info.shared_info->physical_graphics_memory = (uint8 *)info.pci->u.h0.base_registers[0]; 444 445 info.shared_info->graphics_memory_size = totalSize; 446 info.shared_info->frame_buffer_offset = 0; 447 info.shared_info->dpms_mode = B_DPMS_ON; 448 449 if ((info.device_type & INTEL_TYPE_9xx) != 0) { 450 info.shared_info->pll_info.reference_frequency = 96000; // 96 kHz 451 info.shared_info->pll_info.max_frequency = 400000; // 400 MHz RAM DAC speed 452 info.shared_info->pll_info.min_frequency = 20000; // 20 MHz 453 } else { 454 info.shared_info->pll_info.reference_frequency = 48000; // 48 kHz 455 info.shared_info->pll_info.max_frequency = 350000; // 350 MHz RAM DAC speed 456 info.shared_info->pll_info.min_frequency = 25000; // 25 MHz 457 } 458 459 info.shared_info->pll_info.divisor_register = INTEL_DISPLAY_A_PLL_DIVISOR_0; 460 461 info.shared_info->device_type = info.device_type; 462 #ifdef __HAIKU__ 463 strlcpy(info.shared_info->device_identifier, info.device_identifier, 464 sizeof(info.shared_info->device_identifier)); 465 #else 466 strcpy(info.shared_info->device_identifier, info.device_identifier); 467 #endif 468 469 // setup overlay registers 470 471 if (info.device_type == (INTEL_TYPE_9xx | INTEL_TYPE_G33)) { 472 if (mem_alloc(info.memory_manager, B_PAGE_SIZE, &info, 473 &info.overlay_handle, &info.overlay_offset) == B_OK) { 474 info.overlay_registers = (overlay_registers *)(info.graphics_memory 475 + info.overlay_offset); 476 info.shared_info->overlay_offset = info.overlay_offset; 477 } 478 } else { 479 info.overlay_registers = (overlay_registers *)((uint8 *)info.shared_info 480 + ROUND_TO_PAGE_SIZE(sizeof(intel_shared_info))); 481 } 482 init_overlay_registers(info.overlay_registers); 483 484 physical_entry physicalEntry; 485 get_memory_map(info.overlay_registers, sizeof(overlay_registers), &physicalEntry, 1); 486 info.shared_info->physical_overlay_registers = (uint8 *)physicalEntry.address; 487 488 // The hardware status page and the cursor memory share one area with 489 // the overlay registers and the shared info 490 491 uint8 *base = (uint8 *)info.shared_info + ROUND_TO_PAGE_SIZE(sizeof(intel_shared_info)); 492 get_memory_map(base + B_PAGE_SIZE, B_PAGE_SIZE, &physicalEntry, 1); 493 info.shared_info->physical_status_page = (uint8 *)physicalEntry.address; 494 495 get_memory_map(base + 2 * B_PAGE_SIZE, B_PAGE_SIZE, &physicalEntry, 1); 496 info.shared_info->physical_cursor_memory = (uint8 *)physicalEntry.address; 497 498 // setup the GTT to point to include the additional memory 499 500 if (stolenSize < totalSize) { 501 for (size_t offset = stolenSize; offset < totalSize;) { 502 physical_entry physicalEntry; 503 get_memory_map(additionalMemory + offset - stolenSize, 504 totalSize - offset, &physicalEntry, 1); 505 506 for (size_t i = 0; i < physicalEntry.size; i += B_PAGE_SIZE) { 507 set_gtt_entry(info, offset + i, (uint8 *)physicalEntry.address + i); 508 } 509 510 offset += physicalEntry.size; 511 } 512 } 513 514 // We also need to map the cursor memory into the GTT 515 516 info.shared_info->hardware_cursor_enabled = hardwareCursor; 517 if (hardwareCursor) { 518 // This could also be part of the usual graphics memory, but since we 519 // need to make sure it's not in the graphics local memory (and I don't 520 // even know yet how to determine that a chip has local memory...), we 521 // keep the current strategy and put it into the shared area. 522 // Unfortunately, the GTT is not readable until it has been written into 523 // the double buffered register set; we cannot get its original contents. 524 525 set_gtt_entry(info, totalSize, info.shared_info->physical_cursor_memory); 526 info.shared_info->cursor_buffer_offset = totalSize; 527 } 528 529 init_interrupt_handler(info); 530 531 TRACE((DEVICE_NAME "intel_extreme_init() completed successfully!\n")); 532 return B_OK; 533 } 534 535 536 void 537 intel_extreme_uninit(intel_info &info) 538 { 539 TRACE((DEVICE_NAME": intel_extreme_uninit()\n")); 540 541 if (!info.fake_interrupts && info.shared_info->vblank_sem > 0) { 542 // disable interrupt generation 543 write16(info.registers + INTEL_INTERRUPT_ENABLED, 0); 544 write16(info.registers + INTEL_INTERRUPT_MASK, ~0); 545 546 remove_io_interrupt_handler(info.pci->u.h0.interrupt_line, 547 intel_interrupt_handler, &info); 548 } 549 550 mem_destroy(info.memory_manager); 551 552 delete_area(info.graphics_memory_area); 553 delete_area(info.gtt_area); 554 delete_area(info.registers_area); 555 delete_area(info.shared_area); 556 557 // we may or may not have allocated additional graphics memory 558 if (info.additional_memory_area >= B_OK) 559 delete_area(info.additional_memory_area); 560 } 561 562