/* * Copyright 2006-2011, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Alexander von Gluck, kallisti5@unixzen.com */ #include "connector.h" #include #include #include "accelerant_protos.h" #include "accelerant.h" #include "bios.h" #include "encoder.h" #include "gpu.h" #include "utility.h" #undef TRACE #define TRACE_CONNECTOR #ifdef TRACE_CONNECTOR # define TRACE(x...) _sPrintf("radeon_hd: " x) #else # define TRACE(x...) ; #endif #define ERROR(x...) _sPrintf("radeon_hd: " x) static void gpio_lock_i2c(void* cookie, bool lock) { gpio_info* info = (gpio_info*)cookie; uint32 buffer = 0; if (lock == true) { // hwCapable and > DCE3 if (info->i2c.hwCapable == true && gInfo->shared_info->dceMajor >= 3) { // Switch GPIO pads to ddc mode buffer = Read32(OUT, info->i2c.sclMaskReg); buffer &= ~(1 << 16); Write32(OUT, info->i2c.sclMaskReg, buffer); } // Clear pins buffer = Read32(OUT, info->i2c.sclAReg) & ~info->i2c.sclAMask; Write32(OUT, info->i2c.sclAReg, buffer); buffer = Read32(OUT, info->i2c.sdaAReg) & ~info->i2c.sdaAMask; Write32(OUT, info->i2c.sdaAReg, buffer); } // Set pins to input buffer = Read32(OUT, info->i2c.sclEnReg) & ~info->i2c.sclEnMask; Write32(OUT, info->i2c.sclEnReg, buffer); buffer = Read32(OUT, info->i2c.sdaEnReg) & ~info->i2c.sdaEnMask; Write32(OUT, info->i2c.sdaEnReg, buffer); // mask clock GPIO pins for software use buffer = Read32(OUT, info->i2c.sclMaskReg); if (lock == true) buffer |= info->i2c.sclMask; else buffer &= ~info->i2c.sclMask; Write32(OUT, info->i2c.sclMaskReg, buffer); Read32(OUT, info->i2c.sclMaskReg); // mask data GPIO pins for software use buffer = Read32(OUT, info->i2c.sdaMaskReg); if (lock == true) buffer |= info->i2c.sdaMask; else buffer &= ~info->i2c.sdaMask; Write32(OUT, info->i2c.sdaMaskReg, buffer); Read32(OUT, info->i2c.sdaMaskReg); } static status_t gpio_get_i2c_bit(void* cookie, int* _clock, int* _data) { gpio_info* info = (gpio_info*)cookie; uint32 scl = Read32(OUT, info->i2c.sclYReg) & info->i2c.sclYMask; uint32 sda = Read32(OUT, info->i2c.sdaYReg) & info->i2c.sdaYMask; *_clock = scl != 0; *_data = sda != 0; return B_OK; } static status_t gpio_set_i2c_bit(void* cookie, int clock, int data) { gpio_info* info = (gpio_info*)cookie; uint32 scl = Read32(OUT, info->i2c.sclEnReg) & ~info->i2c.sclEnMask; scl |= clock ? 0 : info->i2c.sclEnMask; Write32(OUT, info->i2c.sclEnReg, scl); Read32(OUT, info->i2c.sclEnReg); uint32 sda = Read32(OUT, info->i2c.sdaEnReg) & ~info->i2c.sdaEnMask; sda |= data ? 0 : info->i2c.sdaEnMask; Write32(OUT, info->i2c.sdaEnReg, sda); Read32(OUT, info->i2c.sdaEnReg); return B_OK; } uint16 connector_pick_atom_hpdid(uint32 connectorIndex) { radeon_shared_info &info = *gInfo->shared_info; uint16 atomHPDID = 0xff; uint16 hpdPinIndex = gConnector[connectorIndex]->hpdPinIndex; if (info.dceMajor >= 4 && gGPIOInfo[hpdPinIndex]->valid) { // See mmDC_GPIO_HPD_A in drm for register value uint32 targetReg = AVIVO_DC_GPIO_HPD_A; if (info.dceMajor >= 13) { ERROR("WARNING: CHECK NEW DCE mmDC_GPIO_HPD_A value!\n"); targetReg = POL_mmDC_GPIO_HPD_A; } else if (info.dceMajor >= 12) targetReg = POL_mmDC_GPIO_HPD_A; else if (info.dceMajor >= 11) targetReg = CAR_mmDC_GPIO_HPD_A; else if (info.dceMajor >= 10) targetReg = VOL_mmDC_GPIO_HPD_A; else if (info.dceMajor >= 8) targetReg = SEA_mmDC_GPIO_HPD_A; else if (info.dceMajor >= 6) targetReg = SI_DC_GPIO_HPD_A; else if (info.dceMajor >= 4) targetReg = EVERGREEN_DC_GPIO_HPD_A; // You're drunk AMD, go home. (this makes no sense) if (gGPIOInfo[hpdPinIndex]->hwReg == targetReg) { switch(gGPIOInfo[hpdPinIndex]->hwMask) { case (1 << 0): atomHPDID = 0; break; case (1 << 8): atomHPDID = 1; break; case (1 << 16): atomHPDID = 2; break; case (1 << 24): atomHPDID = 3; break; case (1 << 26): atomHPDID = 4; break; case (1 << 28): atomHPDID = 5; break; } } } return atomHPDID; } bool connector_read_edid(uint32 connectorIndex, edid1_info* edid) { // ensure things are sane uint32 i2cPinIndex = gConnector[connectorIndex]->i2cPinIndex; if (gGPIOInfo[i2cPinIndex]->valid == false || gGPIOInfo[i2cPinIndex]->i2c.valid == false) { ERROR("%s: invalid gpio %" B_PRIu32 " for connector %" B_PRIu32 "\n", __func__, i2cPinIndex, connectorIndex); return false; } i2c_bus bus; ddc2_init_timing(&bus); bus.cookie = (void*)gGPIOInfo[i2cPinIndex]; bus.set_signals = &gpio_set_i2c_bit; bus.get_signals = &gpio_get_i2c_bit; gpio_lock_i2c(bus.cookie, true); status_t edid_result = ddc2_read_edid1(&bus, edid, NULL, NULL); gpio_lock_i2c(bus.cookie, false); if (edid_result != B_OK) return false; TRACE("%s: found edid monitor on connector #%" B_PRId32 "\n", __func__, connectorIndex); return true; } bool connector_read_mode_lvds(uint32 connectorIndex, display_mode* mode) { assert(mode); uint8 dceMajor; uint8 dceMinor; int index = GetIndexIntoMasterTable(DATA, LVDS_Info); uint16 offset; union atomLVDSInfo { struct _ATOM_LVDS_INFO info; struct _ATOM_LVDS_INFO_V12 info_12; }; // Wipe out display_mode memset(mode, 0, sizeof(display_mode)); if (atom_parse_data_header(gAtomContext, index, NULL, &dceMajor, &dceMinor, &offset) == B_OK) { union atomLVDSInfo* lvdsInfo = (union atomLVDSInfo*)(gAtomContext->bios + offset); display_timing* timing = &mode->timing; // Pixel Clock timing->pixel_clock = B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usPixClk) * 10; // Horizontal timing->h_display = B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usHActive); timing->h_total = timing->h_display + B_LENDIAN_TO_HOST_INT16( lvdsInfo->info.sLCDTiming.usHBlanking_Time); timing->h_sync_start = timing->h_display + B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usHSyncOffset); timing->h_sync_end = timing->h_sync_start + B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usHSyncWidth); // Vertical timing->v_display = B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usVActive); timing->v_total = timing->v_display + B_LENDIAN_TO_HOST_INT16( lvdsInfo->info.sLCDTiming.usVBlanking_Time); timing->v_sync_start = timing->v_display + B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usVSyncOffset); timing->v_sync_end = timing->v_sync_start + B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.sLCDTiming.usVSyncWidth); #if 0 // Who cares. uint32 powerDelay = B_LENDIAN_TO_HOST_INT16(lvdsInfo->info.usOffDelayInMs); #endif // Store special lvds flags the encoder setup needs gConnector[connectorIndex]->lvdsFlags = lvdsInfo->info.ucLVDS_Misc; // Spread Spectrum ID (in SS table) gInfo->lvdsSpreadSpectrumID = lvdsInfo->info.ucSS_Id; uint16 flags = B_LENDIAN_TO_HOST_INT16( lvdsInfo->info.sLCDTiming.susModeMiscInfo.usAccess); if ((flags & ATOM_VSYNC_POLARITY) == 0) timing->flags |= B_POSITIVE_VSYNC; if ((flags & ATOM_HSYNC_POLARITY) == 0) timing->flags |= B_POSITIVE_HSYNC; // Extra flags if ((flags & ATOM_INTERLACE) != 0) timing->flags |= B_TIMING_INTERLACED; #if 0 // We don't use these timing flags at the moment if ((flags & ATOM_COMPOSITESYNC) != 0) timing->flags |= MODE_FLAG_CSYNC; if ((flags & ATOM_DOUBLE_CLOCK_MODE) != 0) timing->flags |= MODE_FLAG_DBLSCAN; #endif mode->h_display_start = 0; mode->v_display_start = 0; mode->virtual_width = timing->h_display; mode->virtual_height = timing->v_display; // Assume 32-bit color mode->space = B_RGB32_LITTLE; TRACE("%s: %" B_PRIu32 " %" B_PRIu16 " %" B_PRIu16 " %" B_PRIu16 " %" B_PRIu16 " %" B_PRIu16 " %" B_PRIu16 " %" B_PRIu16 " %" B_PRIu16 "\n", __func__, timing->pixel_clock, timing->h_display, timing->h_sync_start, timing->h_sync_end, timing->h_total, timing->v_display, timing->v_sync_start, timing->v_sync_end, timing->v_total); return true; } return false; } static status_t connector_attach_gpio_i2c(uint32 connectorIndex, uint8 hwPin) { gConnector[connectorIndex]->i2cPinIndex = 0; for (uint32 i = 0; i < MAX_GPIO_PINS; i++) { if (gGPIOInfo[i]->hwPin != hwPin) continue; gConnector[connectorIndex]->i2cPinIndex = i; return B_OK; } // We couldnt find the GPIO pin in the known GPIO pins. TRACE("%s: can't find GPIO pin 0x%" B_PRIX8 " for connector %" B_PRIu32 "\n", __func__, hwPin, connectorIndex); return B_ERROR; } static status_t connector_attach_gpio_router(uint32 connectorIndex, uint8 hwPin) { gConnector[connectorIndex]->router.i2cPinIndex = 0; for (uint32 i = 0; i < MAX_GPIO_PINS; i++) { if (gGPIOInfo[i]->hwPin != hwPin) continue; gConnector[connectorIndex]->router.i2cPinIndex = i; return B_OK; } // We couldnt find the GPIO pin in the known GPIO pins. TRACE("%s: can't find GPIO pin 0x%" B_PRIX8 " for connector %" B_PRIu32 "\n", __func__, hwPin, connectorIndex); return B_ERROR; } static status_t connector_attach_gpio_hpd(uint32 connectorIndex, uint8 hwPin) { gConnector[connectorIndex]->hpdPinIndex = 0; for (uint32 i = 0; i < MAX_GPIO_PINS; i++) { if (gGPIOInfo[i]->hwPin != hwPin) continue; gConnector[connectorIndex]->hpdPinIndex = i; return B_OK; } // We couldnt find the GPIO pin in the known GPIO pins. TRACE("%s: can't find GPIO pin 0x%" B_PRIX8 " for connector %" B_PRIu32 "\n", __func__, hwPin, connectorIndex); return B_ERROR; } static status_t gpio_general_populate() { int index = GetIndexIntoMasterTable(DATA, GPIO_Pin_LUT); uint16 tableOffset; uint16 tableSize; struct _ATOM_GPIO_PIN_LUT* gpioInfo; if (atom_parse_data_header(gAtomContext, index, &tableSize, NULL, NULL, &tableOffset)) { ERROR("%s: could't read GPIO_Pin_LUT table from AtomBIOS index %d!\n", __func__, index); } gpioInfo = (struct _ATOM_GPIO_PIN_LUT*)(gAtomContext->bios + tableOffset); int numIndices = (tableSize - sizeof(ATOM_COMMON_TABLE_HEADER)) / sizeof(ATOM_GPIO_PIN_ASSIGNMENT); // Find the next available GPIO pin index int32 gpioIndex = -1; for(int32 pin = 0; pin < MAX_GPIO_PINS; pin++) { if (!gGPIOInfo[pin]->valid) { gpioIndex = pin; break; } } if (gpioIndex < 0) { ERROR("%s: ERROR: Out of space for additional GPIO pins!\n", __func__); return B_ERROR; } ATOM_GPIO_PIN_ASSIGNMENT* pin = gpioInfo->asGPIO_Pin; for (int i = 0; i < numIndices; i++) { if (gGPIOInfo[gpioIndex]->valid) { ERROR("%s: BUG: Attempting to fill already populated gpio pin!\n", __func__); return B_ERROR; } gGPIOInfo[gpioIndex]->valid = true; gGPIOInfo[gpioIndex]->i2c.valid = false; gGPIOInfo[gpioIndex]->hwPin = pin->ucGPIO_ID; gGPIOInfo[gpioIndex]->hwReg = B_LENDIAN_TO_HOST_INT16(pin->usGpioPin_AIndex) * 4; gGPIOInfo[gpioIndex]->hwMask = (1 << pin->ucGpioPinBitShift); pin = (ATOM_GPIO_PIN_ASSIGNMENT*)((uint8*)pin + sizeof(ATOM_GPIO_PIN_ASSIGNMENT)); TRACE("%s: general GPIO @ %" B_PRId32 ", valid: %s, " "hwPin: 0x%" B_PRIX32 "\n", __func__, gpioIndex, gGPIOInfo[gpioIndex]->valid ? "true" : "false", gGPIOInfo[gpioIndex]->hwPin); gpioIndex++; } return B_OK; } static status_t gpio_i2c_populate() { radeon_shared_info &info = *gInfo->shared_info; int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info); uint16 tableOffset; uint16 tableSize; if (atom_parse_data_header(gAtomContext, index, &tableSize, NULL, NULL, &tableOffset) != B_OK) { ERROR("%s: could't read GPIO_I2C_Info table from AtomBIOS index %d!\n", __func__, index); return B_ERROR; } struct _ATOM_GPIO_I2C_INFO* i2cInfo = (struct _ATOM_GPIO_I2C_INFO*)(gAtomContext->bios + tableOffset); uint32 numIndices = (tableSize - sizeof(ATOM_COMMON_TABLE_HEADER)) / sizeof(ATOM_GPIO_I2C_ASSIGMENT); if (numIndices > ATOM_MAX_SUPPORTED_DEVICE) { ERROR("%s: ERROR: AtomBIOS contains more GPIO_Info items then I" "was prepared for! (seen: %" B_PRIu32 "; max: %" B_PRIu32 ")\n", __func__, numIndices, (uint32)ATOM_MAX_SUPPORTED_DEVICE); return B_ERROR; } // Find the next available GPIO pin index int32 gpioIndex = -1; for(int32 pin = 0; pin < MAX_GPIO_PINS; pin++) { if (!gGPIOInfo[pin]->valid) { gpioIndex = pin; break; } } if (gpioIndex < 0) { ERROR("%s: ERROR: Out of space for additional GPIO pins!\n", __func__); return B_ERROR; } for (uint32 i = 0; i < numIndices; i++) { if (gGPIOInfo[gpioIndex]->valid) { ERROR("%s: BUG: Attempting to fill already populated gpio pin!\n", __func__); return B_ERROR; } ATOM_GPIO_I2C_ASSIGMENT* gpio = &i2cInfo->asGPIO_Info[i]; if (info.dceMajor >= 3) { if (i == 4 && B_LENDIAN_TO_HOST_INT16(gpio->usClkMaskRegisterIndex) == 0x1fda && gpio->sucI2cId.ucAccess == 0x94) { gpio->sucI2cId.ucAccess = 0x14; TRACE("%s: BUG: GPIO override for DCE 3 occured\n", __func__); } } if (info.dceMajor >= 4) { if (i == 7 && B_LENDIAN_TO_HOST_INT16(gpio->usClkMaskRegisterIndex) == 0x1936 && gpio->sucI2cId.ucAccess == 0) { gpio->sucI2cId.ucAccess = 0x97; gpio->ucDataMaskShift = 8; gpio->ucDataEnShift = 8; gpio->ucDataY_Shift = 8; gpio->ucDataA_Shift = 8; TRACE("%s: BUG: GPIO override for DCE 4 occured\n", __func__); } } // populate gpio information gGPIOInfo[gpioIndex]->hwPin = gpio->sucI2cId.ucAccess; gGPIOInfo[gpioIndex]->i2c.hwCapable = (gpio->sucI2cId.sbfAccess.bfHW_Capable) ? true : false; // GPIO mask (Allows software to control the GPIO pad) // 0 = chip access; 1 = only software; gGPIOInfo[gpioIndex]->i2c.sclMaskReg = B_LENDIAN_TO_HOST_INT16(gpio->usClkMaskRegisterIndex) * 4; gGPIOInfo[gpioIndex]->i2c.sdaMaskReg = B_LENDIAN_TO_HOST_INT16(gpio->usDataMaskRegisterIndex) * 4; gGPIOInfo[gpioIndex]->i2c.sclMask = 1 << gpio->ucClkMaskShift; gGPIOInfo[gpioIndex]->i2c.sdaMask = 1 << gpio->ucDataMaskShift; // GPIO output / write (A) enable // 0 = GPIO input (Y); 1 = GPIO output (A); gGPIOInfo[gpioIndex]->i2c.sclEnReg = B_LENDIAN_TO_HOST_INT16(gpio->usClkEnRegisterIndex) * 4; gGPIOInfo[gpioIndex]->i2c.sdaEnReg = B_LENDIAN_TO_HOST_INT16(gpio->usDataEnRegisterIndex) * 4; gGPIOInfo[gpioIndex]->i2c.sclEnMask = 1 << gpio->ucClkEnShift; gGPIOInfo[gpioIndex]->i2c.sdaEnMask = 1 << gpio->ucDataEnShift; // GPIO output / write (A) gGPIOInfo[gpioIndex]->i2c.sclAReg = B_LENDIAN_TO_HOST_INT16(gpio->usClkA_RegisterIndex) * 4; gGPIOInfo[gpioIndex]->i2c.sdaAReg = B_LENDIAN_TO_HOST_INT16(gpio->usDataA_RegisterIndex) * 4; gGPIOInfo[gpioIndex]->i2c.sclAMask = 1 << gpio->ucClkA_Shift; gGPIOInfo[gpioIndex]->i2c.sdaAMask = 1 << gpio->ucDataA_Shift; // GPIO input / read (Y) gGPIOInfo[gpioIndex]->i2c.sclYReg = B_LENDIAN_TO_HOST_INT16(gpio->usClkY_RegisterIndex) * 4; gGPIOInfo[gpioIndex]->i2c.sdaYReg = B_LENDIAN_TO_HOST_INT16(gpio->usDataY_RegisterIndex) * 4; gGPIOInfo[gpioIndex]->i2c.sclYMask = 1 << gpio->ucClkY_Shift; gGPIOInfo[gpioIndex]->i2c.sdaYMask = 1 << gpio->ucDataY_Shift; // ensure data is valid gGPIOInfo[gpioIndex]->i2c.valid = gGPIOInfo[gpioIndex]->i2c.sclMaskReg ? true : false; gGPIOInfo[gpioIndex]->valid = gGPIOInfo[gpioIndex]->i2c.valid; TRACE("%s: i2c GPIO @ %" B_PRIu32 ", valid: %s, hwPin: 0x%" B_PRIX32 "\n", __func__, gpioIndex, gGPIOInfo[gpioIndex]->valid ? "true" : "false", gGPIOInfo[gpioIndex]->hwPin); gpioIndex++; } return B_OK; } status_t gpio_populate() { status_t result = gpio_general_populate(); if (result != B_OK) return result; result = gpio_i2c_populate(); return result; } status_t connector_probe_legacy() { int index = GetIndexIntoMasterTable(DATA, SupportedDevicesInfo); uint8 tableMajor; uint8 tableMinor; uint16 tableSize; uint16 tableOffset; if (atom_parse_data_header(gAtomContext, index, &tableSize, &tableMajor, &tableMinor, &tableOffset) != B_OK) { ERROR("%s: unable to parse data header!\n", __func__); return B_ERROR; } union atomSupportedDevices { struct _ATOM_SUPPORTED_DEVICES_INFO info; struct _ATOM_SUPPORTED_DEVICES_INFO_2 info_2; struct _ATOM_SUPPORTED_DEVICES_INFO_2d1 info_2d1; }; union atomSupportedDevices* supportedDevices; supportedDevices = (union atomSupportedDevices*) (gAtomContext->bios + tableOffset); uint16 deviceSupport = B_LENDIAN_TO_HOST_INT16(supportedDevices->info.usDeviceSupport); uint32 maxDevice; if (tableMajor > 1) maxDevice = ATOM_MAX_SUPPORTED_DEVICE; else maxDevice = ATOM_MAX_SUPPORTED_DEVICE_INFO; uint32 i; uint32 connectorIndex = 0; for (i = 0; i < maxDevice; i++) { gConnector[connectorIndex]->valid = false; // check if this connector is used if ((deviceSupport & (1 << i)) == 0) continue; if (i == ATOM_DEVICE_CV_INDEX) { TRACE("%s: skipping component video\n", __func__); continue; } ATOM_CONNECTOR_INFO_I2C ci = supportedDevices->info.asConnInfo[i]; gConnector[connectorIndex]->type = kConnectorConvertLegacy[ ci.sucConnectorInfo.sbfAccess.bfConnectorType]; if (gConnector[connectorIndex]->type == VIDEO_CONNECTOR_UNKNOWN) { TRACE("%s: skipping unknown connector at %" B_PRId32 " of 0x%" B_PRIX8 "\n", __func__, i, ci.sucConnectorInfo.sbfAccess.bfConnectorType); continue; } // TODO: give tv unique connector ids // Always set CRT1 and CRT2 as VGA, some cards incorrectly set // VGA ports as DVI if (i == ATOM_DEVICE_CRT1_INDEX || i == ATOM_DEVICE_CRT2_INDEX) gConnector[connectorIndex]->type = VIDEO_CONNECTOR_VGA; uint8 dac = ci.sucConnectorInfo.sbfAccess.bfAssociatedDAC; uint32 encoderObject = encoder_object_lookup((1 << i), dac); uint32 encoderID = (encoderObject & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; gConnector[connectorIndex]->valid = true; gConnector[connectorIndex]->flags = (1 << i); gConnector[connectorIndex]->encoder.valid = true; gConnector[connectorIndex]->encoder.objectID = encoderID; gConnector[connectorIndex]->encoder.type = encoder_type_lookup(encoderID, (1 << i)); // TODO: Eval external encoders on legacy connector probe gConnector[connectorIndex]->encoderExternal.valid = false; // encoder_is_external(encoderID); connector_attach_gpio_i2c(connectorIndex, ci.sucI2cId.ucAccess); pll_limit_probe(&gConnector[connectorIndex]->encoder.pll); connectorIndex++; } // TODO: combine shared connectors if (connectorIndex == 0) { TRACE("%s: zero connectors found using legacy detection\n", __func__); return B_ERROR; } return B_OK; } // r600+ status_t connector_probe() { int index = GetIndexIntoMasterTable(DATA, Object_Header); uint8 tableMajor; uint8 tableMinor; uint16 tableSize; uint16 tableOffset; if (atom_parse_data_header(gAtomContext, index, &tableSize, &tableMajor, &tableMinor, &tableOffset) != B_OK) { ERROR("%s: ERROR: parsing data header failed!\n", __func__); return B_ERROR; } if (tableMinor < 2) { ERROR("%s: ERROR: table minor version unknown! " "(%" B_PRIu8 ".%" B_PRIu8 ")\n", __func__, tableMajor, tableMinor); return B_ERROR; } ATOM_CONNECTOR_OBJECT_TABLE* connectorObject; ATOM_ENCODER_OBJECT_TABLE* encoderObject; ATOM_OBJECT_TABLE* routerObject; ATOM_DISPLAY_OBJECT_PATH_TABLE* pathObject; ATOM_OBJECT_HEADER* objectHeader; objectHeader = (ATOM_OBJECT_HEADER*)(gAtomContext->bios + tableOffset); pathObject = (ATOM_DISPLAY_OBJECT_PATH_TABLE*) (gAtomContext->bios + tableOffset + B_LENDIAN_TO_HOST_INT16(objectHeader->usDisplayPathTableOffset)); connectorObject = (ATOM_CONNECTOR_OBJECT_TABLE*) (gAtomContext->bios + tableOffset + B_LENDIAN_TO_HOST_INT16(objectHeader->usConnectorObjectTableOffset)); encoderObject = (ATOM_ENCODER_OBJECT_TABLE*) (gAtomContext->bios + tableOffset + B_LENDIAN_TO_HOST_INT16(objectHeader->usEncoderObjectTableOffset)); routerObject = (ATOM_OBJECT_TABLE*) (gAtomContext->bios + tableOffset + B_LENDIAN_TO_HOST_INT16(objectHeader->usRouterObjectTableOffset)); int deviceSupport = B_LENDIAN_TO_HOST_INT16(objectHeader->usDeviceSupport); int pathSize = 0; int32 i = 0; TRACE("%s: found %" B_PRIu8 " potential display paths.\n", __func__, pathObject->ucNumOfDispPath); uint32 connectorIndex = 0; for (i = 0; i < pathObject->ucNumOfDispPath; i++) { if (connectorIndex >= ATOM_MAX_SUPPORTED_DEVICE) continue; uint8* address = (uint8*)pathObject->asDispPath; ATOM_DISPLAY_OBJECT_PATH* path; address += pathSize; path = (ATOM_DISPLAY_OBJECT_PATH*)address; pathSize += B_LENDIAN_TO_HOST_INT16(path->usSize); uint32 connectorType; uint16 connectorFlags = B_LENDIAN_TO_HOST_INT16(path->usDeviceTag); if ((deviceSupport & connectorFlags) != 0) { uint16 connectorObjectID = (B_LENDIAN_TO_HOST_INT16(path->usConnObjectId) & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; //uint8 con_obj_num // = (B_LENDIAN_TO_HOST_INT16(path->usConnObjectId) // & ENUM_ID_MASK) >> ENUM_ID_SHIFT; //uint8 con_obj_type // = (B_LENDIAN_TO_HOST_INT16(path->usConnObjectId) // & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; if (connectorFlags == ATOM_DEVICE_CV_SUPPORT) { TRACE("%s: Path #%" B_PRId32 ": skipping component video.\n", __func__, i); continue; } radeon_shared_info &info = *gInfo->shared_info; // protect kConnectorConvert if (connectorObjectID >= B_COUNT_OF(kConnectorConvert)) { // This can happen when new atombios revisions introduce // new CONNECTOR_OBJECT_ID_* defines (rare) ERROR("%s: Path #%" B_PRId32 ": Unknown connector object ID!\n", __func__, i); continue; } uint16 igpLaneInfo; if ((info.chipsetFlags & CHIP_IGP) != 0) { ERROR("%s: TODO: IGP chip connector detection\n", __func__); // try non-IGP method for now igpLaneInfo = 0; connectorType = kConnectorConvert[connectorObjectID]; } else { igpLaneInfo = 0; connectorType = kConnectorConvert[connectorObjectID]; } if (connectorType == VIDEO_CONNECTOR_UNKNOWN) { ERROR("%s: Path #%" B_PRId32 ": skipping unknown connector.\n", __func__, i); continue; } connector_info* connector = gConnector[connectorIndex]; int32 j; for (j = 0; j < ((B_LENDIAN_TO_HOST_INT16(path->usSize) - 8) / 2); j++) { //uint16 grph_obj_id // = (B_LENDIAN_TO_HOST_INT16(path->usGraphicObjIds[j]) // & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; //uint8 grph_obj_num // = (B_LENDIAN_TO_HOST_INT16(path->usGraphicObjIds[j]) & // ENUM_ID_MASK) >> ENUM_ID_SHIFT; uint8 graphicObjectType = (B_LENDIAN_TO_HOST_INT16(path->usGraphicObjIds[j]) & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; if (graphicObjectType == GRAPH_OBJECT_TYPE_ENCODER) { // Found an encoder int32 k; for (k = 0; k < encoderObject->ucNumberOfObjects; k++) { uint16 encoderObjectRaw = B_LENDIAN_TO_HOST_INT16( encoderObject->asObjects[k].usObjectID); if (B_LENDIAN_TO_HOST_INT16(path->usGraphicObjIds[j]) == encoderObjectRaw) { ATOM_COMMON_RECORD_HEADER* record = (ATOM_COMMON_RECORD_HEADER*) ((uint16*)gAtomContext->bios + tableOffset + B_LENDIAN_TO_HOST_INT16( encoderObject->asObjects[k].usRecordOffset)); ATOM_ENCODER_CAP_RECORD* capRecord; uint16 caps = 0; while (record->ucRecordSize > 0 && record->ucRecordType > 0 && record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { switch (record->ucRecordType) { case ATOM_ENCODER_CAP_RECORD_TYPE: capRecord = (ATOM_ENCODER_CAP_RECORD*) record; caps = B_LENDIAN_TO_HOST_INT16( capRecord->usEncoderCap); break; } record = (ATOM_COMMON_RECORD_HEADER*) ((char*)record + record->ucRecordSize); } uint32 encoderID = (encoderObjectRaw & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; uint32 encoderType = encoder_type_lookup(encoderID, connectorFlags); if (encoderType == VIDEO_ENCODER_NONE) { ERROR("%s: Path #%" B_PRId32 ":" "skipping unknown encoder.\n", __func__, i); continue; } encoder_info* encoder; // External encoders are behind DVO or UNIPHY if (encoder_is_external(encoderID)) { encoder = &connector->encoderExternal; encoder->isExternal = true; encoder->isDPBridge = encoder_is_dp_bridge(encoderID); } else { encoder = &connector->encoder; encoder->isExternal = false; encoder->isDPBridge = false; } // Set up found connector encoder generics encoder->valid = true; encoder->capabilities = caps; encoder->objectID = encoderID; encoder->type = encoderType; encoder->linkEnumeration = (encoderObjectRaw & ENUM_ID_MASK) >> ENUM_ID_SHIFT; pll_limit_probe(&encoder->pll); } } // END if object is encoder } else if (graphicObjectType == GRAPH_OBJECT_TYPE_ROUTER) { int32 k; for (k = 0; k < routerObject->ucNumberOfObjects; k++) { uint16 routerObjectID = B_LENDIAN_TO_HOST_INT16(routerObject->asObjects[k].usObjectID); if (B_LENDIAN_TO_HOST_INT16(path->usGraphicObjIds[j]) == routerObjectID) { ATOM_COMMON_RECORD_HEADER* record = (ATOM_COMMON_RECORD_HEADER*) ((uint16*)gAtomContext->bios + tableOffset + B_LENDIAN_TO_HOST_INT16( routerObject->asObjects[k].usRecordOffset)); ATOM_I2C_RECORD* i2cRecord; ATOM_I2C_ID_CONFIG_ACCESS* i2cConfig; ATOM_ROUTER_DDC_PATH_SELECT_RECORD* ddcPath; ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD* cdPath; ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT* routerConnTable = (ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *) ((uint16*)gAtomContext->bios + tableOffset + B_LENDIAN_TO_HOST_INT16( routerObject->asObjects[k].usSrcDstTableOffset)); uint8* destObjCount = (uint8*)((uint8*)routerConnTable + 1 + (routerConnTable->ucNumberOfSrc * 2)); uint16 *dstObjs = (uint16 *)(destObjCount + 1); int enumId; router_info* router = &connector->router; router->objectID = routerObjectID; for (enumId = 0; enumId < (*destObjCount); enumId++) { if (B_LENDIAN_TO_HOST_INT16(path->usConnObjectId) == B_LENDIAN_TO_HOST_INT16(dstObjs[enumId])) break; } while (record->ucRecordSize > 0 && record->ucRecordType > 0 && record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { switch (record->ucRecordType) { case ATOM_I2C_RECORD_TYPE: i2cRecord = (ATOM_I2C_RECORD*)record; i2cConfig = (ATOM_I2C_ID_CONFIG_ACCESS*)&i2cRecord->sucI2cId; connector_attach_gpio_router(connectorIndex, i2cConfig->ucAccess); router->i2cAddr = i2cRecord->ucI2CAddr >> 1; // ?? break; case ATOM_ROUTER_DDC_PATH_SELECT_RECORD_TYPE: ddcPath = (ATOM_ROUTER_DDC_PATH_SELECT_RECORD*)record; router->ddcValid = true; router->ddcMuxType = ddcPath->ucMuxType; router->ddcMuxControlPin = ddcPath->ucMuxControlPin; router->ddcMuxState = ddcPath->ucMuxState[enumId]; break; case ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD_TYPE: cdPath = (ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD *)record; router->cdValid = true; router->cdMuxType = cdPath->ucMuxType; router->cdMuxControlPin = cdPath->ucMuxControlPin; router->cdMuxState = cdPath->ucMuxState[enumId]; break; } // move to next record record = (ATOM_COMMON_RECORD_HEADER*) ((char *)record + record->ucRecordSize); } } } } // END if object is router } // Set up information buses such as ddc if (((connectorFlags & ATOM_DEVICE_TV_SUPPORT) == 0) && (connectorFlags & ATOM_DEVICE_CV_SUPPORT) == 0) { for (j = 0; j < connectorObject->ucNumberOfObjects; j++) { if (B_LENDIAN_TO_HOST_INT16(path->usConnObjectId) == B_LENDIAN_TO_HOST_INT16( connectorObject->asObjects[j].usObjectID)) { ATOM_COMMON_RECORD_HEADER* record = (ATOM_COMMON_RECORD_HEADER*)(gAtomContext->bios + tableOffset + B_LENDIAN_TO_HOST_INT16( connectorObject->asObjects[j].usRecordOffset)); while (record->ucRecordSize > 0 && record->ucRecordType > 0 && record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { ATOM_I2C_RECORD* i2cRecord; ATOM_I2C_ID_CONFIG_ACCESS* i2cConfig; ATOM_HPD_INT_RECORD* hpdRecord; switch (record->ucRecordType) { case ATOM_I2C_RECORD_TYPE: i2cRecord = (ATOM_I2C_RECORD*)record; i2cConfig = (ATOM_I2C_ID_CONFIG_ACCESS*) &i2cRecord->sucI2cId; connector_attach_gpio_i2c(connectorIndex, i2cConfig->ucAccess); break; case ATOM_HPD_INT_RECORD_TYPE: hpdRecord = (ATOM_HPD_INT_RECORD*)record; connector_attach_gpio_hpd(connectorIndex, hpdRecord->ucHPDIntGPIOID); break; } // move to next record record = (ATOM_COMMON_RECORD_HEADER*) ((char*)record + record->ucRecordSize); } } } } connector->valid = true; connector->flags = connectorFlags; connector->type = connectorType; connector->objectID = connectorObjectID; connectorIndex++; } // END for each valid connector } // end for each display path return B_OK; } bool connector_is_dp(uint32 connectorIndex) { connector_info* connector = gConnector[connectorIndex]; // Traditional DisplayPort connector, or DP over USBC if (connector->type == VIDEO_CONNECTOR_DP || connector->type == VIDEO_CONNECTOR_EDP || connector->type == VIDEO_CONNECTOR_USBC) { return true; } // DisplayPort bridge on external encoder if (connector->encoderExternal.valid == true && connector->encoderExternal.isDPBridge == true) { return true; } return false; } void debug_connectors() { ERROR("Currently detected connectors=============\n"); for (uint32 id = 0; id < ATOM_MAX_SUPPORTED_DEVICE; id++) { if (gConnector[id]->valid == true) { uint32 connectorType = gConnector[id]->type; uint16 i2cPinIndex = gConnector[id]->i2cPinIndex; uint16 hpdPinIndex = gConnector[id]->hpdPinIndex; ERROR("Connector #%" B_PRIu32 ")\n", id); ERROR(" + connector: %s\n", get_connector_name(connectorType)); ERROR(" + i2c gpio table id: %" B_PRIu16 "\n", i2cPinIndex); ERROR(" - gpio hw pin: 0x%" B_PRIX32 "\n", gGPIOInfo[i2cPinIndex]->hwPin); ERROR(" - gpio valid: %s\n", gGPIOInfo[i2cPinIndex]->valid ? "true" : "false"); ERROR(" - i2c valid: %s\n", gGPIOInfo[i2cPinIndex]->i2c.valid ? "true" : "false"); // hot plug detection info ERROR(" + hpd gpio table id: %" B_PRIu16 "\n", hpdPinIndex); ERROR(" - gpio hw pin: 0x%" B_PRIX32 "\n", gGPIOInfo[hpdPinIndex]->hwPin); ERROR(" - gpio valid: %s\n", gGPIOInfo[hpdPinIndex]->valid ? "true" : "false"); // router info router_info* router = &gConnector[id]->router; ERROR(" + router gpio table id: %" B_PRIu16 "\n", router->i2cPinIndex); ERROR(" + router (ddc): %s\n", router->ddcValid ? "true" : "false"); if (router->ddcValid) { ERROR(" - mux type: 0x%" B_PRIX8 "\n", router->ddcMuxType); ERROR(" - mux pin: 0x%" B_PRIX8 "\n", router->ddcMuxControlPin); ERROR(" - mux state: 0x%" B_PRIX8 "\n", router->ddcMuxState); } ERROR(" + router (c/d): %s\n", router->cdValid ? "true" : "false"); if (router->cdValid) { ERROR(" - mux type: 0x%" B_PRIX8 "\n", router->cdMuxType); ERROR(" - mux pin: 0x%" B_PRIX8 "\n", router->cdMuxControlPin); ERROR(" - mux state: 0x%" B_PRIX8 "\n", router->cdMuxState); } // encoder info encoder_info* encoder = &gConnector[id]->encoder; ERROR(" + encoder: %s\n", get_encoder_name(encoder->type)); ERROR(" - id: %" B_PRIu16 "\n", encoder->objectID); ERROR(" - type: %s\n", encoder_name_lookup(encoder->objectID)); ERROR(" - capabilities: 0x%" B_PRIX32 "\n", encoder->capabilities); ERROR(" - enumeration: %" B_PRIu32 "\n", encoder->linkEnumeration); encoder = &gConnector[id]->encoderExternal; ERROR(" - is bridge: %s\n", encoder->valid ? "true" : "false"); if (!encoder->valid) ERROR(" + external encoder: none\n"); else { ERROR(" + external encoder: %s\n", get_encoder_name(encoder->type)); ERROR(" - valid: true\n"); ERROR(" - id: %" B_PRIu16 "\n", encoder->objectID); ERROR(" - type: %s\n", encoder_name_lookup(encoder->objectID)); ERROR(" - enumeration: %" B_PRIu32 "\n", encoder->linkEnumeration); } uint32 connectorFlags = gConnector[id]->flags; bool flags = false; ERROR(" + flags:\n"); if ((connectorFlags & ATOM_DEVICE_CRT1_SUPPORT) != 0) { ERROR(" * device CRT1 support\n"); flags = true; } if ((connectorFlags & ATOM_DEVICE_CRT2_SUPPORT) != 0) { ERROR(" * device CRT2 support\n"); flags = true; } if ((connectorFlags & ATOM_DEVICE_LCD1_SUPPORT) != 0) { ERROR(" * device LCD1 support\n"); flags = true; } if ((connectorFlags & ATOM_DEVICE_LCD2_SUPPORT) != 0) { ERROR(" * device LCD2 support\n"); flags = true; } if ((connectorFlags & ATOM_DEVICE_TV1_SUPPORT) != 0) { ERROR(" * device TV1 support\n"); flags = true; } if ((connectorFlags & ATOM_DEVICE_CV_SUPPORT) != 0) { ERROR(" * device CV support\n"); flags = true; } if ((connectorFlags & ATOM_DEVICE_DFP1_SUPPORT) != 0) { ERROR(" * device DFP1 support\n"); flags = true; } if ((connectorFlags & ATOM_DEVICE_DFP2_SUPPORT) != 0) { ERROR(" * device DFP2 support\n"); flags = true; } if ((connectorFlags & ATOM_DEVICE_DFP3_SUPPORT) != 0) { ERROR(" * device DFP3 support\n"); flags = true; } if ((connectorFlags & ATOM_DEVICE_DFP4_SUPPORT) != 0) { ERROR(" * device DFP4 support\n"); flags = true; } if ((connectorFlags & ATOM_DEVICE_DFP5_SUPPORT) != 0) { ERROR(" * device DFP5 support\n"); flags = true; } if ((connectorFlags & ATOM_DEVICE_DFP6_SUPPORT) != 0) { ERROR(" * device DFP6 support\n"); flags = true; } if (flags == false) ERROR(" * no known flags\n"); } } ERROR("==========================================\n"); }