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