mirror of
https://github.com/NVIDIA/open-gpu-kernel-modules.git
synced 2026-01-30 04:59:46 +00:00
1754 lines
62 KiB
C++
1754 lines
62 KiB
C++
/*
|
|
* SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
* SPDX-License-Identifier: MIT
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
/******************************* DisplayPort********************************\
|
|
* *
|
|
* Module: dp_connectorimpl2x.cpp *
|
|
* DP connector implementation for DP2.x *
|
|
* *
|
|
\***************************************************************************/
|
|
|
|
|
|
|
|
#include "dp_connectorimpl.h"
|
|
#include "dp_connectorimpl2x.h"
|
|
#include "dp_evoadapter2x.h"
|
|
#include "ctrl/ctrl0073/ctrl0073dp.h"
|
|
#include "dp_printf.h"
|
|
|
|
using namespace DisplayPort;
|
|
//
|
|
// The regkey value is available in connector, so using a global variable
|
|
// to expose it in LinkConfiguration
|
|
//
|
|
NvU32 bSupportInternalUhbrOnFpga;
|
|
|
|
/*!
|
|
* @brief Convert data rate to link rate
|
|
*
|
|
* @param[in] dataRate The data rate needs to be converted.
|
|
*
|
|
* @return link rate corresponding to the dataRate.
|
|
*
|
|
*/
|
|
|
|
void ConnectorImpl2x::applyDP2xRegkeyOverrides()
|
|
{
|
|
this->bSupportUHBR2_50 = dpRegkeyDatabase.supportInternalUhbrOnFpga & NV_DP2X_REGKEY_FPGA_UHBR_SUPPORT_2_5G;
|
|
this->bSupportUHBR2_70 = dpRegkeyDatabase.supportInternalUhbrOnFpga & NV_DP2X_REGKEY_FPGA_UHBR_SUPPORT_2_7G;
|
|
this->bSupportUHBR5_00 = dpRegkeyDatabase.supportInternalUhbrOnFpga & NV_DP2X_REGKEY_FPGA_UHBR_SUPPORT_5_0G;
|
|
this->maxLinkRateFromRegkey = dpRegkeyDatabase.applyMaxLinkRateOverrides;
|
|
bSupportInternalUhbrOnFpga = dpRegkeyDatabase.supportInternalUhbrOnFpga;
|
|
if (dpRegkeyDatabase.bIgnoreCableIdCaps)
|
|
{
|
|
hal->setIgnoreCableIdCaps(true);
|
|
}
|
|
}
|
|
|
|
bool ConnectorImpl2x::willLinkSupportModeSST
|
|
(
|
|
const LinkConfiguration & linkConfig,
|
|
const ModesetInfo & modesetInfo,
|
|
const DscParams *pDscParams
|
|
)
|
|
{
|
|
LinkConfiguration lc = linkConfig;
|
|
if (!main->isSupportedDPLinkConfig(lc))
|
|
return false;
|
|
// no headIndex (default 0) for mode enumeration.
|
|
return willLinkSupportMode(linkConfig, modesetInfo, 0, NULL, pDscParams);
|
|
}
|
|
|
|
bool ConnectorImpl2x::willLinkSupportMode
|
|
(
|
|
const LinkConfiguration &linkConfig,
|
|
const ModesetInfo &modesetInfo,
|
|
NvU32 headIndex,
|
|
Watermark *watermark,
|
|
const DscParams *pDscParams
|
|
)
|
|
{
|
|
//
|
|
// mode is not known yet, we have to report is possible
|
|
// Otherwise we're going to mark all devices as zombies on first HPD(c),
|
|
// since modeset info is not available.
|
|
//
|
|
if (modesetInfo.pixelClockHz == 0)
|
|
return true;
|
|
|
|
if (linkConfig.lanes == 0 || linkConfig.peakRate == 0)
|
|
return false;
|
|
|
|
DP_ASSERT(this->isFECSupported());
|
|
|
|
EvoInterface *provider = ((EvoMainLink *)main)->getProvider();
|
|
NvU32 nvosStatus = NVOS_STATUS_SUCCESS;
|
|
|
|
NV0073_CTRL_CMD_CALCULATE_DP_IMP_PARAMS impParams;
|
|
dpMemZero(&impParams, sizeof(NV0073_CTRL_CMD_CALCULATE_DP_IMP_PARAMS));
|
|
|
|
impParams.subDeviceInstance = provider->getSubdeviceIndex();
|
|
impParams.displayId = provider->getDisplayId();
|
|
impParams.headIndex = headIndex;
|
|
|
|
impParams.modesetInfo.twoChannelAudioHz = modesetInfo.twoChannelAudioHz;
|
|
impParams.modesetInfo.eightChannelAudioHz = modesetInfo.eightChannelAudioHz;
|
|
impParams.modesetInfo.pixelFrequencyKHz = (NvU32)(modesetInfo.pixelClockHz / 1000);
|
|
impParams.modesetInfo.rasterWidth = modesetInfo.rasterWidth;
|
|
impParams.modesetInfo.surfaceWidth = modesetInfo.surfaceWidth;
|
|
impParams.modesetInfo.surfaceHeight = modesetInfo.surfaceHeight;
|
|
impParams.modesetInfo.depth = modesetInfo.depth;
|
|
impParams.modesetInfo.rasterBlankStartX = modesetInfo.rasterBlankStartX;
|
|
impParams.modesetInfo.rasterBlankEndX = modesetInfo.rasterBlankEndX;
|
|
impParams.modesetInfo.bitsPerComponent = modesetInfo.bitsPerComponent;
|
|
impParams.modesetInfo.colorFormat = modesetInfo.colorFormat;
|
|
impParams.modesetInfo.bDSCEnabled = modesetInfo.bEnableDsc;
|
|
|
|
impParams.linkConfig.linkRate10M = (NvU32)linkConfig.peakRate;
|
|
impParams.linkConfig.laneCount = linkConfig.lanes;
|
|
impParams.linkConfig.bEnhancedFraming = linkConfig.enhancedFraming;
|
|
impParams.linkConfig.bDp2xChannelCoding = linkConfig.bIs128b132bChannelCoding;
|
|
impParams.linkConfig.bFECEnabled = linkConfig.bEnableFEC;
|
|
impParams.linkConfig.bMultiStreamTopology = linkConfig.multistream;
|
|
|
|
if (pDscParams != NULL && pDscParams->forcedParams != NULL)
|
|
{
|
|
impParams.dscInfo.sliceCount = pDscParams->forcedParams->sliceCount;
|
|
impParams.dscInfo.sliceWidth = pDscParams->forcedParams->sliceWidth;
|
|
impParams.dscInfo.sliceHeight = pDscParams->forcedParams->sliceHeight;
|
|
impParams.dscInfo.dscVersionMajor = pDscParams->forcedParams->dscRevision.versionMajor;
|
|
impParams.dscInfo.dscVersionMinor = pDscParams->forcedParams->dscRevision.versionMinor;
|
|
}
|
|
|
|
nvosStatus = provider->rmControl0073(NV0073_CTRL_CMD_CALCULATE_DP_IMP,
|
|
&impParams, sizeof impParams);
|
|
|
|
if ((nvosStatus == NVOS_STATUS_SUCCESS) && (watermark != NULL))
|
|
{
|
|
watermark->waterMark = impParams.watermark.waterMark;
|
|
watermark->tuSize = impParams.watermark.tuSize;
|
|
watermark->hBlankSym = impParams.watermark.hBlankSym;
|
|
watermark->vBlankSym = impParams.watermark.vBlankSym;
|
|
watermark->effectiveBpp = impParams.watermark.effectiveBpp;
|
|
}
|
|
|
|
return impParams.watermark.bIsModePossible;
|
|
}
|
|
|
|
/*!
|
|
* @brief Check if the link configuration is valid on the system to be trained.
|
|
*
|
|
* @param[in] lConfig The link configuration to be validated.
|
|
*
|
|
* @return If the input link configuration is valid
|
|
*/
|
|
bool ConnectorImpl2x::validateLinkConfiguration(const LinkConfiguration &lConfig)
|
|
{
|
|
NvU64 linkRate10M = lConfig.peakRate;
|
|
|
|
if (!IS_VALID_LANECOUNT(lConfig.lanes))
|
|
{
|
|
DP_PRINTF(DP_ERROR, "DP2xCONN> Invalid Lane Count=%d", lConfig.lanes);
|
|
return false;
|
|
}
|
|
|
|
if (lConfig.lanes > hal->getMaxLaneCount())
|
|
{
|
|
DP_PRINTF(DP_ERROR, "DP2xCONN> Requested Lane Count=%d is larger than sinkMaxLaneCount=%d",
|
|
lConfig.lanes, hal->getMaxLaneCount());
|
|
return false;
|
|
}
|
|
|
|
if (lConfig.lanes != 0)
|
|
{
|
|
if (!IS_VALID_DP2_X_LINKBW(linkRate10M))
|
|
{
|
|
DP_PRINTF(DP_ERROR, "DP2xCONN> Requested link rate=%d is not valid", linkRate10M);
|
|
return false;
|
|
}
|
|
|
|
if (lConfig.peakRate > hal->getMaxLinkRate())
|
|
{
|
|
DP_PRINTF(DP_ERROR, "DP2xCONN> Requested link rate=%d is larger than sinkMaxLinkRate=%d",
|
|
linkRate10M, hal->getMaxLinkRate());
|
|
return false;
|
|
}
|
|
|
|
if (IS_INTERMEDIATE_DP2_X_LINKBW(linkRate10M))
|
|
{
|
|
NvU16 *ilrTable;
|
|
NvU32 i;
|
|
if (!hal->isIndexedLinkrateEnabled())
|
|
{
|
|
DP_PRINTF(DP_ERROR, "DP2xCONN> Indexed Link Rate=%d is Not Enabled in Sink", linkRate10M);
|
|
return false;
|
|
}
|
|
|
|
ilrTable = hal->getLinkRateTable();
|
|
for (i = 0; i < NV0073_CTRL_DP_MAX_INDEXED_LINK_RATES; i++)
|
|
{
|
|
//
|
|
// lConfig.peakRate is in 10M convention and ilrTable entries are the values read from DPCD which is 200k convention
|
|
// Convert the ilrTable value to 10M from 200KHz before the comparison
|
|
//
|
|
if (LINK_RATE_200KHZ_TO_10MHZ(ilrTable[i]) == linkRate10M)
|
|
break;
|
|
if (ilrTable[i] == 0)
|
|
{
|
|
DP_PRINTF(DP_ERROR, "DP2xCONN> Indexed Link Rate=%d is Not Found", linkRate10M);
|
|
return false;
|
|
}
|
|
}
|
|
if (i == NV0073_CTRL_DP_MAX_INDEXED_LINK_RATES)
|
|
{
|
|
DP_PRINTF(DP_ERROR, "DP2xCONN> Indexed Link Rate=%d is Not Found", linkRate10M);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ConnectorImpl2x::populateAllDpConfigs()
|
|
{
|
|
LinkRate maxLinkRate;
|
|
|
|
// maxLinkRateSupported checks GPU and DFP caps. UHBR is included.
|
|
LinkRate gpuMaxLinkRate = main->maxLinkRateSupported();
|
|
NvU32 gpuUhbrCaps = main->getUHBRSupported();
|
|
|
|
// sinkMaxLinkRate checks LTTPR and sink caps. UHBR is included.
|
|
LinkRate sinkMaxLinkRate = hal->getMaxLinkRate();
|
|
NvU32 sinkUhbrCaps = hal->getUHBRSupported();
|
|
NvU32 uhbrCaps = gpuUhbrCaps & sinkUhbrCaps;
|
|
|
|
unsigned maxLaneCount = 4U;
|
|
|
|
// For eDP, user can override maxLinkRate thru regkey, with valid value.
|
|
if ((main->isEDP()) &&
|
|
maxLinkRateFromRegkey && (IS_VALID_DP2_X_LINKBW(maxLinkRateFromRegkey)))
|
|
{
|
|
sinkMaxLinkRate = maxLinkRateFromRegkey;
|
|
}
|
|
|
|
if (FLD_TEST_DRF(0073_CTRL_CMD_DP, _GET_CAPS_UHBR_SUPPORTED, _UHBR20_0, _YES, uhbrCaps))
|
|
{
|
|
maxLinkRate = dp2LinkRate_20_0Gbps;
|
|
}
|
|
else if (FLD_TEST_DRF(0073_CTRL_CMD_DP, _GET_CAPS_UHBR_SUPPORTED, _UHBR13_5, _YES, uhbrCaps))
|
|
{
|
|
maxLinkRate = dp2LinkRate_13_5Gbps;
|
|
}
|
|
else if (FLD_TEST_DRF(0073_CTRL_CMD_DP, _GET_CAPS_UHBR_SUPPORTED, _UHBR10_0, _YES, uhbrCaps))
|
|
{
|
|
maxLinkRate = dp2LinkRate_10_0Gbps;
|
|
}
|
|
else
|
|
{
|
|
maxLinkRate = sinkMaxLinkRate ? DP_MIN(sinkMaxLinkRate, gpuMaxLinkRate) : gpuMaxLinkRate;
|
|
}
|
|
|
|
if (hal->getMaxLaneCount())
|
|
{
|
|
maxLaneCount = DP_MIN(hal->getMaxLaneCountSupportedAtLinkRate(maxLinkRate),
|
|
hal->getMaxLaneCount());
|
|
}
|
|
|
|
// If sinkMaxLinkRate == 0 it means there is no real sink
|
|
main->updateFallbackMap(maxLaneCount, maxLinkRate,
|
|
sinkMaxLinkRate ? uhbrCaps : gpuUhbrCaps);
|
|
|
|
ConnectorImpl::populateAllDpConfigs();
|
|
}
|
|
|
|
/*!
|
|
* @brief Find all the supported link rates.
|
|
*
|
|
* @return The list of supported link rates.
|
|
*/
|
|
LinkRates* ConnectorImpl2x::importDpLinkRates()
|
|
{
|
|
LinkRate linkRate;
|
|
LinkRates *pConnectorLinkRates = linkPolicy.getLinkRates();
|
|
|
|
NvU32 gpuUhbrCaps = main->getUHBRSupported();
|
|
NvU32 sinkUhbrCaps = hal->getUHBRSupported();
|
|
NvU32 uhbrCaps = (sinkUhbrCaps & gpuUhbrCaps);
|
|
|
|
bool bUHBR10_0Supported = FLD_TEST_DRF(0073_CTRL_CMD_DP, _GET_CAPS_UHBR_SUPPORTED,
|
|
_UHBR10_0, _YES, uhbrCaps);
|
|
bool bUHBR13_5Supported = FLD_TEST_DRF(0073_CTRL_CMD_DP, _GET_CAPS_UHBR_SUPPORTED,
|
|
_UHBR13_5, _YES, uhbrCaps);
|
|
bool bUHBR20_0Supported = FLD_TEST_DRF(0073_CTRL_CMD_DP, _GET_CAPS_UHBR_SUPPORTED,
|
|
_UHBR20_0, _YES, uhbrCaps);
|
|
|
|
// Attempt to configure link rate table mode if supported
|
|
if (hal->isIndexedLinkrateCapable() &&
|
|
main->configureLinkRateTable(hal->getLinkRateTable(), pConnectorLinkRates))
|
|
{
|
|
// Maximal link rate is limited with link rate table
|
|
hal->setIndexedLinkrateEnabled(true);
|
|
}
|
|
else
|
|
{
|
|
// Reset configured link rate table if ever enabled to get RM act right
|
|
if (hal->isIndexedLinkrateEnabled())
|
|
{
|
|
main->configureLinkRateTable(NULL, NULL);
|
|
hal->setIndexedLinkrateEnabled(false);
|
|
}
|
|
pConnectorLinkRates->clear();
|
|
}
|
|
|
|
// Get maximal link rate supported by GPU
|
|
linkRate = main->maxLinkRateSupported();
|
|
|
|
// Insert in order any additional entries regardless of ILR Capability
|
|
|
|
if (linkRate >= dp2LinkRate_1_62Gbps)
|
|
pConnectorLinkRates->insert((NvU16)dp2LinkRate_1_62Gbps);
|
|
|
|
if (this->bSupportUHBR2_50)
|
|
pConnectorLinkRates->insert((NvU16)dp2LinkRate_2_50Gbps);
|
|
|
|
if ((linkRate >= dp2LinkRate_2_70Gbps) || (this->bSupportUHBR2_70))
|
|
pConnectorLinkRates->insert((NvU16)dp2LinkRate_2_70Gbps);
|
|
|
|
if (linkRate >= dp2LinkRate_5_40Gbps)
|
|
pConnectorLinkRates->insert((NvU16)dp2LinkRate_5_40Gbps);
|
|
|
|
if (linkRate >= dp2LinkRate_8_10Gbps)
|
|
pConnectorLinkRates->insert((NvU16)dp2LinkRate_8_10Gbps);
|
|
|
|
if (this->bSupportUHBR5_00)
|
|
pConnectorLinkRates->insert((NvU16)dp2LinkRate_5_00Gbps);
|
|
|
|
if (bUHBR10_0Supported)
|
|
pConnectorLinkRates->insert((NvU16)dp2LinkRate_10_0Gbps);
|
|
|
|
if (bUHBR13_5Supported)
|
|
pConnectorLinkRates->insert((NvU16)dp2LinkRate_13_5Gbps);
|
|
|
|
if (bUHBR20_0Supported)
|
|
pConnectorLinkRates->insert((NvU16)dp2LinkRate_20_0Gbps);
|
|
|
|
return pConnectorLinkRates;
|
|
}
|
|
|
|
/*!
|
|
* @brief Find the max supported link configuration.
|
|
*
|
|
* @return The max supported link configuration.
|
|
*/
|
|
LinkConfiguration ConnectorImpl2x::getMaxLinkConfig()
|
|
{
|
|
DP_ASSERT(hal);
|
|
|
|
LinkRate maxLinkRate;
|
|
unsigned maxLaneCount = 4U;
|
|
|
|
// maxLinkRateSupported checks GPU and DFP caps. UHBR is included.
|
|
LinkRate gpuMaxLinkRate = main->maxLinkRateSupported();
|
|
NvU32 gpuUhbrCaps = main->getUHBRSupported();
|
|
|
|
// sinkMaxLinkRate checks LTTPR and sink caps. UHBR is included.
|
|
LinkRate sinkMaxLinkRate = hal->getMaxLinkRate();
|
|
NvU32 sinkUhbrCaps = hal->getUHBRSupported();
|
|
NvU32 uhbrCaps = gpuUhbrCaps & sinkUhbrCaps;
|
|
|
|
|
|
// For eDP, user can override maxLinkRate thru regkey, with valid value.
|
|
if ((main->isEDP()) &&
|
|
maxLinkRateFromRegkey && (IS_VALID_DP2_X_LINKBW(maxLinkRateFromRegkey)))
|
|
{
|
|
sinkMaxLinkRate = maxLinkRateFromRegkey;
|
|
}
|
|
|
|
maxLinkRate = sinkMaxLinkRate ? DP_MIN(sinkMaxLinkRate, gpuMaxLinkRate) : gpuMaxLinkRate;
|
|
|
|
if (uhbrCaps)
|
|
{
|
|
if (FLD_TEST_DRF(0073_CTRL_CMD_DP, _GET_CAPS_UHBR_SUPPORTED, _UHBR20_0, _YES, uhbrCaps))
|
|
{
|
|
maxLinkRate = dp2LinkRate_20_0Gbps;
|
|
}
|
|
else if (FLD_TEST_DRF(0073_CTRL_CMD_DP, _GET_CAPS_UHBR_SUPPORTED, _UHBR13_5, _YES, uhbrCaps))
|
|
{
|
|
maxLinkRate = dp2LinkRate_13_5Gbps;
|
|
}
|
|
else if (FLD_TEST_DRF(0073_CTRL_CMD_DP, _GET_CAPS_UHBR_SUPPORTED, _UHBR10_0, _YES, uhbrCaps))
|
|
{
|
|
maxLinkRate = dp2LinkRate_10_0Gbps;
|
|
}
|
|
}
|
|
|
|
if (hal->getMaxLaneCount())
|
|
{
|
|
maxLaneCount = DP_MIN(hal->getMaxLaneCountSupportedAtLinkRate(maxLinkRate),
|
|
hal->getMaxLaneCount());
|
|
}
|
|
|
|
return LinkConfiguration (&this->linkPolicy,
|
|
maxLaneCount, maxLinkRate,
|
|
this->hal->getEnhancedFraming(),
|
|
linkUseMultistream(),
|
|
false, /* disablePostLTRequest */
|
|
this->bFECEnable,
|
|
false, /* bDisableLTTPR */
|
|
this->getDownspreadDisabled());
|
|
}
|
|
|
|
/*!
|
|
* @brief Check if the mode is possible for the current MST configuration.
|
|
* Note that 128b/132b is always MST with FEC enabled.
|
|
*
|
|
* @return The mode info in the group is supported by current MST configuration
|
|
*/
|
|
bool ConnectorImpl2x::checkIsModePossibleMST(GroupImpl * targetGroup)
|
|
{
|
|
if (!willLinkSupportMode(activeLinkConfig, targetGroup->lastModesetInfo, targetGroup->headIndex,
|
|
&targetGroup->timeslot.watermarks))
|
|
{
|
|
DP_ASSERT(!"DP2xCONN> DisplayDriver bug! This mode is not possible at "
|
|
"any link configuration. It should have been rejected at mode "
|
|
"filtering time!");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ConnectorImpl2x::compoundQueryAttachMSTGeneric(Group * target,
|
|
const DpModesetParams &modesetParams, // Modeset info
|
|
CompoundQueryAttachMSTInfo * localInfo,
|
|
DscParams *pDscParams, // DSC parameters
|
|
DP_IMP_ERROR *pErrorCode)
|
|
{
|
|
// I. Evaluate use of local link bandwidth
|
|
|
|
// Calculate the PBN required
|
|
unsigned base_pbn, slots, slots_pbn;
|
|
localInfo->lc.pbnRequired(localInfo->localModesetInfo, base_pbn, slots, slots_pbn);
|
|
|
|
// Accumulate the amount of PBN rounded up to nearest timeslot
|
|
compoundQueryLocalLinkPBN += slots_pbn;
|
|
if (compoundQueryLocalLinkPBN > localInfo->lc.pbnTotal())
|
|
{
|
|
compoundQueryResult = false;
|
|
SET_DP_IMP_ERROR(pErrorCode, DP_IMP_ERROR_INSUFFICIENT_BANDWIDTH)
|
|
return false;
|
|
}
|
|
|
|
// Verify the min blanking, etc. No headIndex (default 0) for mode enumeration.
|
|
if (!willLinkSupportMode(localInfo->lc, localInfo->localModesetInfo))
|
|
{
|
|
compoundQueryResult = false;
|
|
SET_DP_IMP_ERROR(pErrorCode, DP_IMP_ERROR_WATERMARK_BLANKING)
|
|
return false;
|
|
}
|
|
|
|
if (!hal->isDp2xChannelCodingCapable())
|
|
{
|
|
for(Device * d = target->enumDevices(0); d; d = target->enumDevices(d))
|
|
{
|
|
DeviceImpl * i = (DeviceImpl *)d;
|
|
|
|
// Allocate bandwidth for the entire path to the root
|
|
// NOTE: Above we're already handle the local link
|
|
DeviceImpl * tail = i;
|
|
while (tail && tail->getParent())
|
|
{
|
|
// Have we already accounted for this stream?
|
|
if (!(tail->bandwidth.compound_query_state.bandwidthAllocatedForIndex & (1 << compoundQueryCount)))
|
|
{
|
|
tail->bandwidth.compound_query_state.bandwidthAllocatedForIndex |= (1 << compoundQueryCount);
|
|
|
|
LinkConfiguration * linkConfig = tail->inferLeafLink(NULL);
|
|
tail->bandwidth.compound_query_state.timeslots_used_by_query += linkConfig->slotsForPBN(base_pbn);
|
|
|
|
if (tail->bandwidth.compound_query_state.timeslots_used_by_query >
|
|
tail->bandwidth.compound_query_state.totalTimeSlots)
|
|
{
|
|
compoundQueryResult = false;
|
|
SET_DP_IMP_ERROR(pErrorCode, DP_IMP_ERROR_INSUFFICIENT_BANDWIDTH)
|
|
}
|
|
}
|
|
tail = (DeviceImpl*)tail->getParent();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(Device * d = target->enumDevices(0); d; d = target->enumDevices(d))
|
|
{
|
|
DeviceImpl * tail = (DeviceImpl *)d;
|
|
if (!(tail->bandwidth.compound_query_state.bandwidthAllocatedForIndex & (1 << compoundQueryCount)))
|
|
{
|
|
tail->bandwidth.compound_query_state.bandwidthAllocatedForIndex |= (1 << compoundQueryCount);
|
|
tail->inferPathConstraints();
|
|
|
|
if (slots_pbn > DP_MIN(tail->bandwidth.enum_path.total, tail->bandwidth.enum_path.dfpLinkAvailable))
|
|
{
|
|
compoundQueryResult = false;
|
|
SET_DP_IMP_ERROR(pErrorCode, DP_IMP_ERROR_INSUFFICIENT_BANDWIDTH)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return compoundQueryResult;
|
|
}
|
|
|
|
/*!
|
|
* @brief Notify library before/after modeset (update)
|
|
* Here is what NAB essentially does:
|
|
* 0. Makes sure TMDS is not attached
|
|
* 1. Trains link to optimized link config ("optimized" depends on DP1.1, DP1.2)
|
|
* 2. Performs quick watermark check for IMP. If IMP is not possible, forces link, zombies devices
|
|
* 3. if anything of above fails, marks devices in given group as zombies
|
|
*
|
|
* Update to ConnectorImpl2x version:
|
|
* 1. Drop all single head multi stream (SST/MST).
|
|
* 2. Drop 2H1OR
|
|
*
|
|
* @return true - NAB passed
|
|
* false - NAB failed due to invalid params or link training failure
|
|
* Link configs are forced in case of link training failure
|
|
*/
|
|
bool ConnectorImpl2x::notifyAttachBegin(Group *target, const DpModesetParams &modesetParams)
|
|
{
|
|
unsigned twoChannelAudioHz = modesetParams.modesetInfo.twoChannelAudioHz;
|
|
unsigned eightChannelAudioHz = modesetParams.modesetInfo.eightChannelAudioHz;
|
|
NvU64 pixelClockHz = modesetParams.modesetInfo.pixelClockHz;
|
|
unsigned rasterWidth = modesetParams.modesetInfo.rasterWidth;
|
|
unsigned rasterHeight = modesetParams.modesetInfo.rasterHeight;
|
|
unsigned rasterBlankStartX = modesetParams.modesetInfo.rasterBlankStartX;
|
|
unsigned rasterBlankEndX = modesetParams.modesetInfo.rasterBlankEndX;
|
|
unsigned depth = modesetParams.modesetInfo.depth;
|
|
bool bLinkTrainingStatus = true;
|
|
bool bEnableFEC = true;
|
|
bool bEnableDsc = modesetParams.modesetInfo.bEnableDsc;
|
|
bool bEnablePassThroughForPCON = modesetParams.modesetInfo.bEnablePassThroughForPCON;
|
|
Device *newDev = target->enumDevices(0);
|
|
DeviceImpl *dev = (DeviceImpl *)newDev;
|
|
|
|
if(preferredLinkConfig.isValid())
|
|
{
|
|
bEnableFEC = preferredLinkConfig.bEnableFEC;
|
|
}
|
|
else
|
|
{
|
|
DeviceImpl * nativeDev = findDeviceInList(Address());
|
|
if (main->isEDP() && nativeDev)
|
|
{
|
|
// eDP can support DSC with and without FEC
|
|
bEnableFEC = bEnableDsc && nativeDev->getFECSupport();
|
|
}
|
|
else
|
|
{
|
|
bEnableFEC = bEnableDsc;
|
|
}
|
|
}
|
|
|
|
DP_PRINTF(DP_NOTICE, "DP2xCONN> Notify Attach Begin (Head %d, pclk %d (KHz) raster %d x %d %d bpp)",
|
|
modesetParams.headIndex, (pixelClockHz/1000), rasterWidth, rasterHeight, depth);
|
|
NV_DPTRACE_INFO(NOTIFY_ATTACH_BEGIN, modesetParams.headIndex, pixelClockHz, rasterWidth, rasterHeight,
|
|
depth, bEnableDsc, bEnableFEC);
|
|
|
|
if (!depth || !pixelClockHz)
|
|
{
|
|
DP_ASSERT(!"DP2xCONN> Params with zero value passed to query!");
|
|
return false;
|
|
}
|
|
|
|
for (Device * dev = target->enumDevices(0); dev; dev = target->enumDevices(dev))
|
|
{
|
|
Address::StringBuffer buffer;
|
|
DP_USED(buffer);
|
|
DP_PRINTF(DP_NOTICE, "DP2xCONN> | %s (%s) |", dev->getTopologyAddress().toString(buffer),
|
|
dev->isVideoSink() ? "VIDEO" : "BRANCH");
|
|
}
|
|
|
|
if (firmwareGroup && ((GroupImpl *)firmwareGroup)->headInFirmware)
|
|
{
|
|
DP_ASSERT(bIsUefiSystem || (!"DP2xCONN> Firmware still active on head. De-activating"));
|
|
}
|
|
|
|
GroupImpl* targetImpl = (GroupImpl*)target;
|
|
|
|
if (bEnableDsc)
|
|
{
|
|
DP_PRINTF(DP_NOTICE, "DP2xCONN> DSC Mode = %s", (modesetParams.modesetInfo.mode == DSC_SINGLE) ? "SINGLE" : "DUAL");
|
|
targetImpl->dscModeRequest = modesetParams.modesetInfo.mode;
|
|
}
|
|
|
|
DP_ASSERT(!(targetImpl->isHeadAttached() && targetImpl->bIsHeadShutdownNeeded) &&
|
|
"DP2xCONN> Head should have been shut down but it is still active!");
|
|
|
|
targetImpl->headInFirmware = false;
|
|
if (firmwareGroup)
|
|
{
|
|
((GroupImpl *)firmwareGroup)->headInFirmware = false;
|
|
}
|
|
|
|
if (firmwareGroup && activeGroups.contains((GroupImpl*)firmwareGroup))
|
|
{
|
|
if (((GroupImpl *)firmwareGroup)->isHeadAttached())
|
|
{
|
|
targetImpl->setHeadAttached(true);
|
|
}
|
|
activeGroups.remove((GroupImpl*)firmwareGroup);
|
|
inactiveGroups.insertBack((GroupImpl*)firmwareGroup);
|
|
}
|
|
|
|
if (this->linkGuessed)
|
|
{
|
|
DP_ASSERT(!"Link was not assessed previously. Probable reason: system was not in driver mode. Assessing now");
|
|
this->assessLink();
|
|
}
|
|
|
|
DP_ASSERT(this->isLinkQuiesced == 0 && "DP2xCONN> According to bracketting calls TMDS/alternate DP still active!");
|
|
|
|
// Transfer the group to active list
|
|
inactiveGroups.remove(targetImpl);
|
|
activeGroups.insertBack(targetImpl);
|
|
|
|
if (modesetParams.colorFormat == dpColorFormat_YCbCr422 &&
|
|
dev && dev->dscCaps.dscDecoderColorFormatCaps.bYCbCrNative422)
|
|
{
|
|
targetImpl->lastModesetInfo = ModesetInfo(twoChannelAudioHz, eightChannelAudioHz,
|
|
pixelClockHz, rasterWidth, rasterHeight,
|
|
(rasterBlankStartX - rasterBlankEndX), modesetParams.modesetInfo.surfaceHeight,
|
|
depth, rasterBlankStartX, rasterBlankEndX, bEnableDsc, modesetParams.modesetInfo.mode,
|
|
false, dpColorFormat_YCbCr422_Native);
|
|
}
|
|
else
|
|
{
|
|
targetImpl->lastModesetInfo = ModesetInfo(twoChannelAudioHz, eightChannelAudioHz,
|
|
pixelClockHz, rasterWidth, rasterHeight,
|
|
(rasterBlankStartX - rasterBlankEndX), modesetParams.modesetInfo.surfaceHeight,
|
|
depth, rasterBlankStartX, rasterBlankEndX, bEnableDsc, modesetParams.modesetInfo.mode,
|
|
false, modesetParams.colorFormat);
|
|
}
|
|
|
|
targetImpl->headIndex = modesetParams.headIndex;
|
|
targetImpl->streamIndex = main->headToStream(modesetParams.headIndex, (messageManager != NULL), targetImpl->singleHeadMultiStreamID);
|
|
targetImpl->colorFormat = modesetParams.colorFormat;
|
|
|
|
DP_ASSERT(!this->isLinkQuiesced && "DP2xCONN> TMDS is attached, NABegin is impossible!");
|
|
|
|
//
|
|
// Update the FEC enabled flag according to the mode requested.
|
|
//
|
|
// In MST config, if one panel needs DSC/FEC and the other one does not,
|
|
// we still need to keep FEC enabled on the connector since at least one
|
|
// stream needs it.
|
|
//
|
|
this->bFECEnable |= bEnableFEC;
|
|
|
|
highestAssessedLC.enableFEC(this->bFECEnable);
|
|
|
|
if (main->isEDP())
|
|
{
|
|
main->configurePowerState(true);
|
|
if (bOuiCached)
|
|
{
|
|
hal->setOuiSource(cachedSourceOUI, &cachedSourceModelName[0],
|
|
6 /* string length of ieeeOuiDevId */,
|
|
cachedSourceChipRevision);
|
|
}
|
|
else
|
|
{
|
|
DP_ASSERT(!"DP2xCONN> eDP Source OUI is not cached!");
|
|
}
|
|
}
|
|
|
|
// if failed, we're guaranteed that assessed link rate didn't meet the mode requirements
|
|
// isZombie() will catch this
|
|
bLinkTrainingStatus = trainLinkOptimized(getMaxLinkConfig());
|
|
|
|
// if LT is successful, see if panel supports DSC and if so, set DSC enabled/disabled
|
|
// according to the mode requested.
|
|
if(bLinkTrainingStatus)
|
|
{
|
|
for (Device * dev = target->enumDevices(0); dev; dev = target->enumDevices(dev))
|
|
{
|
|
if (bPConConnected)
|
|
{
|
|
if (!(((DeviceImpl *)dev)->setDscEnableDPToHDMIPCON(bEnableDsc, bEnablePassThroughForPCON)))
|
|
{
|
|
DP_ASSERT(!"DP2xCONN> Failed to configure DSC on DP to HDMI PCON!");
|
|
}
|
|
}
|
|
else if(!setDeviceDscState(dev, bEnableDsc))
|
|
{
|
|
DP_ASSERT(!"DP2xCONN> Failed to configure DSC on Sink!");
|
|
}
|
|
}
|
|
}
|
|
|
|
beforeAddStream(targetImpl);
|
|
|
|
if (!linkUseMultistream() || main->supportMSAOverMST())
|
|
{
|
|
bool enableInbandStereoSignaling = false;
|
|
|
|
DP_ASSERT(activeGroups.isEmpty() == false);
|
|
|
|
if (main->isInbandStereoSignalingSupported())
|
|
{
|
|
enableInbandStereoSignaling = true;
|
|
}
|
|
|
|
//
|
|
// Bug 200362535
|
|
// setDpStereoMSAParameters does not cache the msa params. It will immediately
|
|
// apply just the stereo specific parameters. This is required because we
|
|
// can toggle the msa params using nvidia control panel and in that scenario
|
|
// we do not get supervisor interrupts. Since SV interrupts do not occur the
|
|
// msa parameters do not get applied. So to avoid having to reboot to apply the
|
|
// stereo msa params setDpStereoMSAParameters is called.
|
|
//
|
|
// setDpMSAParameters will contain all msa params, including stereo cached.
|
|
// These will be applied during supervisor interrupt. So if we will get
|
|
// SV interrupts later the same stereo settings will be applied twice.
|
|
// first by setDpStereoMSAParameters and later by setDpMSAParameters.
|
|
//
|
|
main->setDpStereoMSAParameters(!enableInbandStereoSignaling, modesetParams.msaparams);
|
|
main->setDpMSAParameters(!enableInbandStereoSignaling, modesetParams.msaparams);
|
|
}
|
|
|
|
NV_DPTRACE_INFO(NOTIFY_ATTACH_BEGIN_STATUS, bLinkTrainingStatus);
|
|
|
|
// Move the group to intransistion since we are at the end of notifyAttachBegin
|
|
intransitionGroups.insertFront(targetImpl);
|
|
if (dev && dev->bApplyPclkWarBug4949066 == true)
|
|
{
|
|
EvoInterface *provider = ((EvoMainLink *)main)->getProvider();
|
|
NV0073_CTRL_CMD_DP_SET_PROP_FORCE_PCLK_FACTOR_PARAMS params = {0};
|
|
params.subDeviceInstance = provider->getSubdeviceIndex();
|
|
params.head = modesetParams.headIndex;
|
|
params.bEnable = NV_TRUE;
|
|
|
|
NvU32 ret = provider->rmControl0073(NV0073_CTRL_CMD_DP_SET_PROP_FORCE_PCLK_FACTOR, ¶ms, sizeof(params));
|
|
if (ret != NVOS_STATUS_SUCCESS)
|
|
{
|
|
DP_PRINTF(DP_ERROR, "Failed to enable the WAR for bug4949066!");
|
|
}
|
|
}
|
|
bFromResumeToNAB = false;
|
|
return bLinkTrainingStatus;
|
|
}
|
|
|
|
/*!
|
|
* @brief End of notify attach.
|
|
*
|
|
* modesetCancelled True, when DD respected NAB failure and cancelled modeset.
|
|
* False, when NAB succeeded, or DD didn't honor NAB failure
|
|
*
|
|
* Here is what NAE supposed to do:
|
|
* 1. modesetCancelled == TRUE, NAB failed:
|
|
* unzombie all devices and set linkForced to false; We have Status Quo for next modeset
|
|
* 2. modesetCancelled == False, NAB failed:
|
|
* If NAB failed, linkForces is TRUE. NAE goes finds zombied devices and notifies DD about them.
|
|
* 3. modesetCancelled == False, NAB succeeded:
|
|
* NAE is no-op. (but we have some special sanity code)
|
|
*/
|
|
void ConnectorImpl2x::notifyAttachEnd(bool modesetCancelled)
|
|
{
|
|
if (!activeLinkConfig.bIs128b132bChannelCoding)
|
|
return ConnectorImpl::notifyAttachEnd(modesetCancelled);
|
|
|
|
GroupImpl* currentModesetDeviceGroup = NULL;
|
|
DP_PRINTF(DP_NOTICE, "DP2xCONN> Notify Attach End for 128b/132b channel coding");
|
|
NV_DPTRACE_INFO(NOTIFY_ATTACH_END);
|
|
|
|
bFromResumeToNAB = false;
|
|
|
|
if (intransitionGroups.isEmpty())
|
|
{
|
|
DP_ASSERT(!"DP2xCONN> INVALID STATE: Modeset Group is NULL");
|
|
return;
|
|
}
|
|
|
|
currentModesetDeviceGroup = intransitionGroups.pop();
|
|
|
|
if (modesetCancelled)
|
|
{
|
|
currentModesetDeviceGroup->setHeadAttached(false);
|
|
}
|
|
|
|
// set dscModeActive to what was requested in NAB and clear dscModeRequest
|
|
currentModesetDeviceGroup->dscModeActive = currentModesetDeviceGroup->dscModeRequest;
|
|
currentModesetDeviceGroup->dscModeRequest = DSC_MODE_NONE;
|
|
|
|
currentModesetDeviceGroup->setHeadAttached(true);
|
|
RmDfpCache dfpCache = {0};
|
|
dfpCache.updMask = 0;
|
|
if (currentModesetDeviceGroup->isHeadAttached())
|
|
{
|
|
for (DeviceImpl * dev = (DeviceImpl *)currentModesetDeviceGroup->enumDevices(0);
|
|
dev; dev = (DeviceImpl *)currentModesetDeviceGroup->enumDevices(dev))
|
|
{
|
|
dfpCache.bcaps = *dev->BCAPS;
|
|
for (unsigned i=0; i<HDCP_KSV_SIZE; i++)
|
|
dfpCache.bksv[i] = dev->BKSV[i];
|
|
|
|
dfpCache.updMask |= (1 << NV0073_CTRL_DFP_UPDATE_DYNAMIC_DFP_CACHE_MASK_BCAPS);
|
|
dfpCache.updMask |= (1 << NV0073_CTRL_DFP_UPDATE_DYNAMIC_DFP_CACHE_MASK_BKSV);
|
|
main->rmUpdateDynamicDfpCache(dev->activeGroup->headIndex, &dfpCache, True);
|
|
|
|
// Remove this while enabling HDCP for MSC
|
|
break;
|
|
}
|
|
}
|
|
|
|
afterAddStream(currentModesetDeviceGroup);
|
|
|
|
//
|
|
// Turn on the Authentication/Encryption back if previous is on.
|
|
// For DP1.1, let the upstream to turn it back.
|
|
// For DP1.2, we should turn the modeset back if it was on.
|
|
// The authentication will be called off during the modeset.
|
|
//
|
|
HDCPState hdcpState = {0};
|
|
main->configureHDCPGetHDCPState(hdcpState);
|
|
if ((!hdcpState.HDCP_State_Authenticated) && (isHDCPAuthOn == true)
|
|
&& (currentModesetDeviceGroup->hdcpEnabled))
|
|
{
|
|
if (!this->linkUseMultistream())
|
|
{
|
|
currentModesetDeviceGroup->hdcpEnabled = isHDCPAuthOn = false;
|
|
}
|
|
}
|
|
|
|
fireEvents();
|
|
}
|
|
|
|
/*!
|
|
* @brief Calculate and allocate time slot required for the mode.
|
|
* Calling ConnectorImpl version which handles bandwidth calculation.
|
|
* For 128b/132b channel coding and SSTopology, need to assign all 64 slots
|
|
* to the stream and make sure starting from slot 0.
|
|
*
|
|
* @return If the allocation is passing.
|
|
*/
|
|
bool ConnectorImpl2x::allocateTimeslice(GroupImpl * targetGroup)
|
|
{
|
|
bool result = true;
|
|
if (activeLinkConfig.bIs128b132bChannelCoding)
|
|
{
|
|
firstFreeSlot = 0;
|
|
}
|
|
result = ConnectorImpl::allocateTimeslice(targetGroup);
|
|
|
|
// If result is false means the mode is not supported.
|
|
if (!result)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!activeLinkConfig.bIs128b132bChannelCoding || linkUseMultistream()) {
|
|
goto done;
|
|
}
|
|
|
|
// 128b/132b SSTology takes all (64) slots.
|
|
targetGroup->timeslot.count = maximumSlots;
|
|
targetGroup->timeslot.begin = 0U;
|
|
|
|
// no freeSlots.
|
|
freeSlots = 0U;
|
|
|
|
done:
|
|
targetGroup->setTimeslotAllocated(true);
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
* @brief Setup GPU before stream is added to connector.
|
|
*
|
|
* @param[in] group The stream needs to be attached.
|
|
* @param[in] test If it's called to test HW.
|
|
* @param[in] forFlushMode If it's called for flush mode.
|
|
*
|
|
* @return If the stream is forced to add.
|
|
*/
|
|
bool ConnectorImpl2x::beforeAddStream(GroupImpl * group, bool test, bool forFlushMode)
|
|
{
|
|
bool bForced = false;
|
|
|
|
if (group->isTimeslotAllocated())
|
|
return bForced;
|
|
|
|
if (!activeLinkConfig.bIs128b132bChannelCoding)
|
|
{
|
|
// Handle NVD5 8b/10b modeset.
|
|
NvBool bIsModePossible = false;
|
|
Watermark water = {0};
|
|
|
|
if (linkUseMultistream())
|
|
{
|
|
// MST - reuse DP1x code path.
|
|
return ConnectorImpl::beforeAddStreamMST(group, test, forFlushMode);
|
|
}
|
|
// SST call into DP2x code path.
|
|
bIsModePossible = willLinkSupportMode(activeLinkConfig, group->lastModesetInfo,
|
|
group->headIndex, &water);
|
|
if (bIsModePossible || test)
|
|
{
|
|
main->configureSingleStream(group->headIndex,
|
|
water.hBlankSym,
|
|
water.vBlankSym,
|
|
activeLinkConfig.enhancedFraming,
|
|
water.tuSize,
|
|
water.waterMark,
|
|
group->colorFormat,
|
|
DP_SINGLE_HEAD_MULTI_STREAM_PIPELINE_ID_PRIMARY,
|
|
DP_SINGLE_HEAD_MULTI_STREAM_MODE_NONE,
|
|
false /*bEnableAudioOverRightPanel*/,
|
|
false /*bEnable2Head1Or*/);
|
|
}
|
|
if (!bIsModePossible)
|
|
{
|
|
if (test)
|
|
{
|
|
bForced = true;
|
|
DP_PRINTF(DP_ERROR, "DP2x-TS> Unable to allocate stream. Setting RG_DIV mode");
|
|
}
|
|
else
|
|
DP_ASSERT(0);
|
|
}
|
|
return bForced;
|
|
}
|
|
|
|
// 128b/132b only has MST mode.
|
|
if (allocateTimeslice(group))
|
|
{
|
|
flushTimeslotsToHardware();
|
|
group->setTimeslotAllocated(true);
|
|
|
|
if (forFlushMode || addStreamMSTIntransitionGroups.isEmpty())
|
|
{
|
|
hal->payloadTableClearACT();
|
|
hal->payloadAllocate(group->streamIndex, group->timeslot.begin, group->timeslot.count);
|
|
main->triggerACT();
|
|
}
|
|
else
|
|
{
|
|
group->bDeferredPayloadAlloc = true;
|
|
}
|
|
addStreamMSTIntransitionGroups.insertFront(group);
|
|
}
|
|
else
|
|
{
|
|
if (!test)
|
|
{
|
|
DP_PRINTF(DP_ERROR, "DP2x-TS> Unable to allocate stream. Should call mainLink->configureStream to trigger RG_DIV mode");
|
|
main->configureMultiStream(group->headIndex,
|
|
group->timeslot.watermarks.hBlankSym, group->timeslot.watermarks.vBlankSym,
|
|
1, 0, 0, 0, group->colorFormat, group->singleHeadMultiStreamID,
|
|
group->singleHeadMultiStreamMode, bAudioOverRightPanel);
|
|
}
|
|
else
|
|
{
|
|
flushTimeslotsToHardware();
|
|
|
|
if (forFlushMode)
|
|
{
|
|
hal->payloadTableClearACT();
|
|
hal->payloadAllocate(group->streamIndex, group->timeslot.begin, group->timeslot.count);
|
|
main->triggerACT();
|
|
}
|
|
|
|
DP_PRINTF(DP_ERROR, "DP2x-TS> Unable to allocate stream. Setting RG_DIV mode");
|
|
bForced = true;
|
|
}
|
|
}
|
|
|
|
return bForced;
|
|
}
|
|
|
|
/*!
|
|
* @brief Setup sink and SW state after stream is added to connector.
|
|
*
|
|
* @param[in] group The stream needs to be attached.
|
|
*
|
|
*/
|
|
void ConnectorImpl2x::afterAddStream(GroupImpl * group)
|
|
{
|
|
if (!activeLinkConfig.bIs128b132bChannelCoding)
|
|
return ConnectorImpl::afterAddStream(group);
|
|
|
|
// For 128b/132b always use MST protocol
|
|
DP_ASSERT(group->isTimeslotAllocated());
|
|
|
|
// Skip this as there is no timeslot allocation
|
|
if (!group->timeslot.count)
|
|
return;
|
|
|
|
if (group->bDeferredPayloadAlloc)
|
|
{
|
|
DP_ASSERT(addStreamMSTIntransitionGroups.contains(group));
|
|
hal->payloadTableClearACT();
|
|
hal->payloadAllocate(group->streamIndex, group->timeslot.begin, group->timeslot.count);
|
|
main->triggerACT();
|
|
}
|
|
group->bDeferredPayloadAlloc = false;
|
|
|
|
if (addStreamMSTIntransitionGroups.contains(group))
|
|
{
|
|
addStreamMSTIntransitionGroups.remove(group);
|
|
}
|
|
|
|
if (!hal->payloadWaitForACTReceived())
|
|
{
|
|
DP_ASSERT(!"DP2x-TS> ACT has not been received.Triggering ACT once more");
|
|
|
|
//
|
|
// Bug 1334070: During modeset for cloned displays on certain GPU family,
|
|
// ACT triggered during SOR attach is not being received due to timing issues.
|
|
// Also DP1.2 spec mentions that there is no harm in sending the ACT
|
|
// again if there is no change in payload table. Hence triggering ACT once more here
|
|
//
|
|
main->triggerACT();
|
|
if (!hal->payloadWaitForACTReceived())
|
|
{
|
|
DP_PRINTF(DP_ERROR, "DP2x-TS> Downstream device did not receive ACT during stream re-add.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (Device * d = group->enumDevices(0); d; d = group->enumDevices(d))
|
|
{
|
|
group->update((DeviceImpl *)d, true);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* @brief Setup GPU before stream is deleted from the connector.
|
|
*
|
|
* @param[in] group The stream needs to be attached.
|
|
* @param[in] forFlushMode If it's called for flush mode.
|
|
*
|
|
*/
|
|
void ConnectorImpl2x::beforeDeleteStream(GroupImpl * group, bool forFlushMode)
|
|
{
|
|
if (!group->isTimeslotAllocated() && !group->headInFirmware)
|
|
return;
|
|
|
|
if (!activeLinkConfig.bIs128b132bChannelCoding)
|
|
return ConnectorImpl::beforeDeleteStream(group, forFlushMode);
|
|
|
|
// check if this is a firmware group
|
|
if (group && group->isHeadAttached() && group->headInFirmware)
|
|
{
|
|
// Firmware group can be assumed to be taking up all 63 slots.
|
|
group->timeslot.begin = 1U;
|
|
group->timeslot.count = 63U;
|
|
this->freeSlots = 0U;
|
|
|
|
// Clear the payload table
|
|
hal->payloadTableClearACT();
|
|
if (!hal->payloadAllocate(0, 0, 63))
|
|
{
|
|
DP_PRINTF(DP_ERROR, "DP2xCONN> Payload table could not be cleared");
|
|
}
|
|
// Clear gpu timeslots.
|
|
freeTimeslice(group);
|
|
flushTimeslotsToHardware();
|
|
|
|
group->setTimeslotAllocated(false);
|
|
|
|
main->triggerACT();
|
|
group->bWaitForDeAllocACT = false;
|
|
|
|
return;
|
|
}
|
|
|
|
if (group && group->isHeadAttached() && group->isTimeslotAllocated())
|
|
{
|
|
// Detach all the panels from payload
|
|
for (Device * d = group->enumDevices(0); d; d = group->enumDevices(d))
|
|
{
|
|
group->update(d, false);
|
|
}
|
|
|
|
freeTimeslice(group);
|
|
flushTimeslotsToHardware();
|
|
group->setTimeslotAllocated(false);
|
|
group->bWaitForDeAllocACT = true;
|
|
|
|
// Delete the stream
|
|
hal->payloadTableClearACT();
|
|
hal->payloadAllocate(group->streamIndex, group->timeslot.begin, 0);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* @brief Setup GPU/Sink after stream is deleted from the connector.
|
|
*
|
|
* @param[in] group The stream needs to be attached.
|
|
*
|
|
*/
|
|
void ConnectorImpl2x::afterDeleteStream(GroupImpl * group)
|
|
{
|
|
if (!activeLinkConfig.bIs128b132bChannelCoding)
|
|
return ConnectorImpl::afterDeleteStream(group);
|
|
|
|
DP_ASSERT(!group->isTimeslotAllocated());
|
|
main->triggerACT();
|
|
if (group->isHeadAttached() && group->bWaitForDeAllocACT)
|
|
{
|
|
if (!hal->payloadWaitForACTReceived())
|
|
{
|
|
DP_ASSERT(!"DP2xCONN> Delete stream failed. Device did not acknowledge stream deletion ACT!");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* @brief Wrapper for link train function. Calls ConnectorImpl::train and update
|
|
* maximumSlots if 128b/132b channel coding is used.
|
|
*
|
|
* @param[in] lConfig Requested link configuration.
|
|
* @param[in] force If the request is forced (fake LT).
|
|
* @param[in] trainType NormalLinkTrain, NoLinkTrain or FastLinkTrain
|
|
*
|
|
* @return If link training is done and result is the same as requested.
|
|
*/
|
|
bool ConnectorImpl2x::train(const LinkConfiguration &lConfig, bool force, LinkTrainingType trainType)
|
|
{
|
|
bool trainResult = ConnectorImpl::train(lConfig, force, trainType);
|
|
if (activeLinkConfig.lanes != 0 &&
|
|
activeLinkConfig.bIs128b132bChannelCoding)
|
|
{
|
|
// For 128b/132b, all 64 timeslots are available to use.
|
|
maximumSlots = 64;
|
|
freeSlots = maximumSlots;
|
|
firstFreeSlot = 0;
|
|
}
|
|
|
|
// Invalidate the UHBR if the connector is a USB-C to DP/USB-C.
|
|
if (!trainResult && main->isConnectorUSBTypeC() &&
|
|
lConfig.bIs128b132bChannelCoding && lConfig.peakRate > dp2LinkRate_10_0Gbps)
|
|
{
|
|
hal->overrideCableIdCap(lConfig.peakRate, false);
|
|
}
|
|
return trainResult;
|
|
}
|
|
|
|
/*!
|
|
* @brief Wrapper for notifyDetachBegin.
|
|
* Setup HDCP before calls ConnectorImpl::notifyDetachBegin for 128b/132b SSTopology
|
|
*
|
|
* @param[in] target The stream to be attached.
|
|
*/
|
|
void ConnectorImpl2x::notifyDetachBegin(Group *target)
|
|
{
|
|
if (!target)
|
|
target = firmwareGroup;
|
|
|
|
Device *newDev = target->enumDevices(0);
|
|
DeviceImpl *dev = (DeviceImpl *)newDev;
|
|
GroupImpl *group = (GroupImpl*)target;
|
|
|
|
if (dev != NULL && dev->bApplyPclkWarBug4949066 == true)
|
|
{
|
|
EvoInterface *provider = ((EvoMainLink *)main)->getProvider();
|
|
NV0073_CTRL_CMD_DP_SET_PROP_FORCE_PCLK_FACTOR_PARAMS params = {0};
|
|
params.subDeviceInstance = provider->getSubdeviceIndex();
|
|
params.head = group->headIndex;
|
|
params.bEnable = NV_FALSE;
|
|
|
|
NvU32 ret = provider->rmControl0073(NV0073_CTRL_CMD_DP_SET_PROP_FORCE_PCLK_FACTOR, ¶ms, sizeof(params));
|
|
if (ret != NVOS_STATUS_SUCCESS)
|
|
{
|
|
DP_PRINTF(DP_ERROR, "Failed to Disable the WAR for bug4949066!");
|
|
}
|
|
}
|
|
return ConnectorImpl::notifyDetachBegin(target);
|
|
}
|
|
|
|
void ConnectorImpl2x::notifyDetachEnd(bool bKeepOdAlive)
|
|
{
|
|
if (!activeLinkConfig.bIs128b132bChannelCoding)
|
|
return ConnectorImpl::notifyDetachEnd(bKeepOdAlive);
|
|
|
|
GroupImpl* currentModesetDeviceGroup = NULL;
|
|
DP_PRINTF(DP_NOTICE, "DP2xCONN> Notify detach end");
|
|
NV_DPTRACE_INFO(NOTIFY_DETACH_END);
|
|
|
|
if (intransitionGroups.isEmpty())
|
|
{
|
|
DP_ASSERT(!"DP2xCONN> INVALID STATE: Modeset Group is NULL");
|
|
return;
|
|
}
|
|
|
|
currentModesetDeviceGroup = intransitionGroups.pop();
|
|
|
|
afterDeleteStream(currentModesetDeviceGroup);
|
|
|
|
if (!linkUseMultistream())
|
|
{
|
|
Device * d = 0;
|
|
for (d = currentModesetDeviceGroup->enumDevices(0);
|
|
currentModesetDeviceGroup->enumDevices(d) != 0;
|
|
d = currentModesetDeviceGroup->enumDevices(d))
|
|
{
|
|
// only one device in the group
|
|
DP_ASSERT(d && (((DeviceImpl*)d)->activeGroup == currentModesetDeviceGroup));
|
|
}
|
|
}
|
|
|
|
// nullify last modeset info
|
|
dpMemZero(¤tModesetDeviceGroup->lastModesetInfo, sizeof(ModesetInfo));
|
|
currentModesetDeviceGroup->setHeadAttached(false);
|
|
currentModesetDeviceGroup->headInFirmware = false;
|
|
currentModesetDeviceGroup->dscModeActive = DSC_MODE_NONE;
|
|
|
|
// Mark head as disconnected
|
|
bNoLtDoneAfterHeadDetach = true;
|
|
|
|
//
|
|
// Update the last modeset HDCP status here. Hdcp got disabled after modeset
|
|
// thus hdcpPreviousStatus would be false to SST after device inserted.
|
|
//
|
|
HDCPState hdcpState = {0};
|
|
main->configureHDCPGetHDCPState(hdcpState);
|
|
if (!(isHDCPAuthOn = hdcpState.HDCP_State_Authenticated))
|
|
{
|
|
currentModesetDeviceGroup->hdcpEnabled = false;
|
|
}
|
|
|
|
// Reset value of bIsHeadShutdownNeeded to get rid of false asserts
|
|
currentModesetDeviceGroup->bIsHeadShutdownNeeded = false;
|
|
|
|
// If this is eDP and the LCD power is not ON, we don't need to Disable DSC here
|
|
bool bPanelPwrSts = true;
|
|
if ((!main->isEDP()) || (main->getEdpPowerData(&bPanelPwrSts, NULL) && bPanelPwrSts))
|
|
{
|
|
// Disable DSC decompression on the panel if panel supports DSC and reset bFECEnable Flag
|
|
for (Device * dev = currentModesetDeviceGroup->enumDevices(0); dev;
|
|
dev = currentModesetDeviceGroup->enumDevices(dev))
|
|
{
|
|
if(!(setDeviceDscState(dev, false/*bEnableDsc*/)))
|
|
{
|
|
DP_ASSERT(!"DP2xCONN> Failed to configure DSC on Sink!");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Transfer to inactive group and cancel pending callbacks for that group.
|
|
currentModesetDeviceGroup->cancelHdcpCallbacks();
|
|
activeGroups.remove(currentModesetDeviceGroup);
|
|
inactiveGroups.insertBack(currentModesetDeviceGroup);
|
|
|
|
if (activeGroups.isEmpty())
|
|
{
|
|
cancelHdcpCallbacks();
|
|
|
|
// We disconnected a panel, try to clear the transition
|
|
if (linkAwaitingTransition)
|
|
{
|
|
assessLink();
|
|
}
|
|
//
|
|
// Power down the links as we have switched away from the monitor.
|
|
// Only power down if we are in single stream
|
|
//
|
|
else
|
|
{
|
|
//
|
|
// Power down the links as we have switched away from the monitor.
|
|
// For shared SOR case, we need this to keep SW stats in DP instances in sync.
|
|
// Only power down the link when it's not a compliance test device.
|
|
//
|
|
// Some eDP panels are known having problems when power down.
|
|
// See bug 1425706, 1376753, 1347872, 1355592
|
|
//
|
|
// Hotplug may trigger detach before processNewDevice if previous state has
|
|
// lost device not yet detached. Avoid to powerdown for the case for following
|
|
// device discovery hdcp probe.
|
|
//
|
|
if (!bIsDiscoveryDetectActive)
|
|
powerdownLink(!main->skipPowerdownEdpPanelWhenHeadDetach() && !bKeepOdAlive);
|
|
}
|
|
if (this->policyModesetOrderMitigation && this->modesetOrderMitigation)
|
|
this->modesetOrderMitigation = false;
|
|
}
|
|
fireEvents();
|
|
}
|
|
|
|
/*!
|
|
* @brief Engage flush mode in the HW
|
|
*
|
|
* @return boolean to indicate success/failure
|
|
*
|
|
*/
|
|
|
|
bool ConnectorImpl2x::enableFlush()
|
|
{
|
|
// Fallback to legacy SF Flush sequence
|
|
if (!main->isRgFlushSequenceUsed())
|
|
{
|
|
return ConnectorImpl::enableFlush();
|
|
}
|
|
|
|
bool bHeadAttached = false;
|
|
|
|
if (activeGroups.isEmpty())
|
|
return true;
|
|
|
|
//
|
|
// If SST check that head should be attached with single group else if MST at least
|
|
// 1 group should have headAttached before calling flush on SOR
|
|
//
|
|
if (!this->linkUseMultistream())
|
|
{
|
|
GroupImpl * activeGroup = this->getActiveGroupForSST();
|
|
|
|
if (activeGroup && !activeGroup->isHeadAttached() && intransitionGroups.isEmpty())
|
|
{
|
|
DP_PRINTF(DP_ERROR, "DP2xCONN> SST-Flush mode should not be called when head is not attached. Returning early without enabling flush");
|
|
bFlushSkipped = true;
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (ListElement * e = activeGroups.begin(); e != activeGroups.end(); e = e->next)
|
|
{
|
|
GroupImpl * group = (GroupImpl *)e;
|
|
if (group->isHeadAttached())
|
|
{
|
|
bHeadAttached = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bHeadAttached)
|
|
{
|
|
DP_PRINTF(DP_ERROR, "DP2xCONN> MST-Flush mode should not be called when head is not attached. Returning early without enabling flush");
|
|
bFlushSkipped = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (!main->setFlushMode(FlushModePhase1))
|
|
{
|
|
DP_PRINTF(DP_ERROR, "DP2xCONN> Set flush mode phase 1 failed\n");
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Enabling flush mode shuts down the link, so the next link training
|
|
// call must not skip programming the hardware. Otherwise, EVO will
|
|
// hang if the head is still active when flush mode is disabled.
|
|
//
|
|
|
|
bSkipLt = false;
|
|
|
|
sortActiveGroups(false);
|
|
|
|
for (ListElement * e = activeGroups.begin(); e != activeGroups.end(); e = e->next)
|
|
{
|
|
GroupImpl * g = (GroupImpl *)e;
|
|
|
|
if (!this->linkUseMultistream())
|
|
{
|
|
GroupImpl * activeGroup = this->getActiveGroupForSST();
|
|
DP_ASSERT(g == activeGroup);
|
|
}
|
|
|
|
main->preLinkTraining(g->headIndex);
|
|
|
|
beforeDeleteStream(g, true);
|
|
|
|
if (this->linkUseMultistream() || activeLinkConfig.bIs128b132bChannelCoding)
|
|
{
|
|
main->triggerACT();
|
|
}
|
|
|
|
afterDeleteStream(g);
|
|
}
|
|
|
|
if (!main->setFlushMode(FlushModePhase2))
|
|
{
|
|
DP_PRINTF(DP_ERROR, "DP2xCONN> Set flush mode phase 2 failed\n");
|
|
return false;
|
|
}
|
|
|
|
// Reset activeLinkConfig to indicate the link is now lost
|
|
if (!this->bDisable5019537Fix)
|
|
{
|
|
activeLinkConfig = LinkConfiguration();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*!
|
|
* @brief Disengage flush mode in HW
|
|
*
|
|
* @param[in] test Indicates whether force disable even on failure
|
|
*
|
|
*/
|
|
|
|
void ConnectorImpl2x::disableFlush(bool test)
|
|
{
|
|
// Fallback to legacy SF Flush sequence
|
|
if (!main->isRgFlushSequenceUsed())
|
|
{
|
|
return ConnectorImpl::disableFlush();
|
|
}
|
|
|
|
bool bHeadAttached = false;
|
|
NvU32 attachFailedHeadMask = 0;
|
|
|
|
if (activeGroups.isEmpty())
|
|
return;
|
|
|
|
if (bFlushSkipped)
|
|
{
|
|
bFlushSkipped = false;
|
|
return;
|
|
}
|
|
|
|
sortActiveGroups(true);
|
|
|
|
//
|
|
// If SST check that head should be attached with single group else if MST at least
|
|
// 1 group should have headAttached before calling disable flush on SOR
|
|
//
|
|
if (!this->linkUseMultistream())
|
|
{
|
|
GroupImpl * activeGroup = this->getActiveGroupForSST();
|
|
|
|
if (activeGroup && !activeGroup->isHeadAttached() && intransitionGroups.isEmpty())
|
|
{
|
|
DP_PRINTF(DP_ERROR, "DP2xCONN> SST-Flush mode disable should not be called when head is not attached. Returning early without disabling flush\n");
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (ListElement * e = activeGroups.begin(); e != activeGroups.end(); e = e->next)
|
|
{
|
|
GroupImpl * group = (GroupImpl *)e;
|
|
if (group->isHeadAttached())
|
|
{
|
|
bHeadAttached = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bHeadAttached)
|
|
{
|
|
DP_PRINTF(DP_ERROR, "DP2xCONN> MST-Flush mode disable should not be called when head is not attached. Returning early without disabling flush\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We need to rebuild the tiemslot configuration when exiting flush mode
|
|
// Bug 1550750: Change the order to proceed from last to front as they were added.
|
|
// Some tiled monitors are happy with this.
|
|
//
|
|
for (ListElement * e = activeGroups.last(); e != activeGroups.end(); e = e->prev)
|
|
{
|
|
GroupImpl * g = (GroupImpl *)e;
|
|
|
|
if (!g->isHeadAttached() && this->linkUseMultistream())
|
|
continue;
|
|
|
|
if (!main->clearFlushMode(FlushModePhase1, false, g->headIndex))
|
|
{
|
|
DP_PRINTF(DP_ERROR, "DP2xCONN> Clear flush mode phase 1 failed for headIndex: %d\n", g->headIndex);
|
|
}
|
|
|
|
// Allocate the timeslot configuration
|
|
if (beforeAddStream(g, test, true))
|
|
{
|
|
attachFailedHeadMask |= 1 << g->headIndex;
|
|
}
|
|
|
|
main->postLinkTraining(g->headIndex);
|
|
|
|
afterAddStream(g); // ACT is triggered here
|
|
}
|
|
|
|
if (!main->clearFlushMode(FlushModePhase2, attachFailedHeadMask))
|
|
{
|
|
DP_PRINTF(DP_ERROR, "DP2xCONN> Clear flush mode phase 2 failed\n");
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static bool
|
|
_IsDP2xLinkQualityPattern
|
|
(
|
|
LinkQualityPatternType lqsPattern
|
|
)
|
|
{
|
|
switch (lqsPattern)
|
|
{
|
|
case LINK_QUAL_128B132B_TPS1:
|
|
case LINK_QUAL_128B132B_TPS2:
|
|
case LINK_QUAL_PRBS9:
|
|
case LINK_QUAL_PRBS11:
|
|
case LINK_QUAL_PRBS15:
|
|
case LINK_QUAL_PRBS23:
|
|
case LINK_QUAL_PRBS31:
|
|
case LINK_QUAL_264BIT_CUST:
|
|
case LINK_QUAL_SQUARE_SEQ_WITH_PRESHOOT_ON_DE_EMPHASIS_ON:
|
|
case LINK_QUAL_SQUARE_SEQ_WITH_PRESHOOT_OFF_DE_EMPHASIS_ON:
|
|
case LINK_QUAL_SQUARE_SEQ_WITH_PRESHOOT_ON_DE_EMPHASIS_OFF:
|
|
case LINK_QUAL_SQUARE_SEQ_WITH_PRESHOOT_OFF_DE_EMPHASIS_OFF:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ConnectorImpl2x::handlePhyPatternRequest()
|
|
{
|
|
bool status = true;
|
|
DP2xPatternInfo patternInfo = {};
|
|
|
|
patternInfo.lqsPattern = hal->getPhyTestPattern();
|
|
|
|
if (!_IsDP2xLinkQualityPattern(patternInfo.lqsPattern))
|
|
{
|
|
return ConnectorImpl::handlePhyPatternRequest();
|
|
}
|
|
|
|
// Get lane count from most current link training
|
|
unsigned requestedLanes = this->activeLinkConfig.lanes;
|
|
|
|
if (patternInfo.lqsPattern == LINK_QUAL_264BIT_CUST)
|
|
{
|
|
hal->get264BitsCustomTestPattern((NvU8 *)&patternInfo.ctsmData[0]);
|
|
}
|
|
else if ((patternInfo.lqsPattern == LINK_QUAL_SQUARE_SEQ_WITH_PRESHOOT_ON_DE_EMPHASIS_ON) ||
|
|
(patternInfo.lqsPattern == LINK_QUAL_SQUARE_SEQ_WITH_PRESHOOT_OFF_DE_EMPHASIS_ON) ||
|
|
(patternInfo.lqsPattern == LINK_QUAL_SQUARE_SEQ_WITH_PRESHOOT_ON_DE_EMPHASIS_OFF) ||
|
|
(patternInfo.lqsPattern == LINK_QUAL_SQUARE_SEQ_WITH_PRESHOOT_OFF_DE_EMPHASIS_OFF))
|
|
{
|
|
hal->getSquarePatternNum(&(patternInfo.sqNum));
|
|
}
|
|
|
|
// send control call to rm for the pattern
|
|
if (!main->physicalLayerSetDP2xTestPattern(&patternInfo))
|
|
{
|
|
DP_ASSERT(0 && "Could not set the PHY_TEST_PATTERN");
|
|
status = false;
|
|
}
|
|
else
|
|
{
|
|
if (AuxRetry::ack != hal->setLinkQualPatternSet(patternInfo, requestedLanes))
|
|
{
|
|
DP_ASSERT(0 && "Could not set the LINK_QUAL_PATTERN");
|
|
status = false;
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
bool ConnectorImpl2x::handleTestLinkTrainRequest()
|
|
{
|
|
if (!activeLinkConfig.multistream)
|
|
{
|
|
// route to 1.x funtion if SST
|
|
return ConnectorImpl::handleTestLinkTrainRequest();
|
|
}
|
|
else
|
|
{
|
|
LinkRate requestedRate;
|
|
unsigned requestedLanes;
|
|
|
|
hal->getTestRequestTraining(requestedRate, requestedLanes);
|
|
// if one of them is illegal; don't ack. let the box try again.
|
|
if (requestedRate == 0 || requestedLanes == 0)
|
|
{
|
|
DP_ASSERT(0 && "illegal requestedRate/Lane, retry..");
|
|
hal->setTestResponse(false);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// Compliance shouldn't ask us to train above its caps
|
|
if (requestedRate == 0 || requestedRate > hal->getMaxLinkRate())
|
|
{
|
|
DP_ASSERT(0 && "illegal requestedRate");
|
|
requestedRate = hal->getMaxLinkRate();
|
|
}
|
|
|
|
if (requestedLanes == 0 || requestedLanes > hal->getMaxLaneCount())
|
|
{
|
|
DP_ASSERT(0 && "illegal requestedLanes");
|
|
requestedLanes = hal->getMaxLaneCount();
|
|
}
|
|
|
|
bool bHeadAttached = false;
|
|
GroupImpl *groupAttached = NULL;
|
|
for (ListElement * e = activeGroups.begin(); e != activeGroups.end(); e = e->next)
|
|
{
|
|
groupAttached = (GroupImpl *)e;
|
|
if (groupAttached->isHeadAttached())
|
|
{
|
|
bHeadAttached = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (groupAttached == NULL)
|
|
{
|
|
DP_ASSERT(0 && "Compliance: no group attached");
|
|
}
|
|
|
|
DP_PRINTF(DP_NOTICE, "DP> Compliance: LT on IRQ request: 0x%x, %d.", requestedRate, requestedLanes);
|
|
// now see whether the current resolution is supported on the requested link config
|
|
LinkConfiguration lc(&linkPolicy, requestedLanes, requestedRate, hal->getEnhancedFraming(),
|
|
false, // MST
|
|
false, // disablePostLTRequest
|
|
false, // bEnableFEC
|
|
false, // bDisableLTTPR
|
|
this->getDownspreadDisabled());
|
|
|
|
if (bHeadAttached)
|
|
{
|
|
if (willLinkSupportMode(lc, groupAttached->lastModesetInfo, groupAttached->headIndex, NULL, NULL))
|
|
{
|
|
DP_PRINTF(DP_NOTICE, "DP> Compliance: Executing LT on IRQ: 0x%x, %d.", requestedRate, requestedLanes);
|
|
// we need to force the requirement irrespective of whether is supported or not.
|
|
if (!enableFlush())
|
|
{
|
|
hal->setTestResponse(false);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Check if linkTraining fails, perform fake linktraining. This is required because
|
|
// if we simply fail linkTraining we will not configure the head which results in
|
|
// TDRs if any modset happens after this.
|
|
//
|
|
hal->setTestResponse(true);
|
|
if (!train(lc, false))
|
|
train(lc, true);
|
|
disableFlush();
|
|
// Don't force/commit. Only keep the request.
|
|
setPreferredLinkConfig(lc, false, false);
|
|
return true;
|
|
}
|
|
}
|
|
else // linkconfig is not supporting bandwidth. Simply return NACK
|
|
{
|
|
DP_PRINTF(DP_ERROR, "DP> Compliance: IMP failed with requested link configuration: 0x%x, %d.",
|
|
requestedRate, requestedLanes);
|
|
hal->setTestResponse(false);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DP_PRINTF(DP_NOTICE, "DP> Compliance: Link Training when the head is not attached.");
|
|
hal->setTestResponse(true);
|
|
if (!train(lc, false))
|
|
train(lc, true);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ConnectorImpl2x::ConnectorImpl2x(MainLink * main, AuxBus * auxBus, Timer * timer, Connector::EventSink * sink)
|
|
: ConnectorImpl(main, auxBus, timer, sink)
|
|
{
|
|
bFlushSkipped = false;
|
|
bDisableDownspread = false;
|
|
applyDP2xRegkeyOverrides();
|
|
}
|
|
|
|
bool ConnectorImpl2x::getDp2xLaneConfig(NvU32 *numLanes, NvU32 *data)
|
|
{
|
|
return (main->getDp2xLaneData(numLanes, data));
|
|
}
|
|
|
|
bool ConnectorImpl2x::setDp2xLaneConfig(NvU32 numLanes, NvU32 *data)
|
|
{
|
|
return (main->setDp2xLaneData(numLanes, data));
|
|
}
|
|
|
|
void ConnectorImpl2x::configInit()
|
|
{
|
|
ConnectorImpl::configInit();
|
|
bMstTimeslotBug4968411 = false;
|
|
bApplyManualTimeslotBug4968411 = false;
|
|
bDisableDownspread = false;
|
|
|
|
applyDP2xRegkeyOverrides();
|
|
}
|
|
|
|
void ConnectorImpl2x::applyTimeslotWAR(unsigned &slot_count)
|
|
{
|
|
if (bApplyManualTimeslotBug4968411)
|
|
{
|
|
slot_count += 1;
|
|
}
|
|
}
|
|
|
|
void ConnectorImpl2x::handleEdidWARs(Edid & edid, DiscoveryManager::Device & device)
|
|
{
|
|
// handle DP1.x edid WARs
|
|
ConnectorImpl::handleEdidWARs(edid, device);
|
|
|
|
if (edid.WARFlags.bAllocateManualTimeslots)
|
|
{
|
|
if ((device.address.size() == 2) && bMstTimeslotBug4968411)
|
|
{
|
|
// Samsung G9 Monitor is behind internal branch, allocate one more timeslot
|
|
bApplyManualTimeslotBug4968411 = true;
|
|
bDP2XPreferNonDSCForLowPClk = true;
|
|
}
|
|
}
|
|
|
|
if (edid.WARFlags.bForceHeadShutdown)
|
|
{
|
|
// SST mode
|
|
if (device.address.size() <= 1)
|
|
{
|
|
bForceHeadShutdownPerMonitor = true;
|
|
}
|
|
}
|
|
|
|
if (edid.WARFlags.bDisableDscMaxBppLimit)
|
|
{
|
|
bDisableDscMaxBppLimit = true;
|
|
}
|
|
}
|
|
|