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