1 /* 2 * Copyright 2006-2014, 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 * Alexander von Gluck IV, kallisti5@unixzen.com 8 */ 9 10 11 #include "intel_extreme.h" 12 13 #include "AreaKeeper.h" 14 #include <unistd.h> 15 #include <stdio.h> 16 #include <string.h> 17 #include <errno.h> 18 19 #include <boot_item.h> 20 #include <driver_settings.h> 21 #include <util/kernel_cpp.h> 22 23 #include <vesa_info.h> 24 25 #include "driver.h" 26 #include "power.h" 27 #include "utility.h" 28 29 30 #define TRACE_INTELEXTREME 31 #ifdef TRACE_INTELEXTREME 32 # define TRACE(x...) dprintf("intel_extreme: " x) 33 #else 34 # define TRACE(x) ; 35 #endif 36 37 #define ERROR(x...) dprintf("intel_extreme: " x) 38 #define CALLED(x...) TRACE("intel_extreme: CALLED %s\n", __PRETTY_FUNCTION__) 39 40 41 static void 42 init_overlay_registers(overlay_registers* registers) 43 { 44 memset(registers, 0, B_PAGE_SIZE); 45 46 registers->contrast_correction = 0x48; 47 registers->saturation_cos_correction = 0x9a; 48 // this by-passes contrast and saturation correction 49 } 50 51 52 static void 53 read_settings(bool &hardwareCursor) 54 { 55 hardwareCursor = false; 56 57 void* settings = load_driver_settings("intel_extreme"); 58 if (settings != NULL) { 59 hardwareCursor = get_driver_boolean_parameter(settings, 60 "hardware_cursor", true, true); 61 62 unload_driver_settings(settings); 63 } 64 } 65 66 67 static int32 68 release_vblank_sem(intel_info &info) 69 { 70 int32 count; 71 if (get_sem_count(info.shared_info->vblank_sem, &count) == B_OK 72 && count < 0) { 73 release_sem_etc(info.shared_info->vblank_sem, -count, 74 B_DO_NOT_RESCHEDULE); 75 return B_INVOKE_SCHEDULER; 76 } 77 78 return B_HANDLED_INTERRUPT; 79 } 80 81 82 static int32 83 intel_interrupt_handler(void* data) 84 { 85 intel_info &info = *(intel_info*)data; 86 uint32 reg = find_reg(info, INTEL_INTERRUPT_IDENTITY); 87 uint16 identity = read16(info, reg); 88 if (identity == 0) 89 return B_UNHANDLED_INTERRUPT; 90 91 int32 handled = B_HANDLED_INTERRUPT; 92 93 while (identity != 0) { 94 95 // TODO: verify that these aren't actually the same 96 bool hasPCH = info.device_type.HasPlatformControlHub(); 97 uint16 mask; 98 99 // Intel changed the PCH register mapping between Sandy Bridge and the 100 // later generations (Ivy Bridge and up). 101 if (info.device_type.InGroup(INTEL_GROUP_SNB)) { 102 mask = hasPCH ? PCH_INTERRUPT_VBLANK_PIPEA_SNB 103 : INTERRUPT_VBLANK_PIPEA; 104 if ((identity & mask) != 0) { 105 handled = release_vblank_sem(info); 106 107 // make sure we'll get another one of those 108 write32(info, INTEL_DISPLAY_A_PIPE_STATUS, 109 DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED); 110 } 111 112 mask = hasPCH ? PCH_INTERRUPT_VBLANK_PIPEB_SNB 113 : INTERRUPT_VBLANK_PIPEB; 114 if ((identity & mask) != 0) { 115 handled = release_vblank_sem(info); 116 117 // make sure we'll get another one of those 118 write32(info, INTEL_DISPLAY_B_PIPE_STATUS, 119 DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED); 120 } 121 } else { 122 mask = hasPCH ? PCH_INTERRUPT_VBLANK_PIPEA 123 : INTERRUPT_VBLANK_PIPEA; 124 if ((identity & mask) != 0) { 125 handled = release_vblank_sem(info); 126 127 // make sure we'll get another one of those 128 write32(info, INTEL_DISPLAY_A_PIPE_STATUS, 129 DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED); 130 } 131 132 mask = hasPCH ? PCH_INTERRUPT_VBLANK_PIPEB 133 : INTERRUPT_VBLANK_PIPEB; 134 if ((identity & mask) != 0) { 135 handled = release_vblank_sem(info); 136 137 // make sure we'll get another one of those 138 write32(info, INTEL_DISPLAY_B_PIPE_STATUS, 139 DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED); 140 } 141 142 #if 0 143 // FIXME we don't have supprot for the 3rd pipe yet 144 mask = hasPCH ? PCH_INTERRUPT_VBLANK_PIPEC 145 : 0; 146 if ((identity & mask) != 0) { 147 handled = release_vblank_sem(info); 148 149 // make sure we'll get another one of those 150 write32(info, INTEL_DISPLAY_C_PIPE_STATUS, 151 DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED); 152 } 153 #endif 154 } 155 156 // setting the bit clears it! 157 write16(info, reg, identity); 158 identity = read16(info, reg); 159 } 160 161 return handled; 162 } 163 164 165 static void 166 init_interrupt_handler(intel_info &info) 167 { 168 info.shared_info->vblank_sem = create_sem(0, "intel extreme vblank"); 169 if (info.shared_info->vblank_sem < B_OK) 170 return; 171 172 status_t status = B_OK; 173 174 // We need to change the owner of the sem to the calling team (usually the 175 // app_server), because userland apps cannot acquire kernel semaphores 176 thread_id thread = find_thread(NULL); 177 thread_info threadInfo; 178 if (get_thread_info(thread, &threadInfo) != B_OK 179 || set_sem_owner(info.shared_info->vblank_sem, threadInfo.team) 180 != B_OK) { 181 status = B_ERROR; 182 } 183 184 // Find the right interrupt vector, using MSIs if available. 185 info.irq = 0xff; 186 info.use_msi = false; 187 if (info.pci->u.h0.interrupt_pin != 0x00) 188 info.irq = info.pci->u.h0.interrupt_line; 189 if (gPCIx86Module != NULL && gPCIx86Module->get_msi_count(info.pci->bus, 190 info.pci->device, info.pci->function) >= 1) { 191 uint8 msiVector = 0; 192 if (gPCIx86Module->configure_msi(info.pci->bus, info.pci->device, 193 info.pci->function, 1, &msiVector) == B_OK 194 && gPCIx86Module->enable_msi(info.pci->bus, info.pci->device, 195 info.pci->function) == B_OK) { 196 ERROR("using message signaled interrupts\n"); 197 info.irq = msiVector; 198 info.use_msi = true; 199 } 200 } 201 202 if (status == B_OK && info.irq != 0xff) { 203 // we've gotten an interrupt line for us to use 204 205 info.fake_interrupts = false; 206 207 status = install_io_interrupt_handler(info.irq, 208 &intel_interrupt_handler, (void*)&info, 0); 209 if (status == B_OK) { 210 write32(info, INTEL_DISPLAY_A_PIPE_STATUS, 211 DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED); 212 write32(info, INTEL_DISPLAY_B_PIPE_STATUS, 213 DISPLAY_PIPE_VBLANK_STATUS | DISPLAY_PIPE_VBLANK_ENABLED); 214 215 write16(info, find_reg(info, INTEL_INTERRUPT_IDENTITY), ~0); 216 217 // enable interrupts - we only want VBLANK interrupts 218 bool hasPCH = info.device_type.HasPlatformControlHub(); 219 uint16 enable = hasPCH 220 ? (PCH_INTERRUPT_VBLANK_PIPEA | PCH_INTERRUPT_VBLANK_PIPEB) 221 : (INTERRUPT_VBLANK_PIPEA | INTERRUPT_VBLANK_PIPEB); 222 223 write16(info, find_reg(info, INTEL_INTERRUPT_ENABLED), enable); 224 write16(info, find_reg(info, INTEL_INTERRUPT_MASK), ~enable); 225 } 226 } 227 if (status < B_OK) { 228 // There is no interrupt reserved for us, or we couldn't install our 229 // interrupt handler, let's fake the vblank interrupt for our clients 230 // using a timer interrupt 231 info.fake_interrupts = true; 232 233 // TODO: fake interrupts! 234 TRACE("Fake interrupt mode (no PCI interrupt line assigned\n"); 235 status = B_ERROR; 236 } 237 238 if (status < B_OK) { 239 delete_sem(info.shared_info->vblank_sem); 240 info.shared_info->vblank_sem = B_ERROR; 241 } 242 } 243 244 245 // #pragma mark - 246 247 248 status_t 249 intel_free_memory(intel_info &info, addr_t base) 250 { 251 return gGART->free_memory(info.aperture, base); 252 } 253 254 255 status_t 256 intel_allocate_memory(intel_info &info, size_t size, size_t alignment, 257 uint32 flags, addr_t* _base, phys_addr_t* _physicalBase) 258 { 259 return gGART->allocate_memory(info.aperture, size, alignment, 260 flags, _base, _physicalBase); 261 } 262 263 264 status_t 265 intel_extreme_init(intel_info &info) 266 { 267 CALLED(); 268 info.aperture = gGART->map_aperture(info.pci->bus, info.pci->device, 269 info.pci->function, 0, &info.aperture_base); 270 if (info.aperture < B_OK) { 271 ERROR("error: could not map GART aperture! (%s)\n", strerror(info.aperture)); 272 return info.aperture; 273 } 274 275 AreaKeeper sharedCreator; 276 info.shared_area = sharedCreator.Create("intel extreme shared info", 277 (void**)&info.shared_info, B_ANY_KERNEL_ADDRESS, 278 ROUND_TO_PAGE_SIZE(sizeof(intel_shared_info)) + 3 * B_PAGE_SIZE, 279 B_FULL_LOCK, 0); 280 if (info.shared_area < B_OK) { 281 ERROR("error: could not create shared area!\n"); 282 gGART->unmap_aperture(info.aperture); 283 return info.shared_area; 284 } 285 286 memset((void*)info.shared_info, 0, sizeof(intel_shared_info)); 287 288 int fbIndex = 0; 289 int mmioIndex = 1; 290 if (info.device_type.Generation() >= 3) { 291 // For some reason Intel saw the need to change the order of the 292 // mappings with the introduction of the i9xx family 293 mmioIndex = 0; 294 fbIndex = 2; 295 } 296 297 // evaluate driver settings, if any 298 299 bool hardwareCursor; 300 read_settings(hardwareCursor); 301 302 // memory mapped I/O 303 304 // TODO: registers are mapped twice (by us and intel_gart), maybe we 305 // can share it between the drivers 306 307 AreaKeeper mmioMapper; 308 info.registers_area = mmioMapper.Map("intel extreme mmio", 309 info.pci->u.h0.base_registers[mmioIndex], 310 info.pci->u.h0.base_register_sizes[mmioIndex], 311 B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, 312 (void**)&info.registers); 313 if (mmioMapper.InitCheck() < B_OK) { 314 ERROR("error: could not map memory I/O!\n"); 315 gGART->unmap_aperture(info.aperture); 316 return info.registers_area; 317 } 318 319 ERROR("Init Intel generation %" B_PRId32 " GPU %s PCH split.\n", 320 info.device_type.Generation(), 321 info.device_type.HasPlatformControlHub() ? "with" : "without"); 322 323 uint32* blocks = info.shared_info->register_blocks; 324 blocks[REGISTER_BLOCK(REGS_FLAT)] = 0; 325 326 // setup the register blocks for the different architectures 327 if (info.device_type.HasPlatformControlHub()) { 328 // PCH based platforms (IronLake through ultra-low-power Broadwells) 329 blocks[REGISTER_BLOCK(REGS_NORTH_SHARED)] 330 = PCH_NORTH_SHARED_REGISTER_BASE; 331 blocks[REGISTER_BLOCK(REGS_NORTH_PIPE_AND_PORT)] 332 = PCH_NORTH_PIPE_AND_PORT_REGISTER_BASE; 333 blocks[REGISTER_BLOCK(REGS_NORTH_PLANE_CONTROL)] 334 = PCH_NORTH_PLANE_CONTROL_REGISTER_BASE; 335 blocks[REGISTER_BLOCK(REGS_SOUTH_SHARED)] 336 = PCH_SOUTH_SHARED_REGISTER_BASE; 337 blocks[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT)] 338 = PCH_SOUTH_TRANSCODER_AND_PORT_REGISTER_BASE; 339 } else { 340 // (G)MCH/ICH based platforms 341 blocks[REGISTER_BLOCK(REGS_NORTH_SHARED)] 342 = MCH_SHARED_REGISTER_BASE; 343 blocks[REGISTER_BLOCK(REGS_NORTH_PIPE_AND_PORT)] 344 = MCH_PIPE_AND_PORT_REGISTER_BASE; 345 blocks[REGISTER_BLOCK(REGS_NORTH_PLANE_CONTROL)] 346 = MCH_PLANE_CONTROL_REGISTER_BASE; 347 blocks[REGISTER_BLOCK(REGS_SOUTH_SHARED)] 348 = ICH_SHARED_REGISTER_BASE; 349 blocks[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT)] 350 = ICH_PORT_REGISTER_BASE; 351 } 352 353 // Everything in the display PRM gets +0x180000 354 if (info.device_type.InGroup(INTEL_GROUP_VLV)) { 355 // "I nearly got violent with the hw guys when they told me..." 356 blocks[REGISTER_BLOCK(REGS_SOUTH_SHARED)] += VLV_DISPLAY_BASE; 357 blocks[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT)] += VLV_DISPLAY_BASE; 358 } 359 360 TRACE("REGS_NORTH_SHARED: 0x%X\n", 361 blocks[REGISTER_BLOCK(REGS_NORTH_SHARED)]); 362 TRACE("REGS_NORTH_PIPE_AND_PORT: 0x%X\n", 363 blocks[REGISTER_BLOCK(REGS_NORTH_PIPE_AND_PORT)]); 364 TRACE("REGS_NORTH_PLANE_CONTROL: 0x%X\n", 365 blocks[REGISTER_BLOCK(REGS_NORTH_PLANE_CONTROL)]); 366 TRACE("REGS_SOUTH_SHARED: 0x%X\n", 367 blocks[REGISTER_BLOCK(REGS_SOUTH_SHARED)]); 368 TRACE("REGS_SOUTH_TRANSCODER_PORT: 0x%X\n", 369 blocks[REGISTER_BLOCK(REGS_SOUTH_TRANSCODER_PORT)]); 370 371 // make sure bus master, memory-mapped I/O, and frame buffer is enabled 372 set_pci_config(info.pci, PCI_command, 2, get_pci_config(info.pci, 373 PCI_command, 2) | PCI_command_io | PCI_command_memory 374 | PCI_command_master); 375 376 // reserve ring buffer memory (currently, this memory is placed in 377 // the graphics memory), but this could bring us problems with 378 // write combining... 379 380 ring_buffer &primary = info.shared_info->primary_ring_buffer; 381 if (intel_allocate_memory(info, 16 * B_PAGE_SIZE, 0, 0, 382 (addr_t*)&primary.base) == B_OK) { 383 primary.register_base = INTEL_PRIMARY_RING_BUFFER; 384 primary.size = 16 * B_PAGE_SIZE; 385 primary.offset = (addr_t)primary.base - info.aperture_base; 386 } 387 388 // Enable clock gating 389 intel_en_gating(info); 390 391 // Enable automatic gpu downclocking if we can to save power 392 intel_en_downclock(info); 393 394 // no errors, so keep areas and mappings 395 sharedCreator.Detach(); 396 mmioMapper.Detach(); 397 398 aperture_info apertureInfo; 399 gGART->get_aperture_info(info.aperture, &apertureInfo); 400 401 info.shared_info->registers_area = info.registers_area; 402 info.shared_info->graphics_memory = (uint8*)info.aperture_base; 403 info.shared_info->physical_graphics_memory = apertureInfo.physical_base; 404 info.shared_info->graphics_memory_size = apertureInfo.size; 405 info.shared_info->frame_buffer = 0; 406 info.shared_info->dpms_mode = B_DPMS_ON; 407 408 // Pull VBIOS panel mode for later use 409 info.shared_info->got_vbt = get_lvds_mode_from_bios( 410 &info.shared_info->panel_mode); 411 412 /* at least 855gm can't drive more than one head at time */ 413 if (info.device_type.InFamily(INTEL_FAMILY_8xx)) 414 info.shared_info->single_head_locked = 1; 415 416 if (info.device_type.InFamily(INTEL_FAMILY_9xx) 417 | info.device_type.InFamily(INTEL_FAMILY_SER5)) { 418 info.shared_info->pll_info.reference_frequency = 96000; // 96 kHz 419 info.shared_info->pll_info.max_frequency = 400000; 420 // 400 MHz RAM DAC speed 421 info.shared_info->pll_info.min_frequency = 20000; // 20 MHz 422 } else { 423 info.shared_info->pll_info.reference_frequency = 48000; // 48 kHz 424 info.shared_info->pll_info.max_frequency = 350000; 425 // 350 MHz RAM DAC speed 426 info.shared_info->pll_info.min_frequency = 25000; // 25 MHz 427 } 428 429 info.shared_info->pll_info.divisor_register = INTEL_DISPLAY_A_PLL_DIVISOR_0; 430 431 info.shared_info->device_type = info.device_type; 432 #ifdef __HAIKU__ 433 strlcpy(info.shared_info->device_identifier, info.device_identifier, 434 sizeof(info.shared_info->device_identifier)); 435 #else 436 strcpy(info.shared_info->device_identifier, info.device_identifier); 437 #endif 438 439 // setup overlay registers 440 441 status_t status = intel_allocate_memory(info, B_PAGE_SIZE, 0, 442 intel_uses_physical_overlay(*info.shared_info) 443 ? B_APERTURE_NEED_PHYSICAL : 0, 444 (addr_t*)&info.overlay_registers, 445 &info.shared_info->physical_overlay_registers); 446 if (status == B_OK) { 447 info.shared_info->overlay_offset = (addr_t)info.overlay_registers 448 - info.aperture_base; 449 init_overlay_registers(info.overlay_registers); 450 } else { 451 ERROR("error: could not allocate overlay memory! %s\n", 452 strerror(status)); 453 } 454 455 // Allocate hardware status page and the cursor memory 456 457 if (intel_allocate_memory(info, B_PAGE_SIZE, 0, B_APERTURE_NEED_PHYSICAL, 458 (addr_t*)info.shared_info->status_page, 459 &info.shared_info->physical_status_page) == B_OK) { 460 // TODO: set status page 461 } 462 if (hardwareCursor) { 463 intel_allocate_memory(info, B_PAGE_SIZE, 0, B_APERTURE_NEED_PHYSICAL, 464 (addr_t*)&info.shared_info->cursor_memory, 465 &info.shared_info->physical_cursor_memory); 466 } 467 468 edid1_info* edidInfo = (edid1_info*)get_boot_item(VESA_EDID_BOOT_INFO, 469 NULL); 470 if (edidInfo != NULL) { 471 info.shared_info->has_vesa_edid_info = true; 472 memcpy(&info.shared_info->vesa_edid_info, edidInfo, sizeof(edid1_info)); 473 } 474 475 init_interrupt_handler(info); 476 477 if (info.device_type.HasPlatformControlHub()) { 478 if (info.device_type.Generation() == 5) { 479 info.shared_info->fdi_link_frequency = (read32(info, FDI_PLL_BIOS_0) 480 & FDI_PLL_FB_CLOCK_MASK) + 2; 481 info.shared_info->fdi_link_frequency *= 100; 482 } else { 483 info.shared_info->fdi_link_frequency = 2700; 484 } 485 } else { 486 info.shared_info->fdi_link_frequency = 0; 487 } 488 489 TRACE("%s: completed successfully!\n", __func__); 490 return B_OK; 491 } 492 493 494 void 495 intel_extreme_uninit(intel_info &info) 496 { 497 CALLED(); 498 499 if (!info.fake_interrupts && info.shared_info->vblank_sem > 0) { 500 // disable interrupt generation 501 write16(info, find_reg(info, INTEL_INTERRUPT_ENABLED), 0); 502 write16(info, find_reg(info, INTEL_INTERRUPT_MASK), ~0); 503 504 remove_io_interrupt_handler(info.irq, intel_interrupt_handler, &info); 505 506 if (info.use_msi && gPCIx86Module != NULL) { 507 gPCIx86Module->disable_msi(info.pci->bus, 508 info.pci->device, info.pci->function); 509 gPCIx86Module->unconfigure_msi(info.pci->bus, 510 info.pci->device, info.pci->function); 511 } 512 } 513 514 gGART->unmap_aperture(info.aperture); 515 516 delete_area(info.registers_area); 517 delete_area(info.shared_area); 518 } 519 520