Skip to content

Commit 45c44fc

Browse files
author
j4kuuu
committed
drm: handle CRTC starvation recovery when outputs are disabled
When more displays are connected than CRTCs available, connectors that arrive after all CRTCs are claimed become starved. The compositor can free a CRTC by disabling an output, but nothing reclaimed that CRTC for the starved connector. Restructure recheckCRTCs() into two passes: first, disabled outputs release their CRTCs and enabled connectors get priority assignment; second, any remaining free CRTCs are given to disabled connectors as backup slots for quick re-enable. When applyCommit() detects an enabledState transition, schedule recheckOutputs() via addIdleEvent so starved connectors pick up the freed CRTC on the next event loop iteration, without reentrancy or blocking the event loop.
1 parent 5d2cb72 commit 45c44fc

1 file changed

Lines changed: 59 additions & 3 deletions

File tree

src/backend/drm/DRM.cpp

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,12 @@ void Aquamarine::CDRMBackend::recheckCRTCs() {
699699
continue;
700700
}
701701

702+
// disabled outputs release their CRTCs so active outputs get priority
703+
if (c->crtc && c->status == DRM_MODE_CONNECTED && c->output && !c->output->enabledState) {
704+
backend->log(AQ_LOG_DEBUG, std::format("drm: {} is disabled, releasing crtc {}", c->szName, c->crtc->id));
705+
c->crtc.reset();
706+
}
707+
702708
if (c->crtc && c->status == DRM_MODE_CONNECTED) {
703709
backend->log(AQ_LOG_DEBUG, std::format("drm: Skipping connector {}, has crtc {} and is connected", c->szName, c->crtc->id));
704710
continue;
@@ -727,14 +733,18 @@ void Aquamarine::CDRMBackend::recheckCRTCs() {
727733

728734
bool assigned = false;
729735

730-
// try to use a connected connector
736+
// try to use a connected, enabled connector
731737
for (auto const& c : recheck) {
732738
if (!(c->possibleCrtcs & (1 << i)))
733739
continue;
734740

735741
if (c->status != DRM_MODE_CONNECTED)
736742
continue;
737743

744+
// Pass 1 only assigns to enabled connectors
745+
if (c->output && !c->output->enabledState)
746+
continue;
747+
738748
// deactivate old output
739749
if (c->output && c->output->state && c->output->state->state().enabled) {
740750
c->output->state->setEnabled(false);
@@ -754,11 +764,38 @@ void Aquamarine::CDRMBackend::recheckCRTCs() {
754764
backend->log(AQ_LOG_DEBUG, std::format("drm: slot {} crtc {} unassigned", i, crtcs.at(i)->id));
755765
}
756766

767+
// Pass 2: assign remaining CRTCs to disabled connectors as backup slots
768+
for (size_t i = 0; i < crtcs.size(); ++i) {
769+
bool taken = false;
770+
for (auto const& c : connectors) {
771+
if (c->crtc == crtcs.at(i)) {
772+
taken = true;
773+
break;
774+
}
775+
}
776+
if (taken)
777+
continue;
778+
779+
for (auto const& c : recheck) {
780+
if (!(c->possibleCrtcs & (1 << i)))
781+
continue;
782+
if (c->status != DRM_MODE_CONNECTED)
783+
continue;
784+
785+
backend->log(AQ_LOG_DEBUG, std::format("drm: backup slot {} crtc {} assigned to disabled {}", i, crtcs.at(i)->id, c->szName));
786+
c->crtc = crtcs.at(i);
787+
std::erase(recheck, c);
788+
break;
789+
}
790+
}
791+
757792
for (auto const& c : connectors) {
758793
if (c->status == DRM_MODE_CONNECTED)
759794
continue;
760795

761-
backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} is not connected{}", c->szName, c->crtc ? std::format(", removing old crtc {}", c->crtc->id) : ""));
796+
if (c->crtc)
797+
backend->log(AQ_LOG_DEBUG, std::format("drm: {} is not connected, clearing stale crtc {}", c->szName, c->crtc->id));
798+
c->crtc.reset();
762799
}
763800

764801
// tell the user to re-assign a valid mode etc, if needed
@@ -884,6 +921,11 @@ void Aquamarine::CDRMBackend::recheckOutputs() {
884921
// now that crtcs are assigned, connect outputs
885922
for (const auto& conn : connectors) {
886923
if (conn->status == DRM_MODE_CONNECTED && !conn->output && !conn->tilingRedundant) {
924+
if (!conn->crtc) {
925+
backend->log(AQ_LOG_DEBUG, std::format("drm: {} has no CRTC, deferring connection", conn->szName));
926+
continue;
927+
}
928+
887929
backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} connected", conn->szName));
888930

889931
auto drmConn = drmModeGetConnector(gpu->fd, conn->id);
@@ -1726,13 +1768,27 @@ void Aquamarine::SDRMConnector::applyCommit(const SDRMConnectorCommitData& data)
17261768
if (output->state->state().committed & COutputState::AQ_OUTPUT_STATE_MODE)
17271769
refresh = calculateRefresh(data.modeInfo);
17281770

1729-
output->enabledState = output->state->state().enabled;
1771+
const bool wasEnabled = output->enabledState;
1772+
output->enabledState = output->state->state().enabled;
17301773

17311774
if (!output->enabledState)
17321775
releaseFBReferences();
17331776

17341777
if (!backend->updateSecondaryRendererState())
17351778
backend->backend->log(AQ_LOG_ERROR, std::format("drm: Failed to update renderer state for {} on applyCommit", szName));
1779+
1780+
if (wasEnabled != output->enabledState) {
1781+
auto bk = backend.lock();
1782+
if (bk) {
1783+
bk->backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} enabledState changed {} -> {}", szName, wasEnabled, output->enabledState));
1784+
auto weak = bk->self;
1785+
bk->backend->addIdleEvent(makeShared<std::function<void(void)>>([weak] {
1786+
auto b = weak.lock();
1787+
if (b)
1788+
b->recheckOutputs();
1789+
}));
1790+
}
1791+
}
17361792
}
17371793

17381794
void Aquamarine::SDRMConnector::rollbackCommit(const SDRMConnectorCommitData& data) {

0 commit comments

Comments
 (0)