1 /* 2 * Copyright 2006-2011, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Alexander von Gluck, kallisti5@unixzen.com 7 */ 8 9 10 #include "connector.h" 11 12 #include <assert.h> 13 #include <Debug.h> 14 15 #include "accelerant_protos.h" 16 #include "accelerant.h" 17 #include "bios.h" 18 #include "encoder.h" 19 #include "gpu.h" 20 #include "utility.h" 21 22 23 #undef TRACE 24 25 #define TRACE_CONNECTOR 26 #ifdef TRACE_CONNECTOR 27 # define TRACE(x...) _sPrintf("radeon_hd: " x) 28 #else 29 # define TRACE(x...) ; 30 #endif 31 32 #define ERROR(x...) _sPrintf("radeon_hd: " x) 33 34 35 static void 36 gpio_lock_i2c(void* cookie, bool lock) 37 { 38 gpio_info* info = (gpio_info*)cookie; 39 40 uint32 buffer = 0; 41 42 if (lock == true) { 43 // hwCapable and > DCE3 44 if (info->i2c.hwCapable == true && gInfo->shared_info->dceMajor >= 3) { 45 // Switch GPIO pads to ddc mode 46 buffer = Read32(OUT, info->i2c.sclMaskReg); 47 buffer &= ~(1 << 16); 48 Write32(OUT, info->i2c.sclMaskReg, buffer); 49 } 50 51 // Clear pins 52 buffer = Read32(OUT, info->i2c.sclAReg) & ~info->i2c.sclAMask; 53 Write32(OUT, info->i2c.sclAReg, buffer); 54 buffer = Read32(OUT, info->i2c.sdaAReg) & ~info->i2c.sdaAMask; 55 Write32(OUT, info->i2c.sdaAReg, buffer); 56 } 57 58 // Set pins to input 59 buffer = Read32(OUT, info->i2c.sclEnReg) & ~info->i2c.sclEnMask; 60 Write32(OUT, info->i2c.sclEnReg, buffer); 61 buffer = Read32(OUT, info->i2c.sdaEnReg) & ~info->i2c.sdaEnMask; 62 Write32(OUT, info->i2c.sdaEnReg, buffer); 63 64 // mask clock GPIO pins for software use 65 buffer = Read32(OUT, info->i2c.sclMaskReg); 66 if (lock == true) 67 buffer |= info->i2c.sclMask; 68 else 69 buffer &= ~info->i2c.sclMask; 70 71 Write32(OUT, info->i2c.sclMaskReg, buffer); 72 Read32(OUT, info->i2c.sclMaskReg); 73 74 // mask data GPIO pins for software use 75 buffer = Read32(OUT, info->i2c.sdaMaskReg); 76 if (lock == true) 77 buffer |= info->i2c.sdaMask; 78 else 79 buffer &= ~info->i2c.sdaMask; 80 81 Write32(OUT, info->i2c.sdaMaskReg, buffer); 82 Read32(OUT, info->i2c.sdaMaskReg); 83 } 84 85 86 static status_t 87 gpio_get_i2c_bit(void* cookie, int* _clock, int* _data) 88 { 89 gpio_info* info = (gpio_info*)cookie; 90 91 uint32 scl = Read32(OUT, info->i2c.sclYReg) & info->i2c.sclYMask; 92 uint32 sda = Read32(OUT, info->i2c.sdaYReg) & info->i2c.sdaYMask; 93 94 *_clock = scl != 0; 95 *_data = sda != 0; 96 97 return B_OK; 98 } 99 100 101 static status_t 102 gpio_set_i2c_bit(void* cookie, int clock, int data) 103 { 104 gpio_info* info = (gpio_info*)cookie; 105 106 uint32 scl = Read32(OUT, info->i2c.sclEnReg) & ~info->i2c.sclEnMask; 107 scl |= clock ? 0 : info->i2c.sclEnMask; 108 Write32(OUT, info->i2c.sclEnReg, scl); 109 Read32(OUT, info->i2c.sclEnReg); 110 111 uint32 sda = Read32(OUT, info->i2c.sdaEnReg) & ~info->i2c.sdaEnMask; 112 sda |= data ? 0 : info->i2c.sdaEnMask; 113 Write32(OUT, info->i2c.sdaEnReg, sda); 114 Read32(OUT, info->i2c.sdaEnReg); 115 116 return B_OK; 117 } 118 119 120 uint16 121 connector_pick_atom_hpdid(uint32 connectorIndex) 122 { 123 radeon_shared_info &info = *gInfo->shared_info; 124 125 uint16 atomHPDID = 0xff; 126 uint16 hpdPinIndex = gConnector[connectorIndex]->hpdPinIndex; 127 if (info.dceMajor >= 4 128 && gGPIOInfo[hpdPinIndex]->valid) { 129 130 // See mmDC_GPIO_HPD_A in drm for register value 131 uint32 targetReg = AVIVO_DC_GPIO_HPD_A; 132 if (info.dceMajor >= 13) { 133 ERROR("WARNING: CHECK NEW DCE mmDC_GPIO_HPD_A value!\n"); 134 targetReg = POL_mmDC_GPIO_HPD_A; 135 } else if (info.dceMajor >= 12) 136 targetReg = POL_mmDC_GPIO_HPD_A; 137 else if (info.dceMajor >= 11) 138 targetReg = CAR_mmDC_GPIO_HPD_A; 139 else if (info.dceMajor >= 10) 140 targetReg = VOL_mmDC_GPIO_HPD_A; 141 else if (info.dceMajor >= 8) 142 targetReg = SEA_mmDC_GPIO_HPD_A; 143 else if (info.dceMajor >= 6) 144 targetReg = SI_DC_GPIO_HPD_A; 145 else if (info.dceMajor >= 4) 146 targetReg = EVERGREEN_DC_GPIO_HPD_A; 147 148 // You're drunk AMD, go home. (this makes no sense) 149 if (gGPIOInfo[hpdPinIndex]->hwReg == targetReg) { 150 switch(gGPIOInfo[hpdPinIndex]->hwMask) { 151 case (1 << 0): 152 atomHPDID = 0; 153 break; 154 case (1 << 8): 155 atomHPDID = 1; 156 break; 157 case (1 << 16): 158 atomHPDID = 2; 159 break; 160 case (1 << 24): 161 atomHPDID = 3; 162 break; 163 case (1 << 26): 164 atomHPDID = 4; 165 break; 166 case (1 << 28): 167 atomHPDID = 5; 168 break; 169 } 170 } 171 } 172 return atomHPDID; 173 } 174 175 176 bool 177 connector_read_edid(uint32 connectorIndex, edid1_info* edid) 178 { 179 // ensure things are sane 180 uint32 i2cPinIndex = gConnector[connectorIndex]->i2cPinIndex; 181 if (gGPIOInfo[i2cPinIndex]->valid == false 182 || gGPIOInfo[i2cPinIndex]->i2c.valid == false) { 183 ERROR("%s: invalid gpio %" B_PRIu32 " for connector %" B_PRIu32 "\n", 184 __func__, i2cPinIndex, connectorIndex); 185 return false; 186 } 187 188 i2c_bus bus; 189 190 ddc2_init_timing(&bus); 191 bus.cookie = (void*)gGPIOInfo[i2cPinIndex]; 192 bus.set_signals = &gpio_set_i2c_bit; 193 bus.get_signals = &gpio_get_i2c_bit; 194 195 gpio_lock_i2c(bus.cookie, true); 196 status_t edid_result = ddc2_read_edid1(&bus, edid, NULL, NULL); 197 gpio_lock_i2c(bus.cookie, false); 198 199 if (edid_result != B_OK) 200 return false; 201 202 TRACE("%s: found edid monitor on connector #%" B_PRId32 "\n", 203 __func__, connectorIndex); 204 205 return true; 206 } 207 208 209 bool 210 connector_read_mode_lvds(uint32 connectorIndex, display_mode* mode) 211 { 212 assert(mode); 213 214 uint8 dceMajor; 215 uint8 dceMinor; 216 int index = GetIndexIntoMasterTable(DATA, LVDS_Info); 217 uint16 offset; 218 219 union atomLVDSInfo { 220 struct _ATOM_LVDS_INFO info; 221 struct _ATOM_LVDS_INFO_V12 info_12; 222 }; 223 224 // Wipe out display_mode 225 memset(mode, 0, sizeof(display_mode)); 226 227 if (atom_parse_data_header(gAtomContext, index, NULL, 228 &dceMajor, &dceMinor, &offset) == B_OK) { 229 230 union atomLVDSInfo* lvdsInfo 231 = (union atomLVDSInfo*)(gAtomContext->bios + offset); 232 233 display_timing* timing = &mode->timing; 234 235 // Pixel Clock 236 timing->pixel_clock 237 = B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usPixClk) * 10; 238 // Horizontal 239 timing->h_display 240 = B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usHActive); 241 timing->h_total = timing->h_display + B_LENDIAN_TO_HOST_INT16( 242 lvdsInfo->info.sLCDTiming.usHBlanking_Time); 243 timing->h_sync_start = timing->h_display 244 + B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usHSyncOffset); 245 timing->h_sync_end = timing->h_sync_start 246 + B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usHSyncWidth); 247 // Vertical 248 timing->v_display 249 = B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usVActive); 250 timing->v_total = timing->v_display + B_LENDIAN_TO_HOST_INT16( 251 lvdsInfo->info.sLCDTiming.usVBlanking_Time); 252 timing->v_sync_start = timing->v_display 253 + B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usVSyncOffset); 254 timing->v_sync_end = timing->v_sync_start 255 + B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usVSyncWidth); 256 257 #if 0 258 // Who cares. 259 uint32 powerDelay 260 = B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.usOffDelayInMs); 261 #endif 262 263 // Store special lvds flags the encoder setup needs 264 gConnector[connectorIndex]->lvdsFlags = lvdsInfo->info.ucLVDS_Misc; 265 266 // Spread Spectrum ID (in SS table) 267 gInfo->lvdsSpreadSpectrumID = lvdsInfo->info.ucSS_Id; 268 269 uint16 flags = B_LENDIAN_TO_HOST_INT16( 270 lvdsInfo->info.sLCDTiming.susModeMiscInfo.usAccess); 271 272 if ((flags & ATOM_VSYNC_POLARITY) == 0) 273 timing->flags |= B_POSITIVE_VSYNC; 274 if ((flags & ATOM_HSYNC_POLARITY) == 0) 275 timing->flags |= B_POSITIVE_HSYNC; 276 277 // Extra flags 278 if ((flags & ATOM_INTERLACE) != 0) 279 timing->flags |= B_TIMING_INTERLACED; 280 281 #if 0 282 // We don't use these timing flags at the moment 283 if ((flags & ATOM_COMPOSITESYNC) != 0) 284 timing->flags |= MODE_FLAG_CSYNC; 285 if ((flags & ATOM_DOUBLE_CLOCK_MODE) != 0) 286 timing->flags |= MODE_FLAG_DBLSCAN; 287 #endif 288 289 mode->h_display_start = 0; 290 mode->v_display_start = 0; 291 mode->virtual_width = timing->h_display; 292 mode->virtual_height = timing->v_display; 293 294 // Assume 32-bit color 295 mode->space = B_RGB32_LITTLE; 296 297 TRACE("%s: %" B_PRIu32 " %" B_PRIu16 " %" B_PRIu16 " %" B_PRIu16 298 " %" B_PRIu16 " %" B_PRIu16 " %" B_PRIu16 " %" B_PRIu16 299 " %" B_PRIu16 "\n", __func__, timing->pixel_clock, 300 timing->h_display, timing->h_sync_start, timing->h_sync_end, 301 timing->h_total, timing->v_display, timing->v_sync_start, 302 timing->v_sync_end, timing->v_total); 303 304 return true; 305 } 306 return false; 307 } 308 309 310 static status_t 311 connector_attach_gpio_i2c(uint32 connectorIndex, uint8 hwPin) 312 { 313 gConnector[connectorIndex]->i2cPinIndex = 0; 314 for (uint32 i = 0; i < MAX_GPIO_PINS; i++) { 315 if (gGPIOInfo[i]->hwPin != hwPin) 316 continue; 317 gConnector[connectorIndex]->i2cPinIndex = i; 318 return B_OK; 319 } 320 321 // We couldnt find the GPIO pin in the known GPIO pins. 322 TRACE("%s: can't find GPIO pin 0x%" B_PRIX8 " for connector %" B_PRIu32 "\n", 323 __func__, hwPin, connectorIndex); 324 return B_ERROR; 325 } 326 327 328 static status_t 329 connector_attach_gpio_router(uint32 connectorIndex, uint8 hwPin) 330 { 331 gConnector[connectorIndex]->router.i2cPinIndex = 0; 332 for (uint32 i = 0; i < MAX_GPIO_PINS; i++) { 333 if (gGPIOInfo[i]->hwPin != hwPin) 334 continue; 335 gConnector[connectorIndex]->router.i2cPinIndex = i; 336 return B_OK; 337 } 338 339 // We couldnt find the GPIO pin in the known GPIO pins. 340 TRACE("%s: can't find GPIO pin 0x%" B_PRIX8 " for connector %" B_PRIu32 "\n", 341 __func__, hwPin, connectorIndex); 342 return B_ERROR; 343 } 344 345 346 static status_t 347 connector_attach_gpio_hpd(uint32 connectorIndex, uint8 hwPin) 348 { 349 gConnector[connectorIndex]->hpdPinIndex = 0; 350 351 for (uint32 i = 0; i < MAX_GPIO_PINS; i++) { 352 if (gGPIOInfo[i]->hwPin != hwPin) 353 continue; 354 gConnector[connectorIndex]->hpdPinIndex = i; 355 return B_OK; 356 } 357 358 // We couldnt find the GPIO pin in the known GPIO pins. 359 TRACE("%s: can't find GPIO pin 0x%" B_PRIX8 " for connector %" B_PRIu32 "\n", 360 __func__, hwPin, connectorIndex); 361 return B_ERROR; 362 } 363 364 365 static status_t 366 gpio_general_populate() 367 { 368 int index = GetIndexIntoMasterTable(DATA, GPIO_Pin_LUT); 369 uint16 tableOffset; 370 uint16 tableSize; 371 372 struct _ATOM_GPIO_PIN_LUT* gpioInfo; 373 374 if (atom_parse_data_header(gAtomContext, index, &tableSize, NULL, NULL, 375 &tableOffset)) { 376 ERROR("%s: could't read GPIO_Pin_LUT table from AtomBIOS index %d!\n", 377 __func__, index); 378 } 379 gpioInfo = (struct _ATOM_GPIO_PIN_LUT*)(gAtomContext->bios + tableOffset); 380 381 int numIndices = (tableSize - sizeof(ATOM_COMMON_TABLE_HEADER)) / 382 sizeof(ATOM_GPIO_PIN_ASSIGNMENT); 383 384 // Find the next available GPIO pin index 385 int32 gpioIndex = -1; 386 for(int32 pin = 0; pin < MAX_GPIO_PINS; pin++) { 387 if (!gGPIOInfo[pin]->valid) { 388 gpioIndex = pin; 389 break; 390 } 391 } 392 if (gpioIndex < 0) { 393 ERROR("%s: ERROR: Out of space for additional GPIO pins!\n", __func__); 394 return B_ERROR; 395 } 396 397 ATOM_GPIO_PIN_ASSIGNMENT* pin = gpioInfo->asGPIO_Pin; 398 for (int i = 0; i < numIndices; i++) { 399 if (gGPIOInfo[gpioIndex]->valid) { 400 ERROR("%s: BUG: Attempting to fill already populated gpio pin!\n", 401 __func__); 402 return B_ERROR; 403 } 404 gGPIOInfo[gpioIndex]->valid = true; 405 gGPIOInfo[gpioIndex]->i2c.valid = false; 406 gGPIOInfo[gpioIndex]->hwPin = pin->ucGPIO_ID; 407 gGPIOInfo[gpioIndex]->hwReg 408 = B_LENDIAN_TO_HOST_INT16(pin->usGpioPin_AIndex) * 4; 409 gGPIOInfo[gpioIndex]->hwMask 410 = (1 << pin->ucGpioPinBitShift); 411 pin = (ATOM_GPIO_PIN_ASSIGNMENT*)((uint8*)pin 412 + sizeof(ATOM_GPIO_PIN_ASSIGNMENT)); 413 414 TRACE("%s: general GPIO @ %" B_PRId32 ", valid: %s, " 415 "hwPin: 0x%" B_PRIX32 "\n", __func__, gpioIndex, 416 gGPIOInfo[gpioIndex]->valid ? "true" : "false", 417 gGPIOInfo[gpioIndex]->hwPin); 418 419 gpioIndex++; 420 } 421 return B_OK; 422 } 423 424 425 static status_t 426 gpio_i2c_populate() 427 { 428 radeon_shared_info &info = *gInfo->shared_info; 429 430 int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info); 431 uint16 tableOffset; 432 uint16 tableSize; 433 434 if (atom_parse_data_header(gAtomContext, index, &tableSize, 435 NULL, NULL, &tableOffset) != B_OK) { 436 ERROR("%s: could't read GPIO_I2C_Info table from AtomBIOS index %d!\n", 437 __func__, index); 438 return B_ERROR; 439 } 440 441 struct _ATOM_GPIO_I2C_INFO* i2cInfo 442 = (struct _ATOM_GPIO_I2C_INFO*)(gAtomContext->bios + tableOffset); 443 444 uint32 numIndices = (tableSize - sizeof(ATOM_COMMON_TABLE_HEADER)) 445 / sizeof(ATOM_GPIO_I2C_ASSIGMENT); 446 447 if (numIndices > ATOM_MAX_SUPPORTED_DEVICE) { 448 ERROR("%s: ERROR: AtomBIOS contains more GPIO_Info items then I" 449 "was prepared for! (seen: %" B_PRIu32 "; max: %" B_PRIu32 ")\n", 450 __func__, numIndices, (uint32)ATOM_MAX_SUPPORTED_DEVICE); 451 return B_ERROR; 452 } 453 454 // Find the next available GPIO pin index 455 int32 gpioIndex = -1; 456 for(int32 pin = 0; pin < MAX_GPIO_PINS; pin++) { 457 if (!gGPIOInfo[pin]->valid) { 458 gpioIndex = pin; 459 break; 460 } 461 } 462 if (gpioIndex < 0) { 463 ERROR("%s: ERROR: Out of space for additional GPIO pins!\n", __func__); 464 return B_ERROR; 465 } 466 467 for (uint32 i = 0; i < numIndices; i++) { 468 if (gGPIOInfo[gpioIndex]->valid) { 469 ERROR("%s: BUG: Attempting to fill already populated gpio pin!\n", 470 __func__); 471 return B_ERROR; 472 } 473 ATOM_GPIO_I2C_ASSIGMENT* gpio = &i2cInfo->asGPIO_Info[i]; 474 475 if (info.dceMajor >= 3) { 476 if (i == 4 && B_LENDIAN_TO_HOST_INT16(gpio->usClkMaskRegisterIndex) 477 == 0x1fda && gpio->sucI2cId.ucAccess == 0x94) { 478 gpio->sucI2cId.ucAccess = 0x14; 479 TRACE("%s: BUG: GPIO override for DCE 3 occured\n", __func__); 480 } 481 } 482 483 if (info.dceMajor >= 4) { 484 if (i == 7 && B_LENDIAN_TO_HOST_INT16(gpio->usClkMaskRegisterIndex) 485 == 0x1936 && gpio->sucI2cId.ucAccess == 0) { 486 gpio->sucI2cId.ucAccess = 0x97; 487 gpio->ucDataMaskShift = 8; 488 gpio->ucDataEnShift = 8; 489 gpio->ucDataY_Shift = 8; 490 gpio->ucDataA_Shift = 8; 491 TRACE("%s: BUG: GPIO override for DCE 4 occured\n", __func__); 492 } 493 } 494 495 // populate gpio information 496 gGPIOInfo[gpioIndex]->hwPin = gpio->sucI2cId.ucAccess; 497 gGPIOInfo[gpioIndex]->i2c.hwCapable 498 = (gpio->sucI2cId.sbfAccess.bfHW_Capable) ? true : false; 499 500 // GPIO mask (Allows software to control the GPIO pad) 501 // 0 = chip access; 1 = only software; 502 gGPIOInfo[gpioIndex]->i2c.sclMaskReg 503 = B_LENDIAN_TO_HOST_INT16(gpio->usClkMaskRegisterIndex) * 4; 504 gGPIOInfo[gpioIndex]->i2c.sdaMaskReg 505 = B_LENDIAN_TO_HOST_INT16(gpio->usDataMaskRegisterIndex) * 4; 506 gGPIOInfo[gpioIndex]->i2c.sclMask = 1 << gpio->ucClkMaskShift; 507 gGPIOInfo[gpioIndex]->i2c.sdaMask = 1 << gpio->ucDataMaskShift; 508 509 // GPIO output / write (A) enable 510 // 0 = GPIO input (Y); 1 = GPIO output (A); 511 gGPIOInfo[gpioIndex]->i2c.sclEnReg 512 = B_LENDIAN_TO_HOST_INT16(gpio->usClkEnRegisterIndex) * 4; 513 gGPIOInfo[gpioIndex]->i2c.sdaEnReg 514 = B_LENDIAN_TO_HOST_INT16(gpio->usDataEnRegisterIndex) * 4; 515 gGPIOInfo[gpioIndex]->i2c.sclEnMask = 1 << gpio->ucClkEnShift; 516 gGPIOInfo[gpioIndex]->i2c.sdaEnMask = 1 << gpio->ucDataEnShift; 517 518 // GPIO output / write (A) 519 gGPIOInfo[gpioIndex]->i2c.sclAReg 520 = B_LENDIAN_TO_HOST_INT16(gpio->usClkA_RegisterIndex) * 4; 521 gGPIOInfo[gpioIndex]->i2c.sdaAReg 522 = B_LENDIAN_TO_HOST_INT16(gpio->usDataA_RegisterIndex) * 4; 523 gGPIOInfo[gpioIndex]->i2c.sclAMask = 1 << gpio->ucClkA_Shift; 524 gGPIOInfo[gpioIndex]->i2c.sdaAMask = 1 << gpio->ucDataA_Shift; 525 526 // GPIO input / read (Y) 527 gGPIOInfo[gpioIndex]->i2c.sclYReg 528 = B_LENDIAN_TO_HOST_INT16(gpio->usClkY_RegisterIndex) * 4; 529 gGPIOInfo[gpioIndex]->i2c.sdaYReg 530 = B_LENDIAN_TO_HOST_INT16(gpio->usDataY_RegisterIndex) * 4; 531 gGPIOInfo[gpioIndex]->i2c.sclYMask = 1 << gpio->ucClkY_Shift; 532 gGPIOInfo[gpioIndex]->i2c.sdaYMask = 1 << gpio->ucDataY_Shift; 533 534 // ensure data is valid 535 gGPIOInfo[gpioIndex]->i2c.valid 536 = gGPIOInfo[gpioIndex]->i2c.sclMaskReg ? true : false; 537 gGPIOInfo[gpioIndex]->valid = gGPIOInfo[gpioIndex]->i2c.valid; 538 539 TRACE("%s: i2c GPIO @ %" B_PRIu32 ", valid: %s, hwPin: 0x%" B_PRIX32 "\n", 540 __func__, gpioIndex, gGPIOInfo[gpioIndex]->valid ? "true" : "false", 541 gGPIOInfo[gpioIndex]->hwPin); 542 543 gpioIndex++; 544 } 545 546 return B_OK; 547 } 548 549 550 status_t 551 gpio_populate() 552 { 553 status_t result = gpio_general_populate(); 554 if (result != B_OK) 555 return result; 556 557 result = gpio_i2c_populate(); 558 return result; 559 } 560 561 562 status_t 563 connector_probe_legacy() 564 { 565 int index = GetIndexIntoMasterTable(DATA, SupportedDevicesInfo); 566 uint8 tableMajor; 567 uint8 tableMinor; 568 uint16 tableSize; 569 uint16 tableOffset; 570 571 if (atom_parse_data_header(gAtomContext, index, &tableSize, 572 &tableMajor, &tableMinor, &tableOffset) != B_OK) { 573 ERROR("%s: unable to parse data header!\n", __func__); 574 return B_ERROR; 575 } 576 577 union atomSupportedDevices { 578 struct _ATOM_SUPPORTED_DEVICES_INFO info; 579 struct _ATOM_SUPPORTED_DEVICES_INFO_2 info_2; 580 struct _ATOM_SUPPORTED_DEVICES_INFO_2d1 info_2d1; 581 }; 582 union atomSupportedDevices* supportedDevices; 583 supportedDevices = (union atomSupportedDevices*) 584 (gAtomContext->bios + tableOffset); 585 586 uint16 deviceSupport 587 = B_LENDIAN_TO_HOST_INT16(supportedDevices->info.usDeviceSupport); 588 589 uint32 maxDevice; 590 591 if (tableMajor > 1) 592 maxDevice = ATOM_MAX_SUPPORTED_DEVICE; 593 else 594 maxDevice = ATOM_MAX_SUPPORTED_DEVICE_INFO; 595 596 uint32 i; 597 uint32 connectorIndex = 0; 598 for (i = 0; i < maxDevice; i++) { 599 600 gConnector[connectorIndex]->valid = false; 601 602 // check if this connector is used 603 if ((deviceSupport & (1 << i)) == 0) 604 continue; 605 606 if (i == ATOM_DEVICE_CV_INDEX) { 607 TRACE("%s: skipping component video\n", 608 __func__); 609 continue; 610 } 611 612 ATOM_CONNECTOR_INFO_I2C ci 613 = supportedDevices->info.asConnInfo[i]; 614 615 gConnector[connectorIndex]->type = kConnectorConvertLegacy[ 616 ci.sucConnectorInfo.sbfAccess.bfConnectorType]; 617 618 if (gConnector[connectorIndex]->type == VIDEO_CONNECTOR_UNKNOWN) { 619 TRACE("%s: skipping unknown connector at %" B_PRId32 620 " of 0x%" B_PRIX8 "\n", __func__, i, 621 ci.sucConnectorInfo.sbfAccess.bfConnectorType); 622 continue; 623 } 624 625 // TODO: give tv unique connector ids 626 627 // Always set CRT1 and CRT2 as VGA, some cards incorrectly set 628 // VGA ports as DVI 629 if (i == ATOM_DEVICE_CRT1_INDEX || i == ATOM_DEVICE_CRT2_INDEX) 630 gConnector[connectorIndex]->type = VIDEO_CONNECTOR_VGA; 631 632 uint8 dac = ci.sucConnectorInfo.sbfAccess.bfAssociatedDAC; 633 uint32 encoderObject = encoder_object_lookup((1 << i), dac); 634 uint32 encoderID = (encoderObject & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; 635 636 gConnector[connectorIndex]->valid = true; 637 gConnector[connectorIndex]->flags = (1 << i); 638 gConnector[connectorIndex]->encoder.valid = true; 639 gConnector[connectorIndex]->encoder.objectID = encoderID; 640 gConnector[connectorIndex]->encoder.type 641 = encoder_type_lookup(encoderID, (1 << i)); 642 643 // TODO: Eval external encoders on legacy connector probe 644 gConnector[connectorIndex]->encoderExternal.valid = false; 645 // encoder_is_external(encoderID); 646 647 connector_attach_gpio_i2c(connectorIndex, ci.sucI2cId.ucAccess); 648 649 pll_limit_probe(&gConnector[connectorIndex]->encoder.pll); 650 651 connectorIndex++; 652 } 653 654 // TODO: combine shared connectors 655 656 if (connectorIndex == 0) { 657 TRACE("%s: zero connectors found using legacy detection\n", __func__); 658 return B_ERROR; 659 } 660 661 return B_OK; 662 } 663 664 665 // r600+ 666 status_t 667 connector_probe() 668 { 669 int index = GetIndexIntoMasterTable(DATA, Object_Header); 670 uint8 tableMajor; 671 uint8 tableMinor; 672 uint16 tableSize; 673 uint16 tableOffset; 674 675 if (atom_parse_data_header(gAtomContext, index, &tableSize, 676 &tableMajor, &tableMinor, &tableOffset) != B_OK) { 677 ERROR("%s: ERROR: parsing data header failed!\n", __func__); 678 return B_ERROR; 679 } 680 681 if (tableMinor < 2) { 682 ERROR("%s: ERROR: table minor version unknown! " 683 "(%" B_PRIu8 ".%" B_PRIu8 ")\n", __func__, tableMajor, tableMinor); 684 return B_ERROR; 685 } 686 687 ATOM_CONNECTOR_OBJECT_TABLE* connectorObject; 688 ATOM_ENCODER_OBJECT_TABLE* encoderObject; 689 ATOM_OBJECT_TABLE* routerObject; 690 ATOM_DISPLAY_OBJECT_PATH_TABLE* pathObject; 691 ATOM_OBJECT_HEADER* objectHeader; 692 693 objectHeader = (ATOM_OBJECT_HEADER*)(gAtomContext->bios + tableOffset); 694 pathObject = (ATOM_DISPLAY_OBJECT_PATH_TABLE*) 695 (gAtomContext->bios + tableOffset 696 + B_LENDIAN_TO_HOST_INT16(objectHeader->usDisplayPathTableOffset)); 697 connectorObject = (ATOM_CONNECTOR_OBJECT_TABLE*) 698 (gAtomContext->bios + tableOffset 699 + B_LENDIAN_TO_HOST_INT16(objectHeader->usConnectorObjectTableOffset)); 700 encoderObject = (ATOM_ENCODER_OBJECT_TABLE*) 701 (gAtomContext->bios + tableOffset 702 + B_LENDIAN_TO_HOST_INT16(objectHeader->usEncoderObjectTableOffset)); 703 routerObject = (ATOM_OBJECT_TABLE*) 704 (gAtomContext->bios + tableOffset 705 + B_LENDIAN_TO_HOST_INT16(objectHeader->usRouterObjectTableOffset)); 706 int deviceSupport = B_LENDIAN_TO_HOST_INT16(objectHeader->usDeviceSupport); 707 708 int pathSize = 0; 709 int32 i = 0; 710 711 TRACE("%s: found %" B_PRIu8 " potential display paths.\n", __func__, 712 pathObject->ucNumOfDispPath); 713 714 uint32 connectorIndex = 0; 715 for (i = 0; i < pathObject->ucNumOfDispPath; i++) { 716 717 if (connectorIndex >= ATOM_MAX_SUPPORTED_DEVICE) 718 continue; 719 720 uint8* address = (uint8*)pathObject->asDispPath; 721 ATOM_DISPLAY_OBJECT_PATH* path; 722 address += pathSize; 723 path = (ATOM_DISPLAY_OBJECT_PATH*)address; 724 pathSize += B_LENDIAN_TO_HOST_INT16(path->usSize); 725 726 uint32 connectorType; 727 uint16 connectorFlags = B_LENDIAN_TO_HOST_INT16(path->usDeviceTag); 728 729 if ((deviceSupport & connectorFlags) != 0) { 730 731 uint16 connectorObjectID 732 = (B_LENDIAN_TO_HOST_INT16(path->usConnObjectId) 733 & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; 734 //uint8 con_obj_num 735 // = (B_LENDIAN_TO_HOST_INT16(path->usConnObjectId) 736 // & ENUM_ID_MASK) >> ENUM_ID_SHIFT; 737 //uint8 con_obj_type 738 // = (B_LENDIAN_TO_HOST_INT16(path->usConnObjectId) 739 // & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; 740 741 if (connectorFlags == ATOM_DEVICE_CV_SUPPORT) { 742 TRACE("%s: Path #%" B_PRId32 ": skipping component video.\n", 743 __func__, i); 744 continue; 745 } 746 747 radeon_shared_info &info = *gInfo->shared_info; 748 749 uint16 igpLaneInfo; 750 if ((info.chipsetFlags & CHIP_IGP) != 0) { 751 ERROR("%s: TODO: IGP chip connector detection\n", __func__); 752 // try non-IGP method for now 753 igpLaneInfo = 0; 754 connectorType = kConnectorConvert[connectorObjectID]; 755 } else { 756 igpLaneInfo = 0; 757 connectorType = kConnectorConvert[connectorObjectID]; 758 } 759 760 if (connectorType == VIDEO_CONNECTOR_UNKNOWN) { 761 ERROR("%s: Path #%" B_PRId32 ": skipping unknown connector.\n", 762 __func__, i); 763 continue; 764 } 765 766 connector_info* connector = gConnector[connectorIndex]; 767 768 int32 j; 769 for (j = 0; j < ((B_LENDIAN_TO_HOST_INT16(path->usSize) - 8) / 2); 770 j++) { 771 //uint16 grph_obj_id 772 // = (B_LENDIAN_TO_HOST_INT16(path->usGraphicObjIds[j]) 773 // & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; 774 //uint8 grph_obj_num 775 // = (B_LENDIAN_TO_HOST_INT16(path->usGraphicObjIds[j]) & 776 // ENUM_ID_MASK) >> ENUM_ID_SHIFT; 777 uint8 graphicObjectType 778 = (B_LENDIAN_TO_HOST_INT16(path->usGraphicObjIds[j]) & 779 OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; 780 781 if (graphicObjectType == GRAPH_OBJECT_TYPE_ENCODER) { 782 // Found an encoder 783 int32 k; 784 for (k = 0; k < encoderObject->ucNumberOfObjects; k++) { 785 uint16 encoderObjectRaw 786 = B_LENDIAN_TO_HOST_INT16( 787 encoderObject->asObjects[k].usObjectID); 788 if (B_LENDIAN_TO_HOST_INT16(path->usGraphicObjIds[j]) 789 == encoderObjectRaw) { 790 ATOM_COMMON_RECORD_HEADER* record 791 = (ATOM_COMMON_RECORD_HEADER*) 792 ((uint16*)gAtomContext->bios + tableOffset 793 + B_LENDIAN_TO_HOST_INT16( 794 encoderObject->asObjects[k].usRecordOffset)); 795 ATOM_ENCODER_CAP_RECORD* capRecord; 796 uint16 caps = 0; 797 while (record->ucRecordSize > 0 798 && record->ucRecordType > 0 799 && record->ucRecordType 800 <= ATOM_MAX_OBJECT_RECORD_NUMBER) { 801 switch (record->ucRecordType) { 802 case ATOM_ENCODER_CAP_RECORD_TYPE: 803 capRecord = (ATOM_ENCODER_CAP_RECORD*) 804 record; 805 caps = B_LENDIAN_TO_HOST_INT16( 806 capRecord->usEncoderCap); 807 break; 808 } 809 record = (ATOM_COMMON_RECORD_HEADER*) 810 ((char*)record + record->ucRecordSize); 811 } 812 813 uint32 encoderID 814 = (encoderObjectRaw & OBJECT_ID_MASK) 815 >> OBJECT_ID_SHIFT; 816 817 uint32 encoderType = encoder_type_lookup(encoderID, 818 connectorFlags); 819 820 if (encoderType == VIDEO_ENCODER_NONE) { 821 ERROR("%s: Path #%" B_PRId32 ":" 822 "skipping unknown encoder.\n", 823 __func__, i); 824 continue; 825 } 826 827 encoder_info* encoder; 828 829 // External encoders are behind DVO or UNIPHY 830 if (encoder_is_external(encoderID)) { 831 encoder = &connector->encoderExternal; 832 encoder->isExternal = true; 833 encoder->isDPBridge 834 = encoder_is_dp_bridge(encoderID); 835 } else { 836 encoder = &connector->encoder; 837 encoder->isExternal = false; 838 encoder->isDPBridge = false; 839 } 840 841 // Set up found connector encoder generics 842 encoder->valid = true; 843 encoder->capabilities = caps; 844 encoder->objectID = encoderID; 845 encoder->type = encoderType; 846 encoder->linkEnumeration 847 = (encoderObjectRaw & ENUM_ID_MASK) 848 >> ENUM_ID_SHIFT; 849 pll_limit_probe(&encoder->pll); 850 } 851 } 852 // END if object is encoder 853 } else if (graphicObjectType == GRAPH_OBJECT_TYPE_ROUTER) { 854 int32 k; 855 for (k = 0; k < routerObject->ucNumberOfObjects; k++) { 856 uint16 routerObjectID 857 = B_LENDIAN_TO_HOST_INT16(routerObject->asObjects[k].usObjectID); 858 if (B_LENDIAN_TO_HOST_INT16(path->usGraphicObjIds[j]) == routerObjectID) { 859 ATOM_COMMON_RECORD_HEADER* record = (ATOM_COMMON_RECORD_HEADER*) 860 ((uint16*)gAtomContext->bios + tableOffset 861 + B_LENDIAN_TO_HOST_INT16( 862 routerObject->asObjects[k].usRecordOffset)); 863 ATOM_I2C_RECORD* i2cRecord; 864 ATOM_I2C_ID_CONFIG_ACCESS* i2cConfig; 865 ATOM_ROUTER_DDC_PATH_SELECT_RECORD* ddcPath; 866 ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD* cdPath; 867 ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT* routerConnTable = 868 (ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *) 869 ((uint16*)gAtomContext->bios + tableOffset + 870 + B_LENDIAN_TO_HOST_INT16( 871 routerObject->asObjects[k].usSrcDstTableOffset)); 872 uint8* destObjCount = (uint8*)((uint8*)routerConnTable + 1 873 + (routerConnTable->ucNumberOfSrc * 2)); 874 uint16 *dstObjs = (uint16 *)(destObjCount + 1); 875 876 int enumId; 877 router_info* router = &connector->router; 878 router->objectID = routerObjectID; 879 for (enumId = 0; enumId < (*destObjCount); enumId++) { 880 if (B_LENDIAN_TO_HOST_INT16(path->usConnObjectId) == 881 B_LENDIAN_TO_HOST_INT16(dstObjs[enumId])) 882 break; 883 } 884 while (record->ucRecordSize > 0 && 885 record->ucRecordType > 0 && 886 record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { 887 switch (record->ucRecordType) { 888 case ATOM_I2C_RECORD_TYPE: 889 i2cRecord = (ATOM_I2C_RECORD*)record; 890 i2cConfig 891 = (ATOM_I2C_ID_CONFIG_ACCESS*)&i2cRecord->sucI2cId; 892 connector_attach_gpio_router(connectorIndex, 893 i2cConfig->ucAccess); 894 router->i2cAddr = i2cRecord->ucI2CAddr >> 1; // ?? 895 break; 896 case ATOM_ROUTER_DDC_PATH_SELECT_RECORD_TYPE: 897 ddcPath = (ATOM_ROUTER_DDC_PATH_SELECT_RECORD*)record; 898 router->ddcValid = true; 899 router->ddcMuxType = ddcPath->ucMuxType; 900 router->ddcMuxControlPin = ddcPath->ucMuxControlPin; 901 router->ddcMuxState = ddcPath->ucMuxState[enumId]; 902 break; 903 case ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD_TYPE: 904 cdPath 905 = (ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD *)record; 906 router->cdValid = true; 907 router->cdMuxType = cdPath->ucMuxType; 908 router->cdMuxControlPin = cdPath->ucMuxControlPin; 909 router->cdMuxState = cdPath->ucMuxState[enumId]; 910 break; 911 } 912 913 // move to next record 914 record = (ATOM_COMMON_RECORD_HEADER*) 915 ((char *)record + record->ucRecordSize); 916 } 917 } 918 } 919 } // END if object is router 920 } 921 922 // Set up information buses such as ddc 923 if (((connectorFlags & ATOM_DEVICE_TV_SUPPORT) == 0) 924 && (connectorFlags & ATOM_DEVICE_CV_SUPPORT) == 0) { 925 for (j = 0; j < connectorObject->ucNumberOfObjects; j++) { 926 if (B_LENDIAN_TO_HOST_INT16(path->usConnObjectId) 927 == B_LENDIAN_TO_HOST_INT16( 928 connectorObject->asObjects[j].usObjectID)) { 929 ATOM_COMMON_RECORD_HEADER* record 930 = (ATOM_COMMON_RECORD_HEADER*)(gAtomContext->bios 931 + tableOffset + B_LENDIAN_TO_HOST_INT16( 932 connectorObject->asObjects[j].usRecordOffset)); 933 while (record->ucRecordSize > 0 934 && record->ucRecordType > 0 935 && record->ucRecordType 936 <= ATOM_MAX_OBJECT_RECORD_NUMBER) { 937 ATOM_I2C_RECORD* i2cRecord; 938 ATOM_I2C_ID_CONFIG_ACCESS* i2cConfig; 939 ATOM_HPD_INT_RECORD* hpdRecord; 940 941 switch (record->ucRecordType) { 942 case ATOM_I2C_RECORD_TYPE: 943 i2cRecord 944 = (ATOM_I2C_RECORD*)record; 945 i2cConfig 946 = (ATOM_I2C_ID_CONFIG_ACCESS*) 947 &i2cRecord->sucI2cId; 948 connector_attach_gpio_i2c(connectorIndex, 949 i2cConfig->ucAccess); 950 break; 951 case ATOM_HPD_INT_RECORD_TYPE: 952 hpdRecord = (ATOM_HPD_INT_RECORD*)record; 953 connector_attach_gpio_hpd(connectorIndex, 954 hpdRecord->ucHPDIntGPIOID); 955 break; 956 } 957 958 // move to next record 959 record = (ATOM_COMMON_RECORD_HEADER*) 960 ((char*)record + record->ucRecordSize); 961 } 962 } 963 } 964 } 965 966 connector->valid = true; 967 connector->flags = connectorFlags; 968 connector->type = connectorType; 969 connector->objectID = connectorObjectID; 970 971 connectorIndex++; 972 } // END for each valid connector 973 } // end for each display path 974 975 return B_OK; 976 } 977 978 979 bool 980 connector_is_dp(uint32 connectorIndex) 981 { 982 connector_info* connector = gConnector[connectorIndex]; 983 984 // Traditional DisplayPort connector 985 if (connector->type == VIDEO_CONNECTOR_DP 986 || connector->type == VIDEO_CONNECTOR_EDP) { 987 return true; 988 } 989 990 // DisplayPort bridge on external encoder 991 if (connector->encoderExternal.valid == true 992 && connector->encoderExternal.isDPBridge == true) { 993 return true; 994 } 995 996 return false; 997 } 998 999 1000 void 1001 debug_connectors() 1002 { 1003 ERROR("Currently detected connectors=============\n"); 1004 for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) { 1005 if (gConnector[id]->valid == true) { 1006 uint32 connectorType = gConnector[id]->type; 1007 uint16 i2cPinIndex = gConnector[id]->i2cPinIndex; 1008 uint16 hpdPinIndex = gConnector[id]->hpdPinIndex; 1009 1010 ERROR("Connector #%" B_PRIu32 ")\n", id); 1011 ERROR(" + connector: %s\n", 1012 get_connector_name(connectorType)); 1013 ERROR(" + i2c gpio table id: %" B_PRIu16 "\n", i2cPinIndex); 1014 ERROR(" - gpio hw pin: 0x%" B_PRIX32 "\n", 1015 gGPIOInfo[i2cPinIndex]->hwPin); 1016 ERROR(" - gpio valid: %s\n", 1017 gGPIOInfo[i2cPinIndex]->valid ? "true" : "false"); 1018 ERROR(" - i2c valid: %s\n", 1019 gGPIOInfo[i2cPinIndex]->i2c.valid ? "true" : "false"); 1020 1021 // hot plug detection info 1022 ERROR(" + hpd gpio table id: %" B_PRIu16 "\n", hpdPinIndex); 1023 ERROR(" - gpio hw pin: 0x%" B_PRIX32 "\n", 1024 gGPIOInfo[hpdPinIndex]->hwPin); 1025 ERROR(" - gpio valid: %s\n", 1026 gGPIOInfo[hpdPinIndex]->valid ? "true" : "false"); 1027 1028 // router info 1029 router_info* router = &gConnector[id]->router; 1030 ERROR(" + router gpio table id: %" B_PRIu16 "\n", router->i2cPinIndex); 1031 ERROR(" + router (ddc): %s\n", 1032 router->ddcValid ? "true" : "false"); 1033 if (router->ddcValid) { 1034 ERROR(" - mux type: 0x%" B_PRIX8 "\n", router->ddcMuxType); 1035 ERROR(" - mux pin: 0x%" B_PRIX8 "\n", router->ddcMuxControlPin); 1036 ERROR(" - mux state: 0x%" B_PRIX8 "\n", router->ddcMuxState); 1037 } 1038 ERROR(" + router (c/d): %s\n", 1039 router->cdValid ? "true" : "false"); 1040 if (router->cdValid) { 1041 ERROR(" - mux type: 0x%" B_PRIX8 "\n", router->cdMuxType); 1042 ERROR(" - mux pin: 0x%" B_PRIX8 "\n", router->cdMuxControlPin); 1043 ERROR(" - mux state: 0x%" B_PRIX8 "\n", router->cdMuxState); 1044 } 1045 1046 // encoder info 1047 encoder_info* encoder = &gConnector[id]->encoder; 1048 ERROR(" + encoder: %s\n", 1049 get_encoder_name(encoder->type)); 1050 ERROR(" - id: %" B_PRIu16 "\n", encoder->objectID); 1051 ERROR(" - type: %s\n", 1052 encoder_name_lookup(encoder->objectID)); 1053 ERROR(" - capabilities: 0x%" B_PRIX32 "\n", 1054 encoder->capabilities); 1055 ERROR(" - enumeration: %" B_PRIu32 "\n", 1056 encoder->linkEnumeration); 1057 1058 encoder = &gConnector[id]->encoderExternal; 1059 1060 ERROR(" - is bridge: %s\n", 1061 encoder->valid ? "true" : "false"); 1062 1063 if (!encoder->valid) 1064 ERROR(" + external encoder: none\n"); 1065 else { 1066 ERROR(" + external encoder: %s\n", 1067 get_encoder_name(encoder->type)); 1068 ERROR(" - valid: true\n"); 1069 ERROR(" - id: %" B_PRIu16 "\n", 1070 encoder->objectID); 1071 ERROR(" - type: %s\n", 1072 encoder_name_lookup(encoder->objectID)); 1073 ERROR(" - enumeration: %" B_PRIu32 "\n", 1074 encoder->linkEnumeration); 1075 } 1076 1077 uint32 connectorFlags = gConnector[id]->flags; 1078 bool flags = false; 1079 ERROR(" + flags:\n"); 1080 if ((connectorFlags & ATOM_DEVICE_CRT1_SUPPORT) != 0) { 1081 ERROR(" * device CRT1 support\n"); 1082 flags = true; 1083 } 1084 if ((connectorFlags & ATOM_DEVICE_CRT2_SUPPORT) != 0) { 1085 ERROR(" * device CRT2 support\n"); 1086 flags = true; 1087 } 1088 if ((connectorFlags & ATOM_DEVICE_LCD1_SUPPORT) != 0) { 1089 ERROR(" * device LCD1 support\n"); 1090 flags = true; 1091 } 1092 if ((connectorFlags & ATOM_DEVICE_LCD2_SUPPORT) != 0) { 1093 ERROR(" * device LCD2 support\n"); 1094 flags = true; 1095 } 1096 if ((connectorFlags & ATOM_DEVICE_TV1_SUPPORT) != 0) { 1097 ERROR(" * device TV1 support\n"); 1098 flags = true; 1099 } 1100 if ((connectorFlags & ATOM_DEVICE_CV_SUPPORT) != 0) { 1101 ERROR(" * device CV support\n"); 1102 flags = true; 1103 } 1104 if ((connectorFlags & ATOM_DEVICE_DFP1_SUPPORT) != 0) { 1105 ERROR(" * device DFP1 support\n"); 1106 flags = true; 1107 } 1108 if ((connectorFlags & ATOM_DEVICE_DFP2_SUPPORT) != 0) { 1109 ERROR(" * device DFP2 support\n"); 1110 flags = true; 1111 } 1112 if ((connectorFlags & ATOM_DEVICE_DFP3_SUPPORT) != 0) { 1113 ERROR(" * device DFP3 support\n"); 1114 flags = true; 1115 } 1116 if ((connectorFlags & ATOM_DEVICE_DFP4_SUPPORT) != 0) { 1117 ERROR(" * device DFP4 support\n"); 1118 flags = true; 1119 } 1120 if ((connectorFlags & ATOM_DEVICE_DFP5_SUPPORT) != 0) { 1121 ERROR(" * device DFP5 support\n"); 1122 flags = true; 1123 } 1124 if ((connectorFlags & ATOM_DEVICE_DFP6_SUPPORT) != 0) { 1125 ERROR(" * device DFP6 support\n"); 1126 flags = true; 1127 } 1128 if (flags == false) 1129 ERROR(" * no known flags\n"); 1130 } 1131 } 1132 ERROR("==========================================\n"); 1133 } 1134