From 9f333ab9aef4e6d14d2b89189ffebd270f413683 Mon Sep 17 00:00:00 2001 From: Mick Ryan Date: Tue, 15 Sep 2026 14:19:05 -0400 Subject: [PATCH 1/3] nvkms: select 2Heads1OR for DP modes wider than the DSC 5120 limit Clock-only splitting leaves 6016x3384@60 on a single head, then DSC rejects pic_width > 5120. Query single-head DP validation first so uncompressed wide modes stay on one head. --- src/common/modeset/timing/nvt_dsc_pps.c | 2 +- src/common/modeset/timing/nvt_dsc_pps.h | 1 + src/nvidia-modeset/include/nvkms-evo.h | 5 ++-- src/nvidia-modeset/src/nvkms-evo.c | 32 +++++++++++++++++++++---- src/nvidia-modeset/src/nvkms-modepool.c | 2 +- src/nvidia-modeset/src/nvkms-modeset.c | 3 +++ 6 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/common/modeset/timing/nvt_dsc_pps.c b/src/common/modeset/timing/nvt_dsc_pps.c index 4ed6c25a42..2ee862738b 100644 --- a/src/common/modeset/timing/nvt_dsc_pps.c +++ b/src/common/modeset/timing/nvt_dsc_pps.c @@ -1010,7 +1010,7 @@ DSC_PpsCalcBase } else { - RANGE_CHECK("pic_width", in->pic_width, 32, 5120); + RANGE_CHECK("pic_width", in->pic_width, 32, DSC_MAX_SINGLE_HEAD_WIDTH); } } diff --git a/src/common/modeset/timing/nvt_dsc_pps.h b/src/common/modeset/timing/nvt_dsc_pps.h index bd49c1464a..a13911ef84 100644 --- a/src/common/modeset/timing/nvt_dsc_pps.h +++ b/src/common/modeset/timing/nvt_dsc_pps.h @@ -34,6 +34,7 @@ /* ------------------------ Macros ----------------------------------------- */ #define DSC_MAX_PPS_SIZE_DWORD 32 +#define DSC_MAX_SINGLE_HEAD_WIDTH 5120 /* ------------------------ Datatypes -------------------------------------- */ typedef struct diff --git a/src/nvidia-modeset/include/nvkms-evo.h b/src/nvidia-modeset/include/nvkms-evo.h index 63cc8b9538..e1b0e21861 100644 --- a/src/nvidia-modeset/include/nvkms-evo.h +++ b/src/nvidia-modeset/include/nvkms-evo.h @@ -196,8 +196,8 @@ NvBool nvDowngradeColorFormatAndBpc( const NvBool colorBpcSpecified, NVDpyAttributeColor *pDpyColor); -NvBool nvDPValidateModeEvo(NVDpyEvoPtr pDpyEvo, - NVHwModeTimingsEvoPtr pTimings, +NvBool nvDPValidateModeEvo(const NVDpyEvoRec *pDpyEvo, + const NVHwModeTimingsEvo *pTimings, const NvBool colorFormatSpecified, const NvBool colorBpcSpecified, NVDpyAttributeColor *pDpyColor, @@ -435,6 +435,7 @@ NvBool nvEvoGetSingleMergeHeadSectionHwModeTimings( NvBool nvEvoUse2Heads1OR(const NVDpyEvoRec *pDpyEvo, const NVHwModeTimingsEvo *pTimings, + const NVDpyAttributeColor *pDpyColor, const struct NvKmsModeValidationParams *pParams); NvU32 nvGetRefreshRate10kHz(const NVHwModeTimingsEvo *pTimings); diff --git a/src/nvidia-modeset/src/nvkms-evo.c b/src/nvidia-modeset/src/nvkms-evo.c index 600003f0ab..a7f1b3b1c6 100644 --- a/src/nvidia-modeset/src/nvkms-evo.c +++ b/src/nvidia-modeset/src/nvkms-evo.c @@ -7032,8 +7032,8 @@ NvBool nvDowngradeColorFormatAndBpc( * the given mode timings. */ -NvBool nvDPValidateModeEvo(NVDpyEvoPtr pDpyEvo, - NVHwModeTimingsEvoPtr pTimings, +NvBool nvDPValidateModeEvo(const NVDpyEvoRec *pDpyEvo, + const NVHwModeTimingsEvo *pTimings, const NvBool colorFormatSpecified, const NvBool colorBpcSpecified, NVDpyAttributeColor *pDpyColor, @@ -9813,6 +9813,7 @@ NvBool nvEvoGetSingleMergeHeadSectionHwModeTimings( NvBool nvEvoUse2Heads1OR(const NVDpyEvoRec *pDpyEvo, const NVHwModeTimingsEvo *pTimings, + const NVDpyAttributeColor *pDpyColor, const struct NvKmsModeValidationParams *pParams) { const NVDispEvoRec *pDispEvo = pDpyEvo->pDispEvo; @@ -9846,9 +9847,30 @@ NvBool nvEvoUse2Heads1OR(const NVDpyEvoRec *pDpyEvo, return FALSE; } - /* Use 2Heads1OR mode only if the required pixel clock is greater than the - * maximum pixel clock support by a head. */ - return (pTimings->pixelClock > pHeadCaps->maxPClkKHz); + if (pTimings->pixelClock > pHeadCaps->maxPClkKHz) { + return TRUE; + } + + if (!nvDpyUsesDPLib(pDpyEvo) || + (pParams->dscMode == NVKMS_DSC_MODE_FORCE_DISABLE) || + (pTimings->yuv420Mode == NV_YUV420_MODE_HW) || + (nvEvoVisibleWidth(pTimings) <= DSC_MAX_SINGLE_HEAD_WIDTH)) { + return FALSE; + } + + /* + * A wide DSC picture needs two heads even when its pixel clock fits one. + * Preserve single-head modes that can already pass DP validation, + * including its existing uncompressed color-depth fallbacks. + */ + NVDpyAttributeColor dpyColor = *pDpyColor; + NVDscInfoEvoRec dscInfo = { }; + + return !nvDPValidateModeEvo(pDpyEvo, pTimings, + FALSE /* colorFormatSpecified */, + FALSE /* colorBpcSpecified */, + &dpyColor, FALSE /* b2Heads1Or */, + &dscInfo, pParams); } NvBool nvIsLockGroupFlipLocked(const NVLockGroup *pLockGroup) diff --git a/src/nvidia-modeset/src/nvkms-modepool.c b/src/nvidia-modeset/src/nvkms-modepool.c index 59dfd370df..ad51dc5b7f 100644 --- a/src/nvidia-modeset/src/nvkms-modepool.c +++ b/src/nvidia-modeset/src/nvkms-modepool.c @@ -1721,7 +1721,7 @@ static NvBool ValidateMode(NVDpyEvoPtr pDpyEvo, goto done; } - b2Heads1Or = nvEvoUse2Heads1OR(pDpyEvo, pTimingsEvo, pParams); + b2Heads1Or = nvEvoUse2Heads1OR(pDpyEvo, pTimingsEvo, &dpyColor, pParams); if (nvDpyIsHdmiEvo(pDpyEvo)) { NvBool foundFrlConfig = FALSE; diff --git a/src/nvidia-modeset/src/nvkms-modeset.c b/src/nvidia-modeset/src/nvkms-modeset.c index f51f97bcf9..71609240ac 100644 --- a/src/nvidia-modeset/src/nvkms-modeset.c +++ b/src/nvidia-modeset/src/nvkms-modeset.c @@ -831,6 +831,7 @@ static NvBool AssignProposedHwHeadsGeneric( if (!nvEvoUse2Heads1OR( pDpyEvo, &pProposedApiHead->timings, + &pProposedApiHead->attributes.color, &pProposedApiHead->modeValidationParams)) { continue; } @@ -840,6 +841,7 @@ static NvBool AssignProposedHwHeadsGeneric( nvAssert(!nvEvoUse2Heads1OR( pDpyEvo, &pProposedApiHead->timings, + &pProposedApiHead->attributes.color, &pProposedApiHead->modeValidationParams)); NvU32 foundHead = GetFreeHeads(pDevEvo, apiHead, pDpyEvo, @@ -908,6 +910,7 @@ void ClearIncompatibleHwHeadsMaskOneDisp(const NVDispEvoRec *pDispEvo, const NvBool new2Heads1OrState = nvEvoUse2Heads1OR(pDpyEvo, &pProposedApiHead->timings, + &pProposedApiHead->attributes.color, &pProposedApiHead->modeValidationParams); if (new2Heads1OrState != current2Heads1OrState) { From 15a8ca49e54aa14f3a0b9696c4293c41ec39b0cf Mon Sep 17 00:00:00 2001 From: Mick Ryan Date: Tue, 15 Sep 2026 14:20:20 -0400 Subject: [PATCH 2/3] nvkms: honor specified color/BPC when probing single-head width The 2Heads1OR query used FALSE, FALSE, so it could accept a lower-color single-head fallback that later validation would reject. --- src/nvidia-modeset/include/nvkms-evo.h | 2 ++ src/nvidia-modeset/src/nvkms-evo.c | 6 ++++-- src/nvidia-modeset/src/nvkms-modepool.c | 5 ++++- src/nvidia-modeset/src/nvkms-modeset.c | 6 ++++++ 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/nvidia-modeset/include/nvkms-evo.h b/src/nvidia-modeset/include/nvkms-evo.h index e1b0e21861..bb790ae4e0 100644 --- a/src/nvidia-modeset/include/nvkms-evo.h +++ b/src/nvidia-modeset/include/nvkms-evo.h @@ -436,6 +436,8 @@ NvBool nvEvoGetSingleMergeHeadSectionHwModeTimings( NvBool nvEvoUse2Heads1OR(const NVDpyEvoRec *pDpyEvo, const NVHwModeTimingsEvo *pTimings, const NVDpyAttributeColor *pDpyColor, + const NvBool colorFormatSpecified, + const NvBool colorBpcSpecified, const struct NvKmsModeValidationParams *pParams); NvU32 nvGetRefreshRate10kHz(const NVHwModeTimingsEvo *pTimings); diff --git a/src/nvidia-modeset/src/nvkms-evo.c b/src/nvidia-modeset/src/nvkms-evo.c index a7f1b3b1c6..72cdf5d347 100644 --- a/src/nvidia-modeset/src/nvkms-evo.c +++ b/src/nvidia-modeset/src/nvkms-evo.c @@ -9814,6 +9814,8 @@ NvBool nvEvoGetSingleMergeHeadSectionHwModeTimings( NvBool nvEvoUse2Heads1OR(const NVDpyEvoRec *pDpyEvo, const NVHwModeTimingsEvo *pTimings, const NVDpyAttributeColor *pDpyColor, + const NvBool colorFormatSpecified, + const NvBool colorBpcSpecified, const struct NvKmsModeValidationParams *pParams) { const NVDispEvoRec *pDispEvo = pDpyEvo->pDispEvo; @@ -9867,8 +9869,8 @@ NvBool nvEvoUse2Heads1OR(const NVDpyEvoRec *pDpyEvo, NVDscInfoEvoRec dscInfo = { }; return !nvDPValidateModeEvo(pDpyEvo, pTimings, - FALSE /* colorFormatSpecified */, - FALSE /* colorBpcSpecified */, + colorFormatSpecified, + colorBpcSpecified, &dpyColor, FALSE /* b2Heads1Or */, &dscInfo, pParams); } diff --git a/src/nvidia-modeset/src/nvkms-modepool.c b/src/nvidia-modeset/src/nvkms-modepool.c index ad51dc5b7f..6b42ee5421 100644 --- a/src/nvidia-modeset/src/nvkms-modepool.c +++ b/src/nvidia-modeset/src/nvkms-modepool.c @@ -1721,7 +1721,10 @@ static NvBool ValidateMode(NVDpyEvoPtr pDpyEvo, goto done; } - b2Heads1Or = nvEvoUse2Heads1OR(pDpyEvo, pTimingsEvo, &dpyColor, pParams); + b2Heads1Or = nvEvoUse2Heads1OR(pDpyEvo, pTimingsEvo, &dpyColor, + pDpyOutputColorParams->formatSpecified, + pDpyOutputColorParams->bpcSpecified, + pParams); if (nvDpyIsHdmiEvo(pDpyEvo)) { NvBool foundFrlConfig = FALSE; diff --git a/src/nvidia-modeset/src/nvkms-modeset.c b/src/nvidia-modeset/src/nvkms-modeset.c index 71609240ac..d3809ca16e 100644 --- a/src/nvidia-modeset/src/nvkms-modeset.c +++ b/src/nvidia-modeset/src/nvkms-modeset.c @@ -832,6 +832,8 @@ static NvBool AssignProposedHwHeadsGeneric( pDpyEvo, &pProposedApiHead->timings, &pProposedApiHead->attributes.color, + pProposedApiHead->colorFormatSpecified, + pProposedApiHead->colorBpcSpecified, &pProposedApiHead->modeValidationParams)) { continue; } @@ -842,6 +844,8 @@ static NvBool AssignProposedHwHeadsGeneric( pDpyEvo, &pProposedApiHead->timings, &pProposedApiHead->attributes.color, + pProposedApiHead->colorFormatSpecified, + pProposedApiHead->colorBpcSpecified, &pProposedApiHead->modeValidationParams)); NvU32 foundHead = GetFreeHeads(pDevEvo, apiHead, pDpyEvo, @@ -911,6 +915,8 @@ void ClearIncompatibleHwHeadsMaskOneDisp(const NVDispEvoRec *pDispEvo, nvEvoUse2Heads1OR(pDpyEvo, &pProposedApiHead->timings, &pProposedApiHead->attributes.color, + pProposedApiHead->colorFormatSpecified, + pProposedApiHead->colorBpcSpecified, &pProposedApiHead->modeValidationParams); if (new2Heads1OrState != current2Heads1OrState) { From 1a62581567f644a8fad1e2ca0d55a8a22cffa92c Mon Sep 17 00:00:00 2001 From: Mick Ryan Date: Tue, 15 Sep 2026 16:01:59 -0400 Subject: [PATCH 3/3] perf(nvkms): omit unused DSC probe output Single-head probing only needs a boolean result. The PRE_IMP path accepts NULL, so skip the unused output initialization and PPS copy. --- src/nvidia-modeset/src/nvkms-evo.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/nvidia-modeset/src/nvkms-evo.c b/src/nvidia-modeset/src/nvkms-evo.c index 72cdf5d347..3b1cf25b80 100644 --- a/src/nvidia-modeset/src/nvkms-evo.c +++ b/src/nvidia-modeset/src/nvkms-evo.c @@ -9866,13 +9866,12 @@ NvBool nvEvoUse2Heads1OR(const NVDpyEvoRec *pDpyEvo, * including its existing uncompressed color-depth fallbacks. */ NVDpyAttributeColor dpyColor = *pDpyColor; - NVDscInfoEvoRec dscInfo = { }; return !nvDPValidateModeEvo(pDpyEvo, pTimings, colorFormatSpecified, colorBpcSpecified, &dpyColor, FALSE /* b2Heads1Or */, - &dscInfo, pParams); + NULL /* pDscInfo */, pParams); } NvBool nvIsLockGroupFlipLocked(const NVLockGroup *pLockGroup)