1 /* 2 * Copyright 2006-2016, 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 "accelerant_protos.h" 11 #include "accelerant.h" 12 13 #include "utility.h" 14 15 #include <Debug.h> 16 #include <errno.h> 17 #include <stdlib.h> 18 #include <string.h> 19 #include <unistd.h> 20 #include <syslog.h> 21 22 #include <new> 23 24 #include <AGP.h> 25 26 27 #undef TRACE 28 #define TRACE_ACCELERANT 29 #ifdef TRACE_ACCELERANT 30 # define TRACE(x...) _sPrintf("intel_extreme: " x) 31 #else 32 # define TRACE(x...) 33 #endif 34 35 #define ERROR(x...) _sPrintf("intel_extreme: " x) 36 #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__) 37 38 39 struct accelerant_info* gInfo; 40 uint32 gDumpCount; 41 42 43 class AreaCloner { 44 public: 45 AreaCloner(); 46 ~AreaCloner(); 47 48 area_id Clone(const char* name, void** _address, 49 uint32 spec, uint32 protection, 50 area_id sourceArea); 51 status_t InitCheck() 52 { return fArea < 0 ? (status_t)fArea : B_OK; } 53 void Keep(); 54 55 private: 56 area_id fArea; 57 }; 58 59 60 AreaCloner::AreaCloner() 61 : 62 fArea(-1) 63 { 64 } 65 66 67 AreaCloner::~AreaCloner() 68 { 69 if (fArea >= 0) 70 delete_area(fArea); 71 } 72 73 74 area_id 75 AreaCloner::Clone(const char* name, void** _address, uint32 spec, 76 uint32 protection, area_id sourceArea) 77 { 78 fArea = clone_area(name, _address, spec, protection, sourceArea); 79 return fArea; 80 } 81 82 83 void 84 AreaCloner::Keep() 85 { 86 fArea = -1; 87 } 88 89 90 // #pragma mark - 91 92 93 // intel_reg --mmio=ie-0001.bin --devid=27a2 dump 94 void 95 dump_registers() 96 { 97 char filename[255]; 98 99 sprintf(filename, "/boot/system/cache/tmp/ie-%04" B_PRId32 ".bin", 100 gDumpCount); 101 102 ERROR("%s: Taking register dump #%" B_PRId32 "\n", __func__, gDumpCount); 103 104 int fd = open(filename, O_CREAT | O_WRONLY, 0644); 105 uint32 data = 0; 106 if (fd >= 0) { 107 for (int32 i = 0; i < 0x80000; i += sizeof(data)) { 108 //char line[512]; 109 //int length = sprintf(line, "%05" B_PRIx32 ": " 110 // "%08" B_PRIx32 " %08" B_PRIx32 " %08" B_PRIx32 " %08" B_PRIx32 "\n", 111 // i, read32(i), read32(i + 4), read32(i + 8), read32(i + 12)); 112 data = read32(i); 113 write(fd, &data, sizeof(data)); 114 } 115 close(fd); 116 sync(); 117 } 118 119 gDumpCount++; 120 } 121 122 123 /*! This is the common accelerant_info initializer. It is called by 124 both, the first accelerant and all clones. 125 */ 126 static status_t 127 init_common(int device, bool isClone) 128 { 129 // initialize global accelerant info structure 130 131 // Number of register dumps we have... taken. 132 gDumpCount = 0; 133 134 gInfo = (accelerant_info*)malloc(sizeof(accelerant_info)); 135 if (gInfo == NULL) 136 return B_NO_MEMORY; 137 138 memset(gInfo, 0, sizeof(accelerant_info)); 139 140 gInfo->is_clone = isClone; 141 gInfo->device = device; 142 143 // get basic info from driver 144 145 intel_get_private_data data; 146 data.magic = INTEL_PRIVATE_DATA_MAGIC; 147 148 if (ioctl(device, INTEL_GET_PRIVATE_DATA, &data, 149 sizeof(intel_get_private_data)) != 0) { 150 free(gInfo); 151 return B_ERROR; 152 } 153 154 AreaCloner sharedCloner; 155 gInfo->shared_info_area = sharedCloner.Clone("intel extreme shared info", 156 (void**)&gInfo->shared_info, B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, 157 data.shared_info_area); 158 status_t status = sharedCloner.InitCheck(); 159 if (status < B_OK) { 160 free(gInfo); 161 return status; 162 } 163 164 AreaCloner regsCloner; 165 gInfo->regs_area = regsCloner.Clone("intel extreme regs", 166 (void**)&gInfo->registers, B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, 167 gInfo->shared_info->registers_area); 168 status = regsCloner.InitCheck(); 169 if (status < B_OK) { 170 free(gInfo); 171 return status; 172 } 173 174 sharedCloner.Keep(); 175 regsCloner.Keep(); 176 177 // The overlay registers, hardware status, and cursor memory share 178 // a single area with the shared_info 179 180 if (gInfo->shared_info->overlay_offset != 0) { 181 gInfo->overlay_registers = (struct overlay_registers*) 182 (gInfo->shared_info->graphics_memory 183 + gInfo->shared_info->overlay_offset); 184 } 185 186 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)) { 187 // allocate some extra memory for the 3D context 188 if (intel_allocate_memory(INTEL_i965_3D_CONTEXT_SIZE, 189 B_APERTURE_NON_RESERVED, gInfo->context_base) == B_OK) { 190 gInfo->context_offset = gInfo->context_base 191 - (addr_t)gInfo->shared_info->graphics_memory; 192 } 193 } 194 195 gInfo->pipe_count = 0; 196 197 // Allocate all of our pipes 198 for (int i = 0; i < MAX_PIPES; i++) { 199 switch (i) { 200 case 0: 201 gInfo->pipes[i] = new(std::nothrow) Pipe(INTEL_PIPE_A); 202 break; 203 case 1: 204 gInfo->pipes[i] = new(std::nothrow) Pipe(INTEL_PIPE_B); 205 break; 206 default: 207 ERROR("%s: Unknown pipe %d\n", __func__, i); 208 } 209 if (gInfo->pipes[i] == NULL) 210 ERROR("%s: Error allocating pipe %d\n", __func__, i); 211 else 212 gInfo->pipe_count++; 213 } 214 215 return B_OK; 216 } 217 218 219 /*! Clean up data common to both primary and cloned accelerant */ 220 static void 221 uninit_common(void) 222 { 223 intel_free_memory(gInfo->context_base); 224 225 delete_area(gInfo->regs_area); 226 delete_area(gInfo->shared_info_area); 227 228 gInfo->regs_area = gInfo->shared_info_area = -1; 229 230 // close the file handle ONLY if we're the clone 231 if (gInfo->is_clone) 232 close(gInfo->device); 233 234 free(gInfo); 235 } 236 237 238 static void 239 dump_ports() 240 { 241 if (gInfo->port_count == 0) { 242 TRACE("%s: No ports connected\n", __func__); 243 return; 244 } 245 246 TRACE("%s: Connected ports: (port_count: %" B_PRIu32 ")\n", __func__, 247 gInfo->port_count); 248 249 for (uint32 i = 0; i < gInfo->port_count; i++) { 250 Port* port = gInfo->ports[i]; 251 if (!port) { 252 TRACE("port %" B_PRIu32 ":: INVALID ALLOC!\n", i); 253 continue; 254 } 255 TRACE("port %" B_PRIu32 ": %s %s\n", i, port->PortName(), 256 port->IsConnected() ? "connected" : "disconnected"); 257 } 258 } 259 260 261 static bool 262 has_connected_port(port_index portIndex, uint32 type) 263 { 264 for (uint32 i = 0; i < gInfo->port_count; i++) { 265 Port* port = gInfo->ports[i]; 266 if (type != INTEL_PORT_TYPE_ANY && port->Type() != type) 267 continue; 268 if (portIndex != INTEL_PORT_ANY && port->PortIndex() != portIndex) 269 continue; 270 271 return true; 272 } 273 274 return false; 275 } 276 277 278 static status_t 279 probe_ports() 280 { 281 // Try to determine what ports to use. We use the following heuristic: 282 // * Check for DisplayPort, these can be more or less detected reliably. 283 // * Check for HDMI, it'll fail on devices not having HDMI for us to fall 284 // back to DVI. 285 // * Assume DVI B if no HDMI and no DisplayPort is present, confirmed by 286 // reading EDID in the IsConnected() call. 287 // * Check for analog if possible (there's a detection bit on PCH), 288 // otherwise the assumed presence is confirmed by reading EDID in 289 // IsConnected(). 290 291 TRACE("adpa: %08" B_PRIx32 "\n", read32(INTEL_ANALOG_PORT)); 292 TRACE("dova: %08" B_PRIx32 ", dovb: %08" B_PRIx32 293 ", dovc: %08" B_PRIx32 "\n", read32(INTEL_DIGITAL_PORT_A), 294 read32(INTEL_DIGITAL_PORT_B), read32(INTEL_DIGITAL_PORT_C)); 295 TRACE("lvds: %08" B_PRIx32 "\n", read32(INTEL_DIGITAL_LVDS_PORT)); 296 297 bool foundLVDS = false; 298 299 gInfo->port_count = 0; 300 for (int i = INTEL_PORT_A; i <= INTEL_PORT_D; i++) { 301 TRACE("Probing DisplayPort %d\n", i); 302 Port* displayPort = new(std::nothrow) DisplayPort((port_index)i); 303 if (displayPort == NULL) 304 return B_NO_MEMORY; 305 306 if (displayPort->IsConnected()) 307 gInfo->ports[gInfo->port_count++] = displayPort; 308 else 309 delete displayPort; 310 } 311 312 // Digital Display Interface 313 if (gInfo->shared_info->device_type.HasDDI()) { 314 for (int i = INTEL_PORT_A; i <= INTEL_PORT_E; i++) { 315 TRACE("Probing DDI %d\n", i); 316 317 Port* ddiPort 318 = new(std::nothrow) DigitalDisplayInterface((port_index)i); 319 320 if (ddiPort == NULL) 321 return B_NO_MEMORY; 322 323 if (ddiPort->IsConnected()) 324 gInfo->ports[gInfo->port_count++] = ddiPort; 325 else 326 delete ddiPort; 327 } 328 } 329 330 // Ensure DP_A isn't already taken (or DDI) 331 TRACE("Probing eDP\n"); 332 if (!has_connected_port((port_index)INTEL_PORT_A, INTEL_PORT_TYPE_ANY)) { 333 // also always try eDP, it'll also just fail if not applicable 334 Port* eDPPort = new(std::nothrow) EmbeddedDisplayPort(); 335 if (eDPPort == NULL) 336 return B_NO_MEMORY; 337 if (eDPPort->IsConnected()) 338 gInfo->ports[gInfo->port_count++] = eDPPort; 339 else 340 delete eDPPort; 341 } 342 343 for (int i = INTEL_PORT_B; i <= INTEL_PORT_D; i++) { 344 TRACE("Probing HDMI %d\n", i); 345 if (has_connected_port((port_index)i, INTEL_PORT_TYPE_ANY)) { 346 // Ensure port not already claimed by something like DDI 347 continue; 348 } 349 350 Port* hdmiPort = new(std::nothrow) HDMIPort((port_index)i); 351 if (hdmiPort == NULL) 352 return B_NO_MEMORY; 353 354 if (hdmiPort->IsConnected()) 355 gInfo->ports[gInfo->port_count++] = hdmiPort; 356 else 357 delete hdmiPort; 358 } 359 360 // always try the LVDS port, it'll simply fail if not applicable 361 TRACE("Probing LVDS\n"); 362 Port* lvdsPort = new(std::nothrow) LVDSPort(); 363 if (lvdsPort == NULL) 364 return B_NO_MEMORY; 365 if (lvdsPort->IsConnected()) { 366 foundLVDS = true; 367 gInfo->ports[gInfo->port_count++] = lvdsPort; 368 gInfo->head_mode |= HEAD_MODE_LVDS_PANEL; 369 gInfo->head_mode |= HEAD_MODE_B_DIGITAL; 370 } else 371 delete lvdsPort; 372 373 TRACE("Probing DVI\n"); 374 if (!has_connected_port(INTEL_PORT_ANY, INTEL_PORT_TYPE_ANY)) { 375 // there's neither DisplayPort nor HDMI so far, assume DVI B 376 for (port_index index = INTEL_PORT_B; index <= INTEL_PORT_C; 377 index = (port_index)(index + 1)) { 378 Port* dviPort = new(std::nothrow) DigitalPort(index, "DVI"); 379 if (dviPort == NULL) 380 return B_NO_MEMORY; 381 382 if (dviPort->IsConnected()) { 383 gInfo->ports[gInfo->port_count++] = dviPort; 384 gInfo->head_mode |= HEAD_MODE_B_DIGITAL; 385 } else 386 delete dviPort; 387 } 388 } 389 390 // then finally always try the analog port 391 TRACE("Probing Analog\n"); 392 Port* analogPort = new(std::nothrow) AnalogPort(); 393 if (analogPort == NULL) 394 return B_NO_MEMORY; 395 if (analogPort->IsConnected()) { 396 gInfo->ports[gInfo->port_count++] = analogPort; 397 gInfo->head_mode |= HEAD_MODE_A_ANALOG; 398 } else 399 delete analogPort; 400 401 if (gInfo->port_count == 0) 402 return B_ERROR; 403 404 // Activate reference clocks if needed 405 if (gInfo->shared_info->pch_info == INTEL_PCH_IBX 406 || gInfo->shared_info->pch_info == INTEL_PCH_CPT) { 407 TRACE("Activating clocks\n"); 408 // XXX: Is LVDS the same as Panel? 409 refclk_activate_ilk(foundLVDS); 410 } 411 /* 412 } else if (gInfo->shared_info->pch_info == INTEL_PCH_LPT) { 413 // TODO: Some kind of stepped bend thing? 414 // only needed for vga 415 refclk_activate_lpt(foundLVDS); 416 } 417 */ 418 419 TRACE("Probing complete.\n"); 420 return B_OK; 421 } 422 423 424 static status_t 425 assign_pipes() 426 { 427 // TODO: At some point we should "group" ports to pipes with the same mode. 428 // You can drive multiple ports from a single pipe as long as the mode is 429 // the same. For the moment we could get displays with the wrong pipes 430 // assigned when the count is > 1; 431 432 uint32 current = 0; 433 434 bool assigned[gInfo->pipe_count]; 435 memset(assigned, 0, gInfo->pipe_count); 436 437 // Some ports need to be assigned to a fixed pipe on old hardware (or due 438 // to limitations in the current driver on current hardware). Assign those 439 // first 440 for (uint32 i = 0; i < gInfo->port_count; i++) { 441 if (gInfo->ports[i] == NULL) 442 continue; 443 444 pipe_index preference = gInfo->ports[i]->PipePreference(); 445 if (preference != INTEL_PIPE_ANY) { 446 int index = (preference == INTEL_PIPE_B) ? 1 : 0; 447 if (assigned[index]) { 448 TRACE("Pipe %d is already assigned, it will drive multiple " 449 "displays\n", index); 450 } 451 gInfo->ports[i]->SetPipe(gInfo->pipes[index]); 452 assigned[index] = true; 453 continue; 454 } 455 } 456 457 // In a second pass, assign the remaining ports to the remaining pipes 458 for (uint32 i = 0; i < gInfo->port_count; i++) { 459 if (gInfo->ports[i]->IsConnected()) { 460 while (current < gInfo->pipe_count && assigned[current]) 461 current++; 462 463 if (current >= gInfo->pipe_count) { 464 ERROR("%s: No pipes left to assign to port %s!\n", __func__, 465 gInfo->ports[i]->PortName()); 466 continue; 467 } 468 469 gInfo->ports[i]->SetPipe(gInfo->pipes[current]); 470 } 471 } 472 473 return B_OK; 474 } 475 476 477 // #pragma mark - public accelerant functions 478 479 480 /*! Init primary accelerant */ 481 status_t 482 intel_init_accelerant(int device) 483 { 484 CALLED(); 485 486 status_t status = init_common(device, false); 487 if (status != B_OK) 488 return status; 489 490 intel_shared_info &info = *gInfo->shared_info; 491 492 init_lock(&info.accelerant_lock, "intel extreme accelerant"); 493 init_lock(&info.engine_lock, "intel extreme engine"); 494 495 setup_ring_buffer(info.primary_ring_buffer, "intel primary ring buffer"); 496 497 // Probe all ports 498 status = probe_ports(); 499 500 // On TRACE, dump ports and states 501 dump_ports(); 502 503 if (status != B_OK) 504 ERROR("Warning: zero active displays were found!\n"); 505 506 status = assign_pipes(); 507 508 if (status != B_OK) 509 ERROR("Warning: error while assigning pipes!\n"); 510 511 status = create_mode_list(); 512 if (status != B_OK) { 513 uninit_common(); 514 return status; 515 } 516 517 return B_OK; 518 } 519 520 521 ssize_t 522 intel_accelerant_clone_info_size(void) 523 { 524 CALLED(); 525 // clone info is device name, so return its maximum size 526 return B_PATH_NAME_LENGTH; 527 } 528 529 530 void 531 intel_get_accelerant_clone_info(void* info) 532 { 533 CALLED(); 534 ioctl(gInfo->device, INTEL_GET_DEVICE_NAME, info, B_PATH_NAME_LENGTH); 535 } 536 537 538 status_t 539 intel_clone_accelerant(void* info) 540 { 541 CALLED(); 542 543 // create full device name 544 char path[B_PATH_NAME_LENGTH]; 545 strcpy(path, "/dev/"); 546 #ifdef __HAIKU__ 547 strlcat(path, (const char*)info, sizeof(path)); 548 #else 549 strcat(path, (const char*)info); 550 #endif 551 552 int fd = open(path, B_READ_WRITE); 553 if (fd < 0) 554 return errno; 555 556 status_t status = init_common(fd, true); 557 if (status != B_OK) 558 goto err1; 559 560 // get read-only clone of supported display modes 561 status = gInfo->mode_list_area = clone_area( 562 "intel extreme cloned modes", (void**)&gInfo->mode_list, 563 B_ANY_ADDRESS, B_READ_AREA, gInfo->shared_info->mode_list_area); 564 if (status < B_OK) 565 goto err2; 566 567 return B_OK; 568 569 err2: 570 uninit_common(); 571 err1: 572 close(fd); 573 return status; 574 } 575 576 577 /*! This function is called for both, the primary accelerant and all of 578 its clones. 579 */ 580 void 581 intel_uninit_accelerant(void) 582 { 583 CALLED(); 584 585 // delete accelerant instance data 586 delete_area(gInfo->mode_list_area); 587 gInfo->mode_list = NULL; 588 589 intel_shared_info &info = *gInfo->shared_info; 590 591 uninit_lock(&info.accelerant_lock); 592 uninit_lock(&info.engine_lock); 593 594 uninit_ring_buffer(info.primary_ring_buffer); 595 596 uninit_common(); 597 } 598 599 600 status_t 601 intel_get_accelerant_device_info(accelerant_device_info* info) 602 { 603 CALLED(); 604 605 info->version = B_ACCELERANT_VERSION; 606 607 DeviceType* type = &gInfo->shared_info->device_type; 608 609 if (type->InFamily(INTEL_FAMILY_8xx)) 610 strcpy(info->name, "Intel Extreme"); 611 else if (type->InFamily(INTEL_FAMILY_9xx)) 612 strcpy(info->name, "Intel GMA"); 613 else if (type->InFamily(INTEL_FAMILY_SOC0)) 614 strcpy(info->name, "Intel Atom"); 615 else if (type->InFamily(INTEL_FAMILY_SER5)) 616 strcpy(info->name, "Intel HD/Iris"); 617 else 618 strcpy(info->name, "Intel"); 619 620 strcpy(info->chipset, gInfo->shared_info->device_identifier); 621 strcpy(info->serial_no, "None"); 622 623 info->memory = gInfo->shared_info->graphics_memory_size; 624 info->dac_speed = gInfo->shared_info->pll_info.max_frequency; 625 626 return B_OK; 627 } 628 629 630 sem_id 631 intel_accelerant_retrace_semaphore() 632 { 633 CALLED(); 634 return gInfo->shared_info->vblank_sem; 635 } 636