Subject: [PATCH 22/24] drm: apply tiled Night Light CTM and grouped DPMS

From: Justin Youens <[email-masked]>
Date: Tue, 18 Aug 2026 11:34:57 -0500

Accept AQ_OUTPUT_STATE_CTM on the AQ_DRM_TILED_STARTUP=1 aggregate
commit path and write one CTM blob to every member CRTC atomically,
including identity/off. Preserve that matrix across later frame
commits so hyprsunset no longer poisons the 5K output.

Accept enabled=false as one all-member atomic disable and restore
both tiles together on the following enable, instead of rejecting
DPMS and leaving the UltraFine stuck on.
---
 CMakeLists.txt                            |   1 +
 src/backend/drm/DRM.cpp                   |  87 +++--
 src/backend/drm/TiledAtomic.cpp           |  11 +
 src/backend/drm/TiledAtomic.hpp           |   6 +
 src/backend/drm/TiledDRMIntegration.cpp   | 275 ++++++++++++++--
 src/backend/drm/TiledDRMIntegration.hpp   | 199 +++++++-----
 src/backend/drm/TiledRuntimeAuthority.cpp | 127 ++++++++
 src/backend/drm/TiledRuntimeAuthority.hpp |  26 +-
 tests/TiledGroupedStartupCtm.cpp          | 369 ++++++++++++++++++++++
 9 files changed, 964 insertions(+), 137 deletions(-)
 create mode 100644 tests/TiledGroupedStartupCtm.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index e8d1e47619e25e943754558f1b6ffdfeebe252b3..630b4e02debef5269274fbc73a8f5f8fddf43eae 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -209,6 +209,7 @@ addTiledPrivateTest(tileTopology "tests/TileTopology.cpp")
 addTiledPrivateTest(tileModes "tests/TileModes.cpp")
 addTiledPrivateTest(tiledBackendState "tests/TiledBackendState.cpp")
 addTiledPrivateTest(tiledDRMIntegration "tests/TiledDRMIntegration.cpp")
+addTiledPrivateTest(tiledGroupedStartupCtm "tests/TiledGroupedStartupCtm.cpp")
 addTiledPrivateTest(planeFormats "tests/PlaneFormats.cpp")
 addTiledPrivateTest(planeViewport "tests/PlaneViewport.cpp")
 addTiledPrivateTest(tiledCrtcAllocator "tests/TiledCrtcAllocator.cpp")
diff --git a/src/backend/drm/DRM.cpp b/src/backend/drm/DRM.cpp
index 90bdeae1b2561736b81087b37c5c4bc1b7662bdc..fc50175e06a34c81cb51def517679e4f0cc43e4f 100644
--- a/src/backend/drm/DRM.cpp
+++ b/src/backend/drm/DRM.cpp
@@ -21,6 +21,7 @@
 #include <filesystem>
 #include <limits>
 #include <map>
+#include <optional>
 #include <set>
 #include <span>
 #include <system_error>
@@ -1916,8 +1917,8 @@ void Aquamarine::CDRMBackend::recheckOutputs() {
             const auto STARTUP = NDRM::NTiledDRMIntegration::prepareGroupedStartup(SELF, connectors, CAPTURE_PROVIDER);
             if (STARTUP)
                 backend->log(AQ_LOG_WARNING,
-                             std::format("drm: AQ_DRM_TILED_STARTUP=1 PREPARED {}x{}@{:.2f}Hz aggregate members={} preferred={} on {}", STARTUP.mode.width,
-                                         STARTUP.mode.height, STARTUP.mode.refreshRate / 1000.0, STARTUP.mode.memberCount, STARTUP.mode.preferred, gpu->path));
+                             std::format("drm: AQ_DRM_TILED_STARTUP=1 PREPARED {}x{}@{:.2f}Hz aggregate members={} preferred={} on {}", STARTUP.mode.width, STARTUP.mode.height,
+                                         STARTUP.mode.refreshRate / 1000.0, STARTUP.mode.memberCount, STARTUP.mode.preferred, gpu->path));
             else
                 backend->log(AQ_LOG_ERROR,
                              std::format("drm: AQ_DRM_TILED_STARTUP=1 FALLBACK ordinary outputs on {} status={} test={} route={}", gpu->path, static_cast<uint32_t>(STARTUP.status),
@@ -3193,19 +3194,24 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
             if (ROLE != eGroupedStartupConnectorRole::PRIMARY || !BACKEND || !BACKEND->backend || !BACKEND->backend->session->active)
                 return false;
 
-            const auto& STATE     = state->state();
-            const auto  MODE      = STATE.customMode ? CSharedPointer<SOutputMode>{} : STATE.mode.lock();
-            uint32_t    SUPPORTED = COutputState::AQ_OUTPUT_STATE_DAMAGE | COutputState::AQ_OUTPUT_STATE_ENABLED | COutputState::AQ_OUTPUT_STATE_PRESENTATION_MODE |
-                COutputState::AQ_OUTPUT_STATE_MODE | COutputState::AQ_OUTPUT_STATE_FORMAT | COutputState::AQ_OUTPUT_STATE_BUFFER;
-            // Match the ordinary atomic path while this startup-only aggregate
-            // advertises implicit synchronization: Hyprland may still commit
-            // its renderer fence, but ordinary Atomic ignores it when
-            // supportsExplicit is false.
-            if (!supportsExplicit)
-                SUPPORTED |= COutputState::AQ_OUTPUT_STATE_EXPLICIT_IN_FENCE;
-            const auto UNSUPPORTED = STATE.committed & ~SUPPORTED;
-            if (!MODE || !STATE.enabled || STATE.presentationMode != AQ_OUTPUT_PRESENTATION_VSYNC || STATE.adaptiveSync || STATE.drmFormat == DRM_FORMAT_INVALID ||
-                UNSUPPORTED != 0 || (STATE.buffer && STATE.buffer->attachments.has<CDRMBufferUnimportable>())) {
+            const auto&                                                    STATE = state->state();
+            const auto                                                     MODE  = STATE.customMode ? CSharedPointer<SOutputMode>{} : STATE.mode.lock();
+            const NDRM::NTiledDRMIntegration::SGroupedStartupOutputRequest REQUEST{
+                .committed          = STATE.committed,
+                .enabled            = STATE.enabled,
+                .adaptiveSync       = STATE.adaptiveSync,
+                .presentationMode   = STATE.presentationMode,
+                .drmFormat          = STATE.drmFormat,
+                .hasMode            = static_cast<bool>(MODE),
+                .hasCustomMode      = static_cast<bool>(STATE.customMode),
+                .hasBuffer          = static_cast<bool>(STATE.buffer),
+                .bufferUnimportable = STATE.buffer && STATE.buffer->attachments.has<CDRMBufferUnimportable>(),
+                .supportsExplicit   = supportsExplicit,
+            };
+            const auto DISPOSITION = NDRM::NTiledDRMIntegration::classifyGroupedStartupOutputRequest(REQUEST);
+            if (DISPOSITION == NDRM::NTiledDRMIntegration::eGroupedStartupOutputDisposition::REJECT) {
+                const auto SUPPORTED   = NDRM::NTiledDRMIntegration::groupedStartupSupportedStateBits(supportsExplicit);
+                const auto UNSUPPORTED = STATE.committed & ~SUPPORTED;
                 BACKEND->log(AQ_LOG_ERROR,
                              std::format("drm: AQ_DRM_TILED_STARTUP=1 rejected unsupported aggregate output state committed=0x{:x} unsupported=0x{:x} enabled={} "
                                          "presentation={} adaptive={} format=0x{:08x} mode={} customMode={} modeSize={}x{} modeRefresh={} buffer={} inFence={} outFence={} "
@@ -3216,9 +3222,35 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
                 return false;
             }
 
-            const bool BUFFER_COMMITTED = (STATE.committed & COutputState::AQ_OUTPUT_STATE_BUFFER) != 0;
+            const bool                  BUFFER_COMMITTED = (STATE.committed & COutputState::AQ_OUTPUT_STATE_BUFFER) != 0;
+            const std::optional<Mat3x3> CTM              = (STATE.committed & COutputState::AQ_OUTPUT_STATE_CTM) ? std::optional<Mat3x3>{STATE.ctm} : std::nullopt;
+            if (DISPOSITION == NDRM::NTiledDRMIntegration::eGroupedStartupOutputDisposition::DISABLE) {
+                const auto RESULT = NDRM::NTiledDRMIntegration::commitGroupedStartup(
+                    BACKEND, connector, MODE, {}, STATE.buffer && BUFFER_COMMITTED ? STATE.buffer : CSharedPointer<IBuffer>{}, onlyTest, false, 0, 0, {}, CTM);
+                if (!RESULT.claimed || !RESULT.success) {
+                    BACKEND->log(AQ_LOG_ERROR, std::format("drm: AQ_DRM_TILED_STARTUP=1 disable {} rejected claimed={} success={}", name, RESULT.claimed, RESULT.success));
+                    return false;
+                }
+                if (onlyTest)
+                    return true;
+                enabledState = false;
+                events.commit.emit();
+                state->onCommit();
+                lastCommitNoBuffer       = true;
+                needsFrame               = false;
+                connector->commitTainted = false;
+                BACKEND->log(AQ_LOG_DEBUG, std::format("drm: AQ_DRM_TILED_STARTUP=1 disable {} accepted for every group member", name));
+                return true;
+            }
+
             if (!onlyTest && !BUFFER_COMMITTED) {
-                if (!NDRM::NTiledDRMIntegration::groupedStartupStateOnlyCurrent(BACKEND, connector, MODE)) {
+                if (CTM) {
+                    const auto RESULT = NDRM::NTiledDRMIntegration::commitGroupedStartup(BACKEND, connector, MODE, {}, {}, onlyTest, true, 0, 0, {}, CTM);
+                    if (!RESULT.claimed || !RESULT.success) {
+                        BACKEND->log(AQ_LOG_ERROR, std::format("drm: AQ_DRM_TILED_STARTUP=1 ctm {} rejected claimed={} success={}", name, RESULT.claimed, RESULT.success));
+                        return false;
+                    }
+                } else if (!NDRM::NTiledDRMIntegration::groupedStartupStateOnlyCurrent(BACKEND, connector, MODE)) {
                     BACKEND->log(AQ_LOG_ERROR, std::format("drm: AQ_DRM_TILED_STARTUP=1 state-only {} rejected non-current aggregate committed=0x{:x}", name, STATE.committed));
                     return false;
                 }
@@ -3227,7 +3259,7 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
                 lastCommitNoBuffer       = true;
                 needsFrame               = false;
                 connector->commitTainted = false;
-                BACKEND->log(AQ_LOG_DEBUG, std::format("drm: AQ_DRM_TILED_STARTUP=1 state-only {} accepted without KMS", name));
+                BACKEND->log(AQ_LOG_DEBUG, std::format("drm: AQ_DRM_TILED_STARTUP=1 state-only {} accepted{}", name, CTM ? " with grouped CTM" : " without KMS"));
                 return true;
             }
 
@@ -3259,12 +3291,15 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
                 return false;
 
             uint32_t   captureFailureStage = 0;
-            const auto RESULT = NDRM::NTiledDRMIntegration::commitGroupedStartup(BACKEND, connector, MODE, framebuffer, STATE.buffer, onlyTest, TRANSACTION, DEADLINE, [&]() {
-                auto capture = captureGroupedTestKMS(BACKEND->gpu->fd, BACKEND->connectors, BACKEND->crtcs, BACKEND->planes, captureFailureStage);
-                if (!capture)
-                    BACKEND->log(AQ_LOG_ERROR, std::format("drm: AQ_DRM_TILED_STARTUP=1 commit capture failed at stage {}", captureFailureStage));
-                return capture;
-            });
+            const auto RESULT              = NDRM::NTiledDRMIntegration::commitGroupedStartup(
+                BACKEND, connector, MODE, framebuffer, STATE.buffer, onlyTest, true, TRANSACTION, DEADLINE,
+                [&]() {
+                    auto capture = captureGroupedTestKMS(BACKEND->gpu->fd, BACKEND->connectors, BACKEND->crtcs, BACKEND->planes, captureFailureStage);
+                    if (!capture)
+                        BACKEND->log(AQ_LOG_ERROR, std::format("drm: AQ_DRM_TILED_STARTUP=1 commit capture failed at stage {}", captureFailureStage));
+                    return capture;
+                },
+                CTM);
             if (!RESULT.claimed || !RESULT.success) {
                 const bool TRANSIENT_FRAME = !onlyTest && RESULT.claimed && !RESULT.initial && !RESULT.terminalFailure &&
                     (RESULT.frame.status == eGroupedFrameStatus::RUNTIME_NOT_STARTED ||
@@ -3315,8 +3350,8 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
             if (RESULT.initial) {
                 BACKEND->log(AQ_LOG_DEBUG,
                              std::format("drm: AQ_DRM_TILED_STARTUP=1 initial {} accepted transaction={} status={} kms={} runtime={} registry={}", name, TRANSACTION,
-                                         static_cast<uint32_t>(TRANSACTION_RESULT.status), TRANSACTION_RESULT.effects.kmsAccepted,
-                                         TRANSACTION_RESULT.effects.runtimeAccepted, TRANSACTION_RESULT.effects.registryAccepted));
+                                         static_cast<uint32_t>(TRANSACTION_RESULT.status), TRANSACTION_RESULT.effects.kmsAccepted, TRANSACTION_RESULT.effects.runtimeAccepted,
+                                         TRANSACTION_RESULT.effects.registryAccepted));
             }
 
             enabledState       = true;
diff --git a/src/backend/drm/TiledAtomic.cpp b/src/backend/drm/TiledAtomic.cpp
index 66ea87abc70a61831af688bf0e13856bcf117f4c..57fce787389f7c4b3d9b8b318b18513825db8719 100644
--- a/src/backend/drm/TiledAtomic.cpp
+++ b/src/backend/drm/TiledAtomic.cpp
@@ -2,6 +2,7 @@
 
 #include <algorithm>
 #include <array>
+#include <cerrno>
 #include <cmath>
 #include <exception>
 #include <limits>
@@ -101,6 +102,16 @@ namespace {
         int destroyModeBlob(int drmFD, uint32_t blobId) noexcept override {
             return drmModeDestroyPropertyBlob(drmFD, blobId);
         }
+
+        int createPropertyBlob(int drmFD, const void* data, size_t size, uint32_t* blobId) noexcept override {
+            if (!data || !blobId || size == 0)
+                return -EINVAL;
+            return drmModeCreatePropertyBlob(drmFD, data, size, blobId);
+        }
+
+        int destroyPropertyBlob(int drmFD, uint32_t blobId) noexcept override {
+            return drmModeDestroyPropertyBlob(drmFD, blobId);
+        }
     };
 
     [[nodiscard]] bool addProperty(const CSharedPointer<ITiledAtomicRequestOperations>& operations, drmModeAtomicReqPtr request, uint32_t objectId, uint32_t propertyId,
diff --git a/src/backend/drm/TiledAtomic.hpp b/src/backend/drm/TiledAtomic.hpp
index aa09a90679f290e696ec11d40f83df6c0e59ccd8..1bcc614a52f27e935267df1bf5261b1546eae846 100644
--- a/src/backend/drm/TiledAtomic.hpp
+++ b/src/backend/drm/TiledAtomic.hpp
@@ -464,6 +464,12 @@ namespace Aquamarine::NDRM::NTiledAtomic { // NOLINT(readability-identifier-nami
 
         virtual int createModeBlob(int drmFD, const drmModeModeInfo& mode, uint32_t* blobId) noexcept = 0;
         virtual int destroyModeBlob(int drmFD, uint32_t blobId) noexcept                              = 0;
+        virtual int createPropertyBlob(int /*drmFD*/, const void* /*data*/, size_t /*size*/, uint32_t* /*blobId*/) noexcept {
+            return -1;
+        }
+        virtual int destroyPropertyBlob(int drmFD, uint32_t blobId) noexcept {
+            return destroyModeBlob(drmFD, blobId);
+        }
     };
 
     [[nodiscard]] AQ_TILED_ATOMIC_PRIVATE CSharedPointer<ITiledAtomicRequestOperations> createLibdrmTiledAtomicOperations() noexcept;
diff --git a/src/backend/drm/TiledDRMIntegration.cpp b/src/backend/drm/TiledDRMIntegration.cpp
index 188ee7802e8a69a2ddd4ec6af20496d8530f1217..273df157f70f8defd5a87af3f2ae24f60fc6e649 100644
--- a/src/backend/drm/TiledDRMIntegration.cpp
+++ b/src/backend/drm/TiledDRMIntegration.cpp
@@ -24,6 +24,29 @@ using namespace Aquamarine::NDRM::NTiledOutputBridge;
 using namespace Aquamarine::NDRM::NTiledRuntimeAuthority;
 using namespace Aquamarine::NDRM::NTiledScan;
 
+uint32_t Aquamarine::NDRM::NTiledDRMIntegration::groupedStartupSupportedStateBits(bool supportsExplicit) noexcept {
+    using eProp        = COutputState::eOutputStateProperties;
+    uint32_t supported = eProp::AQ_OUTPUT_STATE_DAMAGE | eProp::AQ_OUTPUT_STATE_ENABLED | eProp::AQ_OUTPUT_STATE_PRESENTATION_MODE | eProp::AQ_OUTPUT_STATE_MODE |
+        eProp::AQ_OUTPUT_STATE_FORMAT | eProp::AQ_OUTPUT_STATE_BUFFER | eProp::AQ_OUTPUT_STATE_CTM;
+    if (!supportsExplicit)
+        supported |= eProp::AQ_OUTPUT_STATE_EXPLICIT_IN_FENCE;
+    return supported;
+}
+
+eGroupedStartupOutputDisposition Aquamarine::NDRM::NTiledDRMIntegration::classifyGroupedStartupOutputRequest(const SGroupedStartupOutputRequest& request) noexcept {
+    using eProp            = COutputState::eOutputStateProperties;
+    const auto SUPPORTED   = groupedStartupSupportedStateBits(request.supportsExplicit);
+    const auto UNSUPPORTED = request.committed & ~SUPPORTED;
+    if (!request.hasMode || request.hasCustomMode || request.presentationMode != AQ_OUTPUT_PRESENTATION_VSYNC || request.adaptiveSync || request.drmFormat == DRM_FORMAT_INVALID ||
+        UNSUPPORTED != 0 || request.bufferUnimportable)
+        return eGroupedStartupOutputDisposition::REJECT;
+    if (!request.enabled)
+        return eGroupedStartupOutputDisposition::DISABLE;
+    if ((request.committed & eProp::AQ_OUTPUT_STATE_BUFFER) == 0)
+        return eGroupedStartupOutputDisposition::STATE_ONLY;
+    return eGroupedStartupOutputDisposition::ENABLE_COMMIT;
+}
+
 namespace {
     thread_local const void*                   LAST_SCAN_KEY      = nullptr;
     thread_local bool                          LAST_SCAN_COMPLETE = false;
@@ -122,15 +145,15 @@ namespace {
     };
 
     struct SBoundedGroupSelection {
-        eBoundedGroupSelection       status = eBoundedGroupSelection::NO_ALLOCATED_GROUP;
-        const CGroupProjection*      group  = nullptr;
-        CSharedPointer<SOutputMode>  mode;
-        SGroupedModeDiagnostics      diagnostics;
+        eBoundedGroupSelection      status = eBoundedGroupSelection::NO_ALLOCATED_GROUP;
+        const CGroupProjection*     group  = nullptr;
+        CSharedPointer<SOutputMode> mode;
+        SGroupedModeDiagnostics     diagnostics;
     };
 
     [[nodiscard]] bool exactBoundedAllocation(const CGroupProjection& candidate) {
-        if (candidate.projection() != eGroupProjection::MULTI_STREAM_ALLOCATED || !candidate.scanDefinition() || !candidate.allocationDefinition() ||
-            !candidate.modeDefinition() || !candidate.modeSet() || !candidate.definitionIdentity())
+        if (candidate.projection() != eGroupProjection::MULTI_STREAM_ALLOCATED || !candidate.scanDefinition() || !candidate.allocationDefinition() || !candidate.modeDefinition() ||
+            !candidate.modeSet() || !candidate.definitionIdentity())
             return false;
 
         const auto  ALLOCATION = candidate.allocation();
@@ -150,10 +173,9 @@ namespace {
             const auto& MEMBER  = TOPOLOGY.members.at(index);
             const auto& BINDING = ALLOCATION->get().bindings().at(index);
             if (!BINDING.tiled() || !sameOwner(BINDING.definition(), candidate.allocationDefinition()) || !BINDING.connector() || !BINDING.connectorIncarnation() ||
-                !BINDING.crtc() || !BINDING.crtcIncarnation() || !BINDING.primaryPlane() || !BINDING.primaryPlaneIncarnation() ||
-                BINDING.connector()->id != MEMBER.connectorId || BINDING.tileX() != MEMBER.tileHLoc || BINDING.tileY() != MEMBER.tileVLoc || BINDING.crtc()->id == 0 ||
-                BINDING.primaryPlane()->id == 0 || !connectorIds.insert(BINDING.connector()->id).second || !crtcIds.insert(BINDING.crtc()->id).second ||
-                !planeIds.insert(BINDING.primaryPlane()->id).second)
+                !BINDING.crtc() || !BINDING.crtcIncarnation() || !BINDING.primaryPlane() || !BINDING.primaryPlaneIncarnation() || BINDING.connector()->id != MEMBER.connectorId ||
+                BINDING.tileX() != MEMBER.tileHLoc || BINDING.tileY() != MEMBER.tileVLoc || BINDING.crtc()->id == 0 || BINDING.primaryPlane()->id == 0 ||
+                !connectorIds.insert(BINDING.connector()->id).second || !crtcIds.insert(BINDING.crtc()->id).second || !planeIds.insert(BINDING.primaryPlane()->id).second)
                 return false;
         }
         return connectorIds.contains(TOPOLOGY.primaryConnector);
@@ -280,9 +302,9 @@ namespace {
             // Keep completion allocation-free while covering the full 32-CRTC
             // route and fail closed instead of dropping an unlock on overflow.
             std::array<CSharedPointer<IBuffer>, GROUPED_STARTUP_RELEASE_BUFFERS> releaseBuffers;
-            size_t                                                                  releaseCount    = 0;
-            bool                                                                    releaseOverflow = false;
-            const auto REMEMBER_BUFFER = [&](const CSharedPointer<IBuffer>& buffer) noexcept {
+            size_t                                                               releaseCount    = 0;
+            bool                                                                 releaseOverflow = false;
+            const auto                                                           REMEMBER_BUFFER = [&](const CSharedPointer<IBuffer>& buffer) noexcept {
                 if (!buffer || std::ranges::any_of(std::span{releaseBuffers}.first(releaseCount), [&](const auto& existing) { return sameOwner(existing, buffer); }))
                     return;
                 if (releaseCount == releaseBuffers.size()) {
@@ -295,10 +317,9 @@ namespace {
                 const auto BUFFER = fb ? fb->buffer.lock() : CSharedPointer<IBuffer>{};
                 REMEMBER_BUFFER(BUFFER);
             };
-            bool CACHE_VALID =
-                acceptedRoute->members().size() == m_memberCount && std::ranges::all_of(acceptedRoute->members(), [&](const auto& member) {
-                    return member.primaryPlane && member.primaryPlane->id == member.primaryPlaneId && sameOwner(member.primaryPlane->back, m_pendingFramebuffer);
-                });
+            bool CACHE_VALID = acceptedRoute->members().size() == m_memberCount && std::ranges::all_of(acceptedRoute->members(), [&](const auto& member) {
+                                   return member.primaryPlane && member.primaryPlane->id == member.primaryPlaneId && sameOwner(member.primaryPlane->back, m_pendingFramebuffer);
+                               });
             if (!CACHE_VALID)
                 m_terminalFailure = true;
             if (CACHE_VALID) {
@@ -428,6 +449,9 @@ class CTiledDRMIntegrationRegistry::CImpl {
         std::vector<SDRMFormat>                       renderFormats;
         bool                                          initialAccepted = false;
         bool                                          terminalFailure = false;
+        bool                                          outputDisabled  = false;
+        std::optional<Hyprutils::Math::Mat3x3>        appliedCtm;
+        uint32_t                                      appliedCtmBlob = 0;
     };
 
     struct SEntry {
@@ -1417,9 +1441,8 @@ SGroupedStartupPrepareResult CTiledDRMIntegrationRegistry::prepareGroupedStartup
             bridgeCurrent = ENTRY->second.outputBridge->withCurrentOwner(current, [&](const POutputBridgeObservation& observation) {
                 const auto SELECTED = selectBoundedGroup(observation);
                 if (SELECTED.status != eBoundedGroupSelection::SELECTED || !SELECTED.group || !SELECTED.mode) {
-                    result.status = SELECTED.status == eBoundedGroupSelection::MULTIPLE_ALLOCATED_GROUPS ?
-                        eGroupedStartupPrepareStatus::MULTIPLE_ALLOCATED_GROUPS_UNSUPPORTED :
-                        eGroupedStartupPrepareStatus::UNSUPPORTED_TOPOLOGY;
+                    result.status = SELECTED.status == eBoundedGroupSelection::MULTIPLE_ALLOCATED_GROUPS ? eGroupedStartupPrepareStatus::MULTIPLE_ALLOCATED_GROUPS_UNSUPPORTED :
+                                                                                                           eGroupedStartupPrepareStatus::UNSUPPORTED_TOPOLOGY;
                     return;
                 }
                 const auto* const selected = SELECTED.group;
@@ -1566,6 +1589,71 @@ std::vector<SDRMFormat> CTiledDRMIntegrationRegistry::groupedStartupRenderFormat
     return groupedStartupRenderFormatsKey(backend.impl_, connector);
 }
 
+namespace {
+    bool applyGroupedStartupMemberPower(std::vector<CSharedPointer<SDRMConnector>>& members, bool& outputDisabled, bool& initialAccepted, bool enabled) noexcept {
+        if (members.empty())
+            return false;
+        for (const auto& MEMBER : members) {
+            if (!MEMBER)
+                return false;
+        }
+        outputDisabled = !enabled;
+        if (!enabled)
+            initialAccepted = false;
+        for (const auto& MEMBER : members) {
+            if (MEMBER->output)
+                MEMBER->output->enabledState = enabled;
+        }
+        return true;
+    }
+
+    std::vector<SGroupedStartupMemberPower> memberPowerSnapshot(const std::vector<CSharedPointer<SDRMConnector>>& members, bool outputDisabled) {
+        std::vector<SGroupedStartupMemberPower> result;
+        result.reserve(members.size());
+        for (const auto& MEMBER : members) {
+            if (!MEMBER)
+                continue;
+            const bool enabled = !outputDisabled && (!MEMBER->output || MEMBER->output->enabledState);
+            result.push_back({.connectorId = MEMBER->id, .enabled = enabled});
+        }
+        return result;
+    }
+
+    std::vector<SGroupedStartupMemberCtm> memberCtmSnapshot(const std::vector<CSharedPointer<SDRMConnector>>& members, const std::optional<Hyprutils::Math::Mat3x3>& appliedCtm) {
+        std::vector<SGroupedStartupMemberCtm> result;
+        result.reserve(members.size());
+        const auto MATRIX   = appliedCtm.value_or(Hyprutils::Math::Mat3x3::identity());
+        const bool APPLIED  = appliedCtm.has_value();
+        const bool IDENTITY = !APPLIED || MATRIX == Hyprutils::Math::Mat3x3::identity();
+        for (const auto& MEMBER : members) {
+            if (!MEMBER)
+                continue;
+            result.push_back({
+                .connectorId = MEMBER->id,
+                .crtcId      = MEMBER->crtc ? MEMBER->crtc->id : 0,
+                .applied     = APPLIED,
+                .identity    = IDENTITY,
+                .stateKnown  = APPLIED && MEMBER->crtc && MEMBER->crtc->atomic.ctmStateKnown,
+                .matrix      = MATRIX,
+            });
+        }
+        return result;
+    }
+
+    void recordGroupedStartupCtm(std::vector<CSharedPointer<SDRMConnector>>& members, std::optional<Hyprutils::Math::Mat3x3>& appliedCtm, uint32_t& appliedCtmBlob,
+                                 const Hyprutils::Math::Mat3x3& matrix, uint32_t blobId) noexcept {
+        appliedCtm     = matrix;
+        appliedCtmBlob = blobId;
+        for (const auto& MEMBER : members) {
+            if (!MEMBER || !MEMBER->crtc)
+                continue;
+            if (blobId)
+                MEMBER->crtc->atomic.ctm = blobId;
+            MEMBER->crtc->atomic.ctmStateKnown = true;
+        }
+    }
+}
+
 bool CTiledDRMIntegrationRegistry::groupedStartupStateOnlyCurrentKey(const void* key, const CSharedPointer<SDRMConnector>& connector,
                                                                      const CSharedPointer<SOutputMode>& outputMode) noexcept {
     const auto THREAD = m_impl->registeredThread(key);
@@ -1579,6 +1667,8 @@ bool CTiledDRMIntegrationRegistry::groupedStartupStateOnlyCurrentKey(const void*
     if (!sameOwner(STARTUP.primary, connector) || STARTUP.terminalFailure || (STARTUP.concreteSink && STARTUP.concreteSink->terminalFailure()) || !STARTUP.modeSet ||
         !STARTUP.modeSet->logicalModeFor(outputMode))
         return false;
+    if (STARTUP.outputDisabled && !applyGroupedStartupMemberPower(STARTUP.members, STARTUP.outputDisabled, STARTUP.initialAccepted, true))
+        return false;
 
     const auto SHADOW            = ENTRY->second.allocationShadow;
     bool       allocationCurrent = false;
@@ -1609,8 +1699,9 @@ bool CTiledDRMIntegrationRegistry::groupedStartupActive(const CWeakPointer<CDRMB
 SGroupedStartupCommitResult CTiledDRMIntegrationRegistry::commitGroupedStartupKey(const void* key, const CWeakPointer<CDRMBackend>& backend,
                                                                                   const CSharedPointer<SDRMConnector>& connector, const CSharedPointer<SOutputMode>& outputMode,
                                                                                   const CSharedPointer<CDRMFB>& framebuffer, const CSharedPointer<IBuffer>& buffer, bool onlyTest,
-                                                                                  uintptr_t transactionId, uint64_t deadlineNs,
-                                                                                  const TGroupedTestKMSCaptureProvider& captureProvider) noexcept {
+                                                                                  bool enabled, uintptr_t transactionId, uint64_t deadlineNs,
+                                                                                  const TGroupedTestKMSCaptureProvider&  captureProvider,
+                                                                                  std::optional<Hyprutils::Math::Mat3x3> ctm) noexcept {
     SGroupedStartupCommitResult result;
     const auto                  THREAD = m_impl->registeredThread(key);
     if (!THREAD || *THREAD != std::this_thread::get_id() || !connector)
@@ -1623,9 +1714,62 @@ SGroupedStartupCommitResult CTiledDRMIntegrationRegistry::commitGroupedStartupKe
     if (STARTUP.concreteSink && STARTUP.concreteSink->terminalFailure())
         STARTUP.terminalFailure = true;
     result.claimed = sameOwner(STARTUP.primary, connector) || std::ranges::any_of(STARTUP.members, [&](const auto& member) { return sameOwner(member, connector); });
-    if (!result.claimed || !sameOwner(STARTUP.primary, connector) || STARTUP.terminalFailure || ENTRY->second.detachBegun || !outputMode || !STARTUP.modeSet ||
-        !STARTUP.modeSet->logicalModeFor(outputMode) || !captureProvider || !ENTRY->second.outputBridge || !ENTRY->second.groupedRuntime || !ENTRY->second.allocationShadow ||
-        m_impl->mutationBusy(ENTRY->second))
+    if (!result.claimed || !sameOwner(STARTUP.primary, connector) || STARTUP.terminalFailure || ENTRY->second.detachBegun)
+        return result;
+
+    const auto applyCtm = [&](const Hyprutils::Math::Mat3x3& matrix) -> bool {
+        if (STARTUP.members.empty() || !ENTRY->second.groupedRuntime)
+            return false;
+        const auto APPLIED = ENTRY->second.groupedRuntime->submitCtm(STARTUP.members, matrix, onlyTest, STARTUP.appliedCtmBlob);
+        if (!APPLIED.success)
+            return false;
+        if (!onlyTest)
+            recordGroupedStartupCtm(STARTUP.members, STARTUP.appliedCtm, STARTUP.appliedCtmBlob, matrix, APPLIED.blobId);
+        return true;
+    };
+
+    if (!enabled) {
+        if (STARTUP.members.empty() || m_impl->mutationBusy(ENTRY->second) || !ENTRY->second.groupedRuntime)
+            return result;
+        const auto DISABLE = ENTRY->second.groupedRuntime->submitDisable(STARTUP.members, onlyTest);
+        if (DISABLE.attempted && !DISABLE.success)
+            return result;
+        if (onlyTest) {
+            result.success = true;
+            return result;
+        }
+        if (!applyGroupedStartupMemberPower(STARTUP.members, STARTUP.outputDisabled, STARTUP.initialAccepted, false))
+            return result;
+        static_cast<void>(ENTRY->second.groupedRuntime->quiesceAll(eTiledQuiesceReason::OUTPUT_DISABLED));
+        if (STARTUP.identity)
+            static_cast<void>(ENTRY->second.groupedRuntime->retire(STARTUP.identity, eTiledRetirementProof::ATOMIC_DISABLE_COMPLETED));
+        result.success = true;
+        return result;
+    }
+
+    if (STARTUP.outputDisabled) {
+        if (!ENTRY->second.groupedRuntime)
+            return result;
+        const auto RESTORE = ENTRY->second.groupedRuntime->submitRestore(STARTUP.members, onlyTest);
+        if (RESTORE.attempted && !RESTORE.success)
+            return result;
+        if (!onlyTest && !applyGroupedStartupMemberPower(STARTUP.members, STARTUP.outputDisabled, STARTUP.initialAccepted, true))
+            return result;
+        if (!onlyTest && STARTUP.appliedCtm && !ctm && !applyCtm(*STARTUP.appliedCtm))
+            return result;
+    }
+
+    if (ctm && !applyCtm(*ctm))
+        return result;
+
+    const bool POWER_ONLY = !framebuffer && !buffer && transactionId == 0;
+    if (POWER_ONLY) {
+        result.success = onlyTest || !STARTUP.outputDisabled;
+        return result;
+    }
+
+    if (!outputMode || !STARTUP.modeSet || !STARTUP.modeSet->logicalModeFor(outputMode) || !captureProvider || !ENTRY->second.outputBridge || !ENTRY->second.groupedRuntime ||
+        !ENTRY->second.allocationShadow || m_impl->mutationBusy(ENTRY->second))
         return result;
     if ((!framebuffer) != (!buffer) || (!onlyTest && (!framebuffer || !buffer || transactionId == 0 || deadlineNs == 0)))
         return result;
@@ -1704,14 +1848,68 @@ SGroupedStartupCommitResult CTiledDRMIntegrationRegistry::commitGroupedStartupKe
     }
     if (!allocationCurrent || !bridgeCurrent)
         result.success = false;
+    if (result.success && !onlyTest && result.initial && STARTUP.appliedCtm && !applyCtm(*STARTUP.appliedCtm))
+        result.success = false;
     return result;
 }
 
 SGroupedStartupCommitResult CTiledDRMIntegrationRegistry::commitGroupedStartup(const CWeakPointer<CDRMBackend>& backend, const CSharedPointer<SDRMConnector>& connector,
                                                                                const CSharedPointer<SOutputMode>& outputMode, const CSharedPointer<CDRMFB>& framebuffer,
-                                                                               const CSharedPointer<IBuffer>& buffer, bool onlyTest, uintptr_t transactionId, uint64_t deadlineNs,
-                                                                               const TGroupedTestKMSCaptureProvider& captureProvider) noexcept {
-    return commitGroupedStartupKey(backend.impl_, backend, connector, outputMode, framebuffer, buffer, onlyTest, transactionId, deadlineNs, captureProvider);
+                                                                               const CSharedPointer<IBuffer>& buffer, bool onlyTest, bool enabled, uintptr_t transactionId,
+                                                                               uint64_t deadlineNs, const TGroupedTestKMSCaptureProvider& captureProvider,
+                                                                               std::optional<Hyprutils::Math::Mat3x3> ctm) noexcept {
+    return commitGroupedStartupKey(backend.impl_, backend, connector, outputMode, framebuffer, buffer, onlyTest, enabled, transactionId, deadlineNs, captureProvider,
+                                   std::move(ctm));
+}
+
+std::vector<SGroupedStartupMemberPower> CTiledDRMIntegrationRegistry::groupedStartupMemberPowerKey(const void* key) noexcept {
+    const auto THREAD = m_impl->registeredThread(key);
+    if (!THREAD || *THREAD != std::this_thread::get_id())
+        return {};
+    const auto ENTRY = m_impl->entries.find(key);
+    if (ENTRY == m_impl->entries.end() || !ENTRY->second.groupedStartup)
+        return {};
+    return memberPowerSnapshot(ENTRY->second.groupedStartup->members, ENTRY->second.groupedStartup->outputDisabled);
+}
+
+std::vector<SGroupedStartupMemberPower> CTiledDRMIntegrationRegistry::groupedStartupMemberPower(const CWeakPointer<CDRMBackend>& backend) noexcept {
+    return groupedStartupMemberPowerKey(backend.impl_);
+}
+
+std::vector<SGroupedStartupMemberCtm> CTiledDRMIntegrationRegistry::groupedStartupMemberCtmKey(const void* key) noexcept {
+    const auto THREAD = m_impl->registeredThread(key);
+    if (!THREAD || *THREAD != std::this_thread::get_id())
+        return {};
+    const auto ENTRY = m_impl->entries.find(key);
+    if (ENTRY == m_impl->entries.end() || !ENTRY->second.groupedStartup)
+        return {};
+    return memberCtmSnapshot(ENTRY->second.groupedStartup->members, ENTRY->second.groupedStartup->appliedCtm);
+}
+
+std::vector<SGroupedStartupMemberCtm> CTiledDRMIntegrationRegistry::groupedStartupMemberCtm(const CWeakPointer<CDRMBackend>& backend) noexcept {
+    return groupedStartupMemberCtmKey(backend.impl_);
+}
+
+bool CTiledDRMIntegrationRegistry::installTestGroupedStartup(const PBackendLifetime& lifetime, const CSharedPointer<SDRMConnector>& primary,
+                                                             std::vector<CSharedPointer<SDRMConnector>> members) noexcept {
+    const void* key    = lifetime.impl_;
+    const auto  THREAD = m_impl->registeredThread(key);
+    if (!THREAD || *THREAD != std::this_thread::get_id() || !primary || members.empty())
+        return false;
+    const auto ENTRY = m_impl->entries.find(key);
+    if (ENTRY == m_impl->entries.end() || ENTRY->second.detachBegun || !ENTRY->second.groupedRuntime || ENTRY->second.groupedStartup)
+        return false;
+    if (!std::ranges::any_of(members, [&](const auto& member) { return sameOwner(member, primary); }))
+        return false;
+    for (const auto& MEMBER : members) {
+        if (!MEMBER || MEMBER->id == 0)
+            return false;
+    }
+    ENTRY->second.groupedStartup.emplace(CImpl::SGroupedStartup{
+        .primary = primary,
+        .members = std::move(members),
+    });
+    return applyGroupedStartupMemberPower(ENTRY->second.groupedStartup->members, ENTRY->second.groupedStartup->outputDisabled, ENTRY->second.groupedStartup->initialAccepted, true);
 }
 
 SGroupedInitialFrameResult CTiledDRMIntegrationRegistry::submitCurrentGroupedInitialFrameKey(const void* key, const POutputBridgeObservation& expected,
@@ -2160,11 +2358,24 @@ bool Aquamarine::NDRM::NTiledDRMIntegration::groupedStartupStateOnlyCurrent(cons
 
 SGroupedStartupCommitResult Aquamarine::NDRM::NTiledDRMIntegration::commitGroupedStartup(const CWeakPointer<CDRMBackend>& backend, const CSharedPointer<SDRMConnector>& connector,
                                                                                          const CSharedPointer<SOutputMode>& outputMode, const CSharedPointer<CDRMFB>& framebuffer,
-                                                                                         const CSharedPointer<IBuffer>& buffer, bool onlyTest, uintptr_t transactionId,
-                                                                                         uint64_t deadlineNs, const TGroupedTestKMSCaptureProvider& captureProvider) noexcept {
+                                                                                         const CSharedPointer<IBuffer>& buffer, bool onlyTest, bool enabled,
+                                                                                         uintptr_t transactionId, uint64_t deadlineNs,
+                                                                                         const TGroupedTestKMSCaptureProvider&  captureProvider,
+                                                                                         std::optional<Hyprutils::Math::Mat3x3> ctm) noexcept {
     auto* const REGISTRY = productionRegistryIfCreated();
-    return REGISTRY ? REGISTRY->commitGroupedStartup(backend, connector, outputMode, framebuffer, buffer, onlyTest, transactionId, deadlineNs, captureProvider) :
-                      SGroupedStartupCommitResult{};
+    return REGISTRY ?
+        REGISTRY->commitGroupedStartup(backend, connector, outputMode, framebuffer, buffer, onlyTest, enabled, transactionId, deadlineNs, captureProvider, std::move(ctm)) :
+        SGroupedStartupCommitResult{};
+}
+
+std::vector<SGroupedStartupMemberPower> Aquamarine::NDRM::NTiledDRMIntegration::groupedStartupMemberPower(const CWeakPointer<CDRMBackend>& backend) noexcept {
+    auto* const REGISTRY = productionRegistryIfCreated();
+    return REGISTRY ? REGISTRY->groupedStartupMemberPower(backend) : std::vector<SGroupedStartupMemberPower>{};
+}
+
+std::vector<SGroupedStartupMemberCtm> Aquamarine::NDRM::NTiledDRMIntegration::groupedStartupMemberCtm(const CWeakPointer<CDRMBackend>& backend) noexcept {
+    auto* const REGISTRY = productionRegistryIfCreated();
+    return REGISTRY ? REGISTRY->groupedStartupMemberCtm(backend) : std::vector<SGroupedStartupMemberCtm>{};
 }
 
 bool Aquamarine::NDRM::NTiledDRMIntegration::groupedStartupActive(const CWeakPointer<CDRMBackend>& backend) noexcept {
diff --git a/src/backend/drm/TiledDRMIntegration.hpp b/src/backend/drm/TiledDRMIntegration.hpp
index 8f0539b99ec0a63e65634f24fa6913b5eb3fd1b4..a5b43f54efadecaa3f893a7852d449e59e236547 100644
--- a/src/backend/drm/TiledDRMIntegration.hpp
+++ b/src/backend/drm/TiledDRMIntegration.hpp
@@ -8,6 +8,7 @@
 
 #include <cstdint>
 #include <functional>
+#include <optional>
 #include <span>
 #include <thread>
 #include <vector>
@@ -108,6 +109,43 @@ namespace Aquamarine::NDRM::NTiledDRMIntegration { // NOLINT(readability-identif
         SGroupedFrameResult        frame;
     };
 
+    enum class eGroupedStartupOutputDisposition : uint8_t {
+        REJECT = 0,
+        STATE_ONLY,
+        ENABLE_COMMIT,
+        DISABLE,
+    };
+
+    struct AQ_TILED_DRM_PRIVATE SGroupedStartupOutputRequest {
+        uint32_t                            committed          = 0;
+        bool                                enabled            = false;
+        bool                                adaptiveSync       = false;
+        Aquamarine::eOutputPresentationMode presentationMode   = Aquamarine::AQ_OUTPUT_PRESENTATION_VSYNC;
+        uint32_t                            drmFormat          = DRM_FORMAT_INVALID;
+        bool                                hasMode            = false;
+        bool                                hasCustomMode      = false;
+        bool                                hasBuffer          = false;
+        bool                                bufferUnimportable = false;
+        bool                                supportsExplicit   = false;
+    };
+
+    struct AQ_TILED_DRM_PRIVATE SGroupedStartupMemberPower {
+        uint32_t connectorId = 0;
+        bool     enabled     = false;
+    };
+
+    struct AQ_TILED_DRM_PRIVATE SGroupedStartupMemberCtm {
+        uint32_t                connectorId = 0;
+        uint32_t                crtcId      = 0;
+        bool                    applied     = false;
+        bool                    identity    = true;
+        bool                    stateKnown  = false;
+        Hyprutils::Math::Mat3x3 matrix      = Hyprutils::Math::Mat3x3::identity();
+    };
+
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE uint32_t                         groupedStartupSupportedStateBits(bool supportsExplicit) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE eGroupedStartupOutputDisposition classifyGroupedStartupOutputRequest(const SGroupedStartupOutputRequest& request) noexcept;
+
     enum class eScanStageResult : uint8_t {
         PREPARED = 0,
         INCOMPLETE,
@@ -276,38 +314,41 @@ namespace Aquamarine::NDRM::NTiledDRMIntegration { // NOLINT(readability-identif
         explicit CTiledDRMIntegrationRegistry(std::thread::id eventThread = std::this_thread::get_id());
         ~CTiledDRMIntegrationRegistry() noexcept;
 
-        CTiledDRMIntegrationRegistry(const CTiledDRMIntegrationRegistry&)                       = delete;
-        CTiledDRMIntegrationRegistry(CTiledDRMIntegrationRegistry&&)                            = delete;
-        CTiledDRMIntegrationRegistry&            operator=(const CTiledDRMIntegrationRegistry&) = delete;
-        CTiledDRMIntegrationRegistry&            operator=(CTiledDRMIntegrationRegistry&&)      = delete;
+        CTiledDRMIntegrationRegistry(const CTiledDRMIntegrationRegistry&)                         = delete;
+        CTiledDRMIntegrationRegistry(CTiledDRMIntegrationRegistry&&)                              = delete;
+        CTiledDRMIntegrationRegistry&              operator=(const CTiledDRMIntegrationRegistry&) = delete;
+        CTiledDRMIntegrationRegistry&              operator=(CTiledDRMIntegrationRegistry&&)      = delete;
 
-        [[nodiscard]] SBeginConnectorScanResult  beginConnectorScan(const CSharedPointer<CDRMBackend>& backend);
-        [[nodiscard]] eScanStageResult           stageConnectorScan(const PConnectorScan& scan, eScanCompleteness completeness, std::vector<SConnectorView> connectors) noexcept;
-        [[nodiscard]] eScanCommitResult          commitConnectorScan(const PConnectorScan& scan) noexcept;
-        void                                     abortConnectorScan(const PConnectorScan& scan) noexcept;
+        [[nodiscard]] SBeginConnectorScanResult    beginConnectorScan(const CSharedPointer<CDRMBackend>& backend);
+        [[nodiscard]] eScanStageResult             stageConnectorScan(const PConnectorScan& scan, eScanCompleteness completeness, std::vector<SConnectorView> connectors) noexcept;
+        [[nodiscard]] eScanCommitResult            commitConnectorScan(const PConnectorScan& scan) noexcept;
+        void                                       abortConnectorScan(const PConnectorScan& scan) noexcept;
 
-        [[nodiscard]] eAllocationCaptureResult   captureAllocationShadow(const CSharedPointer<CDRMBackend>& backend, SAllocationCapture capture) noexcept;
-        [[nodiscard]] PAllocationShadow          currentAllocationShadow(const CWeakPointer<CDRMBackend>& backend) noexcept;
-        [[nodiscard]] bool                       withCurrentAllocationShadow(const CWeakPointer<CDRMBackend>& backend, const PAllocationShadow& expected,
-                                                                             const std::function<void(const CPlan&, const SCurrentState&)>& callback) noexcept;
-        [[nodiscard]] bool                       withCurrentOutputBridge(const CWeakPointer<CDRMBackend>& backend, const PAllocationShadow& expected,
-                                                                         const std::function<void(const POutputBridgeObservation&)>& callback) noexcept;
-        [[nodiscard]] SGroupedTestModesetResult  executeCurrentGroupedTestModeset(const CWeakPointer<CDRMBackend>& backend, const POutputBridgeObservation& expected,
-                                                                                  const NTiledAtomic::PResolvedTiledAtomicReplacement& replacement,
-                                                                                  const PScanGroupDefinition& group, const CSharedPointer<SOutputMode>& outputMode) noexcept;
-        [[nodiscard]] SGroupedTestProbeResult    probeCurrentGroupedTestModeset(const CWeakPointer<CDRMBackend>&      backend,
-                                                                                const TGroupedTestKMSCaptureProvider& captureProvider) noexcept;
+        [[nodiscard]] eAllocationCaptureResult     captureAllocationShadow(const CSharedPointer<CDRMBackend>& backend, SAllocationCapture capture) noexcept;
+        [[nodiscard]] PAllocationShadow            currentAllocationShadow(const CWeakPointer<CDRMBackend>& backend) noexcept;
+        [[nodiscard]] bool                         withCurrentAllocationShadow(const CWeakPointer<CDRMBackend>& backend, const PAllocationShadow& expected,
+                                                                               const std::function<void(const CPlan&, const SCurrentState&)>& callback) noexcept;
+        [[nodiscard]] bool                         withCurrentOutputBridge(const CWeakPointer<CDRMBackend>& backend, const PAllocationShadow& expected,
+                                                                           const std::function<void(const POutputBridgeObservation&)>& callback) noexcept;
+        [[nodiscard]] SGroupedTestModesetResult    executeCurrentGroupedTestModeset(const CWeakPointer<CDRMBackend>& backend, const POutputBridgeObservation& expected,
+                                                                                    const NTiledAtomic::PResolvedTiledAtomicReplacement& replacement,
+                                                                                    const PScanGroupDefinition& group, const CSharedPointer<SOutputMode>& outputMode) noexcept;
+        [[nodiscard]] SGroupedTestProbeResult      probeCurrentGroupedTestModeset(const CWeakPointer<CDRMBackend>&      backend,
+                                                                                  const TGroupedTestKMSCaptureProvider& captureProvider) noexcept;
         [[nodiscard]] SGroupedStartupPrepareResult prepareGroupedStartup(const CWeakPointer<CDRMBackend>& backend, std::span<const CSharedPointer<SDRMConnector>> connectors,
                                                                          const TGroupedTestKMSCaptureProvider& captureProvider) noexcept;
         [[nodiscard]] eGroupedStartupConnectorRole groupedStartupConnectorRole(const CWeakPointer<CDRMBackend>& backend, const CSharedPointer<SDRMConnector>& connector) noexcept;
         [[nodiscard]] std::vector<SDRMFormat>      groupedStartupRenderFormats(const CWeakPointer<CDRMBackend>& backend, const CSharedPointer<SDRMConnector>& connector) noexcept;
-        [[nodiscard]] bool                        groupedStartupStateOnlyCurrent(const CWeakPointer<CDRMBackend>& backend, const CSharedPointer<SDRMConnector>& connector,
-                                                                                const CSharedPointer<SOutputMode>& outputMode) noexcept;
+        [[nodiscard]] bool                         groupedStartupStateOnlyCurrent(const CWeakPointer<CDRMBackend>& backend, const CSharedPointer<SDRMConnector>& connector,
+                                                                                  const CSharedPointer<SOutputMode>& outputMode) noexcept;
         [[nodiscard]] SGroupedStartupCommitResult  commitGroupedStartup(const CWeakPointer<CDRMBackend>& backend, const CSharedPointer<SDRMConnector>& connector,
                                                                         const CSharedPointer<SOutputMode>& outputMode, const CSharedPointer<CDRMFB>& framebuffer,
-                                                                        const CSharedPointer<IBuffer>& buffer, bool onlyTest, uintptr_t transactionId, uint64_t deadlineNs,
-                                                                        const TGroupedTestKMSCaptureProvider& captureProvider) noexcept;
-        [[nodiscard]] bool                         groupedStartupActive(const CWeakPointer<CDRMBackend>& backend) noexcept;
+                                                                        const CSharedPointer<IBuffer>& buffer, bool onlyTest, bool enabled, uintptr_t transactionId,
+                                                                        uint64_t deadlineNs, const TGroupedTestKMSCaptureProvider& captureProvider,
+                                                                        std::optional<Hyprutils::Math::Mat3x3> ctm = {}) noexcept;
+        [[nodiscard]] std::vector<SGroupedStartupMemberPower> groupedStartupMemberPower(const CWeakPointer<CDRMBackend>& backend) noexcept;
+        [[nodiscard]] std::vector<SGroupedStartupMemberCtm>   groupedStartupMemberCtm(const CWeakPointer<CDRMBackend>& backend) noexcept;
+        [[nodiscard]] bool                                    groupedStartupActive(const CWeakPointer<CDRMBackend>& backend) noexcept;
         [[nodiscard]] SGroupedInitialFrameResult submitCurrentGroupedInitialFrame(const CWeakPointer<CDRMBackend>& backend, const POutputBridgeObservation& expected,
                                                                                   const NTiledAtomic::PResolvedTiledAtomicReplacement& replacement,
                                                                                   const PScanGroupDefinition& group, const CSharedPointer<SOutputMode>& outputMode,
@@ -333,35 +374,40 @@ namespace Aquamarine::NDRM::NTiledDRMIntegration { // NOLINT(readability-identif
         void failNextAttachmentForTest() noexcept;
 
       private:
-        [[nodiscard]] SBeginConnectorScanResult  beginAttached(const void* key);
-        [[nodiscard]] eSessionResult             rotateSessionKey(const void* key) noexcept;
-        [[nodiscard]] eDetachResult              beginDetachKey(const void* key, eDetachReason reason) noexcept;
-        [[nodiscard]] eDetachResult              terminalizeDetachedKey(const void* key, eDetachTerminalProof proof) noexcept;
-        [[nodiscard]] eDetachResult              eraseTerminalizedKey(const void* key) noexcept;
-        [[nodiscard]] bool                       attachedKey(const void* key) const noexcept;
-        [[nodiscard]] PCommittedScan             currentCompleteKey(const void* key) noexcept;
-        [[nodiscard]] SScanOutcome               outcomeKey(const void* key) noexcept;
-        [[nodiscard]] eAllocationCaptureResult   captureAllocationKey(const void* key, SAllocationCapture capture) noexcept;
-        [[nodiscard]] PAllocationShadow          currentAllocationKey(const void* key) noexcept;
-        [[nodiscard]] bool                       withCurrentAllocationKey(const void* key, const PAllocationShadow& expected,
-                                                                          const std::function<void(const CPlan&, const SCurrentState&)>& callback) noexcept;
-        [[nodiscard]] bool                       withCurrentOutputBridgeKey(const void* key, const PAllocationShadow& expected,
-                                                                            const std::function<void(const POutputBridgeObservation&)>& callback) noexcept;
-        [[nodiscard]] SGroupedTestModesetResult  executeCurrentGroupedTestModesetKey(const void* key, const POutputBridgeObservation& expected,
-                                                                                     const NTiledAtomic::PResolvedTiledAtomicReplacement& replacement,
-                                                                                     const PScanGroupDefinition& group, const CSharedPointer<SOutputMode>& outputMode) noexcept;
-        [[nodiscard]] SGroupedTestProbeResult    probeCurrentGroupedTestModesetKey(const void* key, const TGroupedTestKMSCaptureProvider& captureProvider) noexcept;
+        [[nodiscard]] SBeginConnectorScanResult    beginAttached(const void* key);
+        [[nodiscard]] eSessionResult               rotateSessionKey(const void* key) noexcept;
+        [[nodiscard]] eDetachResult                beginDetachKey(const void* key, eDetachReason reason) noexcept;
+        [[nodiscard]] eDetachResult                terminalizeDetachedKey(const void* key, eDetachTerminalProof proof) noexcept;
+        [[nodiscard]] eDetachResult                eraseTerminalizedKey(const void* key) noexcept;
+        [[nodiscard]] bool                         attachedKey(const void* key) const noexcept;
+        [[nodiscard]] PCommittedScan               currentCompleteKey(const void* key) noexcept;
+        [[nodiscard]] SScanOutcome                 outcomeKey(const void* key) noexcept;
+        [[nodiscard]] eAllocationCaptureResult     captureAllocationKey(const void* key, SAllocationCapture capture) noexcept;
+        [[nodiscard]] PAllocationShadow            currentAllocationKey(const void* key) noexcept;
+        [[nodiscard]] bool                         withCurrentAllocationKey(const void* key, const PAllocationShadow& expected,
+                                                                            const std::function<void(const CPlan&, const SCurrentState&)>& callback) noexcept;
+        [[nodiscard]] bool                         withCurrentOutputBridgeKey(const void* key, const PAllocationShadow& expected,
+                                                                              const std::function<void(const POutputBridgeObservation&)>& callback) noexcept;
+        [[nodiscard]] SGroupedTestModesetResult    executeCurrentGroupedTestModesetKey(const void* key, const POutputBridgeObservation& expected,
+                                                                                       const NTiledAtomic::PResolvedTiledAtomicReplacement& replacement,
+                                                                                       const PScanGroupDefinition& group, const CSharedPointer<SOutputMode>& outputMode) noexcept;
+        [[nodiscard]] SGroupedTestProbeResult      probeCurrentGroupedTestModesetKey(const void* key, const TGroupedTestKMSCaptureProvider& captureProvider) noexcept;
         [[nodiscard]] SGroupedStartupPrepareResult prepareGroupedStartupKey(const void* key, const CWeakPointer<CDRMBackend>& backend,
                                                                             std::span<const CSharedPointer<SDRMConnector>> connectors,
                                                                             const TGroupedTestKMSCaptureProvider&          captureProvider) noexcept;
         [[nodiscard]] eGroupedStartupConnectorRole groupedStartupConnectorRoleKey(const void* key, const CSharedPointer<SDRMConnector>& connector) noexcept;
         [[nodiscard]] std::vector<SDRMFormat>      groupedStartupRenderFormatsKey(const void* key, const CSharedPointer<SDRMConnector>& connector) noexcept;
-        [[nodiscard]] bool groupedStartupStateOnlyCurrentKey(const void* key, const CSharedPointer<SDRMConnector>& connector,
-                                                             const CSharedPointer<SOutputMode>& outputMode) noexcept;
+        [[nodiscard]] bool                         groupedStartupStateOnlyCurrentKey(const void* key, const CSharedPointer<SDRMConnector>& connector,
+                                                                                     const CSharedPointer<SOutputMode>& outputMode) noexcept;
         [[nodiscard]] SGroupedStartupCommitResult commitGroupedStartupKey(const void* key, const CWeakPointer<CDRMBackend>& backend, const CSharedPointer<SDRMConnector>& connector,
                                                                           const CSharedPointer<SOutputMode>& outputMode, const CSharedPointer<CDRMFB>& framebuffer,
-                                                                          const CSharedPointer<IBuffer>& buffer, bool onlyTest, uintptr_t transactionId, uint64_t deadlineNs,
-                                                                          const TGroupedTestKMSCaptureProvider& captureProvider) noexcept;
+                                                                          const CSharedPointer<IBuffer>& buffer, bool onlyTest, bool enabled, uintptr_t transactionId,
+                                                                          uint64_t deadlineNs, const TGroupedTestKMSCaptureProvider& captureProvider,
+                                                                          std::optional<Hyprutils::Math::Mat3x3> ctm = {}) noexcept;
+        [[nodiscard]] std::vector<SGroupedStartupMemberPower> groupedStartupMemberPowerKey(const void* key) noexcept;
+        [[nodiscard]] std::vector<SGroupedStartupMemberCtm>   groupedStartupMemberCtmKey(const void* key) noexcept;
+        [[nodiscard]] bool                                    installTestGroupedStartup(const PBackendLifetime& lifetime, const CSharedPointer<SDRMConnector>& primary,
+                                                                                        std::vector<CSharedPointer<SDRMConnector>> members) noexcept;
         [[nodiscard]] SGroupedInitialFrameResult submitCurrentGroupedInitialFrameKey(const void* key, const POutputBridgeObservation& expected,
                                                                                      const NTiledAtomic::PResolvedTiledAtomicReplacement& replacement,
                                                                                      const PScanGroupDefinition& group, const CSharedPointer<SOutputMode>& outputMode,
@@ -407,44 +453,47 @@ namespace Aquamarine::NDRM::NTiledDRMIntegration { // NOLINT(readability-identif
         friend class ::Aquamarine::NDRM::NTiledDRMIntegrationTest::CFixtureAuthority;
     };
 
-    [[nodiscard]] AQ_TILED_DRM_PRIVATE SBeginConnectorScanResult beginConnectorScan(const CSharedPointer<CDRMBackend>& backend);
-    [[nodiscard]] AQ_TILED_DRM_PRIVATE eScanStageResult          stageConnectorScan(const PConnectorScan& scan, eScanCompleteness completeness,
-                                                                                    std::vector<SConnectorView> connectors) noexcept;
-    [[nodiscard]] AQ_TILED_DRM_PRIVATE eScanCommitResult         commitConnectorScan(const PConnectorScan& scan) noexcept;
-    AQ_TILED_DRM_PRIVATE void                                    abortConnectorScan(const PConnectorScan& scan) noexcept;
-    [[nodiscard]] AQ_TILED_DRM_PRIVATE eAllocationCaptureResult  captureAllocationShadow(const CSharedPointer<CDRMBackend>& backend, SAllocationCapture capture) noexcept;
-    [[nodiscard]] AQ_TILED_DRM_PRIVATE PAllocationShadow         currentAllocationShadow(const CWeakPointer<CDRMBackend>& backend) noexcept;
-    [[nodiscard]] AQ_TILED_DRM_PRIVATE bool                      withCurrentAllocationShadow(const CWeakPointer<CDRMBackend>& backend, const PAllocationShadow& expected,
-                                                                                             const std::function<void(const CPlan&, const SCurrentState&)>& callback) noexcept;
-    [[nodiscard]] AQ_TILED_DRM_PRIVATE bool                      withCurrentOutputBridge(const CWeakPointer<CDRMBackend>& backend, const PAllocationShadow& expected,
-                                                                                         const std::function<void(const POutputBridgeObservation&)>& callback) noexcept;
-    [[nodiscard]] AQ_TILED_DRM_PRIVATE SGroupedTestProbeResult   probeCurrentGroupedTestModeset(const CWeakPointer<CDRMBackend>&      backend,
-                                                                                                const TGroupedTestKMSCaptureProvider& captureProvider) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE SBeginConnectorScanResult    beginConnectorScan(const CSharedPointer<CDRMBackend>& backend);
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE eScanStageResult             stageConnectorScan(const PConnectorScan& scan, eScanCompleteness completeness,
+                                                                                       std::vector<SConnectorView> connectors) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE eScanCommitResult            commitConnectorScan(const PConnectorScan& scan) noexcept;
+    AQ_TILED_DRM_PRIVATE void                                       abortConnectorScan(const PConnectorScan& scan) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE eAllocationCaptureResult     captureAllocationShadow(const CSharedPointer<CDRMBackend>& backend, SAllocationCapture capture) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE PAllocationShadow            currentAllocationShadow(const CWeakPointer<CDRMBackend>& backend) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE bool                         withCurrentAllocationShadow(const CWeakPointer<CDRMBackend>& backend, const PAllocationShadow& expected,
+                                                                                                const std::function<void(const CPlan&, const SCurrentState&)>& callback) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE bool                         withCurrentOutputBridge(const CWeakPointer<CDRMBackend>& backend, const PAllocationShadow& expected,
+                                                                                            const std::function<void(const POutputBridgeObservation&)>& callback) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE SGroupedTestProbeResult      probeCurrentGroupedTestModeset(const CWeakPointer<CDRMBackend>&      backend,
+                                                                                                   const TGroupedTestKMSCaptureProvider& captureProvider) noexcept;
     [[nodiscard]] AQ_TILED_DRM_PRIVATE SGroupedStartupPrepareResult prepareGroupedStartup(const CWeakPointer<CDRMBackend>&               backend,
                                                                                           std::span<const CSharedPointer<SDRMConnector>> connectors,
                                                                                           const TGroupedTestKMSCaptureProvider&          captureProvider) noexcept;
     [[nodiscard]] AQ_TILED_DRM_PRIVATE eGroupedStartupConnectorRole groupedStartupConnectorRole(const CWeakPointer<CDRMBackend>&     backend,
                                                                                                 const CSharedPointer<SDRMConnector>& connector) noexcept;
-    [[nodiscard]] AQ_TILED_DRM_PRIVATE std::vector<SDRMFormat>     groupedStartupRenderFormats(const CWeakPointer<CDRMBackend>&     backend,
-                                                                                               const CSharedPointer<SDRMConnector>& connector) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE std::vector<SDRMFormat> groupedStartupRenderFormats(const CWeakPointer<CDRMBackend>&     backend,
+                                                                                           const CSharedPointer<SDRMConnector>& connector) noexcept;
     [[nodiscard]] AQ_TILED_DRM_PRIVATE bool groupedStartupStateOnlyCurrent(const CWeakPointer<CDRMBackend>& backend, const CSharedPointer<SDRMConnector>& connector,
                                                                            const CSharedPointer<SOutputMode>& outputMode) noexcept;
     [[nodiscard]] AQ_TILED_DRM_PRIVATE SGroupedStartupCommitResult commitGroupedStartup(const CWeakPointer<CDRMBackend>& backend, const CSharedPointer<SDRMConnector>& connector,
                                                                                         const CSharedPointer<SOutputMode>& outputMode, const CSharedPointer<CDRMFB>& framebuffer,
-                                                                                        const CSharedPointer<IBuffer>& buffer, bool onlyTest, uintptr_t transactionId,
-                                                                                        uint64_t deadlineNs, const TGroupedTestKMSCaptureProvider& captureProvider) noexcept;
-    [[nodiscard]] AQ_TILED_DRM_PRIVATE bool                        groupedStartupActive(const CWeakPointer<CDRMBackend>& backend) noexcept;
-    [[nodiscard]] AQ_TILED_DRM_PRIVATE SOrdinaryKMSGateResult    gateOrdinaryKMS(const CWeakPointer<CDRMBackend>& backend, std::span<const SOrdinaryKMSCRTC> crtcs,
-                                                                                 const std::function<bool()>& callback) noexcept;
-    [[nodiscard]] AQ_TILED_DRM_PRIVATE SGroupedPageFlipResult    routeGroupedPageFlip(const CWeakPointer<CDRMBackend>& backend, uintptr_t transactionId, uint32_t crtcId,
-                                                                                      uint32_t sequence, uint64_t timestampNs) noexcept;
-    [[nodiscard]] AQ_TILED_DRM_PRIVATE eSessionResult            rotateSession(const CWeakPointer<CDRMBackend>& backend) noexcept;
-    [[nodiscard]] AQ_TILED_DRM_PRIVATE eDetachResult             beginDetach(const CWeakPointer<CDRMBackend>& backend, eDetachReason reason) noexcept;
-    [[nodiscard]] AQ_TILED_DRM_PRIVATE eDetachResult             terminalizeDetached(const CWeakPointer<CDRMBackend>& backend, eDetachTerminalProof proof) noexcept;
-    [[nodiscard]] AQ_TILED_DRM_PRIVATE eDetachResult             eraseTerminalized(const CWeakPointer<CDRMBackend>& backend) noexcept;
-    [[nodiscard]] AQ_TILED_DRM_PRIVATE bool                      attached(const CWeakPointer<CDRMBackend>& backend) noexcept;
-    [[nodiscard]] AQ_TILED_DRM_PRIVATE bool                      lastConnectorScanComplete(const CWeakPointer<CDRMBackend>& backend) noexcept;
-    [[nodiscard]] AQ_TILED_DRM_PRIVATE PCommittedScan            currentComplete(const CWeakPointer<CDRMBackend>& backend) noexcept;
+                                                                                        const CSharedPointer<IBuffer>& buffer, bool onlyTest, bool enabled, uintptr_t transactionId,
+                                                                                        uint64_t deadlineNs, const TGroupedTestKMSCaptureProvider& captureProvider,
+                                                                                        std::optional<Hyprutils::Math::Mat3x3> ctm = {}) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE std::vector<SGroupedStartupMemberPower> groupedStartupMemberPower(const CWeakPointer<CDRMBackend>& backend) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE std::vector<SGroupedStartupMemberCtm> groupedStartupMemberCtm(const CWeakPointer<CDRMBackend>& backend) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE bool                                  groupedStartupActive(const CWeakPointer<CDRMBackend>& backend) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE SOrdinaryKMSGateResult                gateOrdinaryKMS(const CWeakPointer<CDRMBackend>& backend, std::span<const SOrdinaryKMSCRTC> crtcs,
+                                                                                             const std::function<bool()>& callback) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE SGroupedPageFlipResult routeGroupedPageFlip(const CWeakPointer<CDRMBackend>& backend, uintptr_t transactionId, uint32_t crtcId,
+                                                                                   uint32_t sequence, uint64_t timestampNs) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE eSessionResult         rotateSession(const CWeakPointer<CDRMBackend>& backend) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE eDetachResult          beginDetach(const CWeakPointer<CDRMBackend>& backend, eDetachReason reason) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE eDetachResult          terminalizeDetached(const CWeakPointer<CDRMBackend>& backend, eDetachTerminalProof proof) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE eDetachResult          eraseTerminalized(const CWeakPointer<CDRMBackend>& backend) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE bool                   attached(const CWeakPointer<CDRMBackend>& backend) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE bool                   lastConnectorScanComplete(const CWeakPointer<CDRMBackend>& backend) noexcept;
+    [[nodiscard]] AQ_TILED_DRM_PRIVATE PCommittedScan         currentComplete(const CWeakPointer<CDRMBackend>& backend) noexcept;
 
 }
 
diff --git a/src/backend/drm/TiledRuntimeAuthority.cpp b/src/backend/drm/TiledRuntimeAuthority.cpp
index 028c282b20c310ff6e18b52d31be1f7e47355fa4..632346955024bc9922dddf4918718109c49727ec 100644
--- a/src/backend/drm/TiledRuntimeAuthority.cpp
+++ b/src/backend/drm/TiledRuntimeAuthority.cpp
@@ -5,11 +5,13 @@
 #include <algorithm>
 #include <cmath>
 #include <exception>
+#include <functional>
 #include <limits>
 #include <ranges>
 #include <utility>
 
 #include <drm_fourcc.h>
+#include <drm_mode.h>
 #include <hyprutils/utils/ScopeGuard.hpp>
 
 using namespace Aquamarine;
@@ -1262,6 +1264,131 @@ SGroupedQuiesceResult CGroupedRuntimeAuthority::quiesceAllUnguarded(eTiledQuiesc
     return result;
 }
 
+namespace {
+    uint64_t doubleToS3132Fixed(double val) noexcept {
+        const uint64_t result = static_cast<uint64_t>(std::abs(val) * static_cast<double>(1ULL << 32));
+        if (val < 0)
+            return result | (1ULL << 63);
+        return result;
+    }
+
+    CGroupedRuntimeAuthority::SGroupedDisableResult
+    submitGroupedMemberProperties(bool onEventThread, bool mutationActive, bool terminal, const CSharedPointer<ITiledAtomicRequestOperations>& operations, int drmFD,
+                                  std::span<const CSharedPointer<SDRMConnector>> members, bool onlyTest,
+                                  const std::function<bool(const CSharedPointer<ITiledAtomicRequestOperations>&, drmModeAtomicReqPtr)>& fill) noexcept {
+        CGroupedRuntimeAuthority::SGroupedDisableResult result;
+        if (!onEventThread || mutationActive || terminal || !operations || drmFD < 0 || members.empty() || !fill)
+            return result;
+
+        for (const auto& MEMBER : members) {
+            if (!MEMBER || MEMBER->id == 0)
+                return result;
+        }
+
+        result.attempted            = true;
+        drmModeAtomicReqPtr request = operations->allocateRequest();
+        if (!request)
+            return result;
+
+        uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
+        if (onlyTest)
+            flags |= DRM_MODE_ATOMIC_TEST_ONLY;
+        if (fill(operations, request) && operations->submit(drmFD, request, flags, nullptr) == 0) {
+            result.submitted = true;
+            result.success   = true;
+        }
+        operations->freeRequest(request);
+        return result;
+    }
+}
+
+CGroupedRuntimeAuthority::SGroupedDisableResult CGroupedRuntimeAuthority::submitDisable(std::span<const CSharedPointer<SDRMConnector>> members, bool onlyTest) noexcept {
+    return submitGroupedMemberProperties(onEventThread(), m_mutationActive, m_terminal, m_testOperations, m_testDrmFD, members, onlyTest,
+                                         [&](const CSharedPointer<ITiledAtomicRequestOperations>& operations, drmModeAtomicReqPtr request) {
+                                             const auto add = [&](uint32_t objectId, uint32_t propertyId, uint64_t value) {
+                                                 return propertyId == 0 || operations->addProperty(request, objectId, propertyId, value) >= 0;
+                                             };
+                                             bool ok = true;
+                                             for (const auto& MEMBER : members) {
+                                                 ok = ok && add(MEMBER->id, MEMBER->props.values.crtc_id, 0);
+                                                 ok = ok && add(MEMBER->id, MEMBER->props.values.dpms, DRM_MODE_DPMS_OFF);
+                                                 if (!MEMBER->crtc)
+                                                     continue;
+                                                 ok = ok && add(MEMBER->crtc->id, MEMBER->crtc->props.values.mode_id, 0);
+                                                 ok = ok && add(MEMBER->crtc->id, MEMBER->crtc->props.values.active, 0);
+                                                 if (!MEMBER->crtc->primary)
+                                                     continue;
+                                                 ok = ok && add(MEMBER->crtc->primary->id, MEMBER->crtc->primary->props.values.fb_id, 0);
+                                                 ok = ok && add(MEMBER->crtc->primary->id, MEMBER->crtc->primary->props.values.crtc_id, 0);
+                                             }
+                                             return ok;
+                                         });
+}
+
+CGroupedRuntimeAuthority::SGroupedDisableResult CGroupedRuntimeAuthority::submitRestore(std::span<const CSharedPointer<SDRMConnector>> members, bool onlyTest) noexcept {
+    return submitGroupedMemberProperties(onEventThread(), m_mutationActive, m_terminal, m_testOperations, m_testDrmFD, members, onlyTest,
+                                         [&](const CSharedPointer<ITiledAtomicRequestOperations>& operations, drmModeAtomicReqPtr request) {
+                                             const auto add = [&](uint32_t objectId, uint32_t propertyId, uint64_t value) {
+                                                 return propertyId == 0 || operations->addProperty(request, objectId, propertyId, value) >= 0;
+                                             };
+                                             bool ok = true;
+                                             for (const auto& MEMBER : members) {
+                                                 if (MEMBER->crtc)
+                                                     ok = ok && add(MEMBER->id, MEMBER->props.values.crtc_id, MEMBER->crtc->id);
+                                                 ok = ok && add(MEMBER->id, MEMBER->props.values.dpms, DRM_MODE_DPMS_ON);
+                                             }
+                                             return ok;
+                                         });
+}
+
+CGroupedRuntimeAuthority::SGroupedCtmResult CGroupedRuntimeAuthority::submitCtm(std::span<const CSharedPointer<SDRMConnector>> members, const Hyprutils::Math::Mat3x3& matrix,
+                                                                                bool onlyTest, uint32_t previousBlobId) noexcept {
+    SGroupedCtmResult result;
+    if (!onEventThread() || m_mutationActive || m_terminal || !m_testOperations || m_testDrmFD < 0 || members.empty())
+        return result;
+
+    for (const auto& MEMBER : members) {
+        if (!MEMBER || MEMBER->id == 0 || !MEMBER->crtc || MEMBER->crtc->id == 0 || MEMBER->crtc->props.values.ctm == 0)
+            return result;
+    }
+
+    result.attempted = true;
+
+    drm_color_ctm packed = {};
+    const auto    values = matrix.getMatrix();
+    for (size_t i = 0; i < 9; ++i)
+        packed.matrix[i] = doubleToS3132Fixed(values[i]);
+
+    uint32_t blobId = 0;
+    if (m_testOperations->createPropertyBlob(m_testDrmFD, &packed, sizeof(packed), &blobId) != 0 || blobId == 0)
+        return result;
+
+    const auto SUBMITTED = submitGroupedMemberProperties(true, false, false, m_testOperations, m_testDrmFD, members, onlyTest,
+                                                         [&](const CSharedPointer<ITiledAtomicRequestOperations>& operations, drmModeAtomicReqPtr request) {
+                                                             for (const auto& MEMBER : members) {
+                                                                 if (operations->addProperty(request, MEMBER->crtc->id, MEMBER->crtc->props.values.ctm, blobId) < 0)
+                                                                     return false;
+                                                             }
+                                                             return true;
+                                                         });
+    if (!SUBMITTED.success) {
+        static_cast<void>(m_testOperations->destroyPropertyBlob(m_testDrmFD, blobId));
+        return result;
+    }
+
+    result.submitted = SUBMITTED.submitted;
+    result.success   = true;
+    if (onlyTest) {
+        static_cast<void>(m_testOperations->destroyPropertyBlob(m_testDrmFD, blobId));
+        return result;
+    }
+
+    if (previousBlobId != 0 && previousBlobId != blobId)
+        static_cast<void>(m_testOperations->destroyPropertyBlob(m_testDrmFD, previousBlobId));
+    result.blobId = blobId;
+    return result;
+}
+
 SGroupedQuiesceResult CGroupedRuntimeAuthority::quiesceAll(eTiledQuiesceReason reason) noexcept {
     if (!onEventThread() || m_mutationActive || m_terminal || reason == eTiledQuiesceReason::NONE)
         return {.invariantFailed = true};
diff --git a/src/backend/drm/TiledRuntimeAuthority.hpp b/src/backend/drm/TiledRuntimeAuthority.hpp
index 455c72fe2eccfe433ac43cc0382b707c328d9014..c21603998464a3852576e0d5ba95aeef0aca2182 100644
--- a/src/backend/drm/TiledRuntimeAuthority.hpp
+++ b/src/backend/drm/TiledRuntimeAuthority.hpp
@@ -478,10 +478,28 @@ namespace Aquamarine::NDRM::NTiledRuntimeAuthority { // NOLINT(readability-ident
         [[nodiscard]] SGroupedQuiesceResult      quiesceAll(eTiledQuiesceReason reason) noexcept;
         [[nodiscard]] SGroupedRetirementResult   retire(const POutputDefinitionIdentity& identity, eTiledRetirementProof proof) noexcept;
 
-        [[nodiscard]] bool                       terminalShutdown(eTiledRegistryShutdownProof registryProof, eTiledRetirementProof runtimeProof) noexcept;
-        [[nodiscard]] bool                       busy() const noexcept;
-        [[nodiscard]] bool                       readyForDestruction() const noexcept;
-        [[nodiscard]] size_t                     runtimeCount() const noexcept;
+        struct SGroupedDisableResult {
+            bool attempted = false;
+            bool submitted = false;
+            bool success   = false;
+        };
+
+        struct SGroupedCtmResult {
+            bool     attempted = false;
+            bool     submitted = false;
+            bool     success   = false;
+            uint32_t blobId    = 0;
+        };
+
+        [[nodiscard]] SGroupedDisableResult submitDisable(std::span<const CSharedPointer<SDRMConnector>> members, bool onlyTest) noexcept;
+        [[nodiscard]] SGroupedDisableResult submitRestore(std::span<const CSharedPointer<SDRMConnector>> members, bool onlyTest) noexcept;
+        [[nodiscard]] SGroupedCtmResult     submitCtm(std::span<const CSharedPointer<SDRMConnector>> members, const Hyprutils::Math::Mat3x3& matrix, bool onlyTest,
+                                                      uint32_t previousBlobId = 0) noexcept;
+
+        [[nodiscard]] bool                  terminalShutdown(eTiledRegistryShutdownProof registryProof, eTiledRetirementProof runtimeProof) noexcept;
+        [[nodiscard]] bool                  busy() const noexcept;
+        [[nodiscard]] bool                  readyForDestruction() const noexcept;
+        [[nodiscard]] size_t                runtimeCount() const noexcept;
 
       private:
         struct SCompletionDebt {
diff --git a/tests/TiledGroupedStartupCtm.cpp b/tests/TiledGroupedStartupCtm.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..52dfe2796f55315de388be9cbdde3b21c928fe85
--- /dev/null
+++ b/tests/TiledGroupedStartupCtm.cpp
@@ -0,0 +1,369 @@
+#include "backend/drm/TiledDRMIntegration.hpp"
+#include "shared.hpp"
+
+#include <array>
+#include <cerrno>
+#include <cstdint>
+#include <optional>
+#include <utility>
+#include <vector>
+
+#include <drm_mode.h>
+#include <hyprutils/math/Mat3x3.hpp>
+#include <hyprutils/memory/SharedPtr.hpp>
+#include <xf86drmMode.h>
+
+using namespace Aquamarine;
+using namespace Aquamarine::NDRM::NTiledDRMIntegration;
+using namespace Aquamarine::NDRM::NTiledIncarnations;
+using namespace Aquamarine::NDRM::NTiledAtomic;
+using namespace Aquamarine::NDRM::NTiledRuntimeAuthority;
+using namespace Hyprutils::Math;
+using namespace Hyprutils::Memory;
+
+namespace Aquamarine::NDRM::NTiledDRMIntegrationTest {
+    class CFixtureAuthority {
+      public:
+        static SBeginConnectorScanResult begin(CTiledDRMIntegrationRegistry& registry, const PBackendLifetime& lifetime) {
+            return registry.beginTestConnectorScan(lifetime);
+        }
+
+        static bool configureTestModeset(CTiledDRMIntegrationRegistry& registry, const PBackendLifetime& lifetime, int drmFD,
+                                         const CSharedPointer<ITiledAtomicRequestOperations>& operations, const CSharedPointer<IGroupedTestFramebufferSource>& framebufferSource) {
+            return registry.configureInjectedTestModeset(lifetime, drmFD, operations, framebufferSource);
+        }
+
+        static bool installGroupedStartup(CTiledDRMIntegrationRegistry& registry, const PBackendLifetime& lifetime, const CSharedPointer<SDRMConnector>& primary,
+                                          std::vector<CSharedPointer<SDRMConnector>> members) {
+            return registry.installTestGroupedStartup(lifetime, primary, std::move(members));
+        }
+
+        static SGroupedStartupCommitResult commit(CTiledDRMIntegrationRegistry& registry, const PBackendLifetime& lifetime, const CWeakPointer<CDRMBackend>& backend,
+                                                  const CSharedPointer<SDRMConnector>& connector, bool onlyTest, bool enabled, std::optional<Mat3x3> ctm = {}) {
+            return registry.commitGroupedStartupKey(lifetime.impl_, backend, connector, {}, {}, {}, onlyTest, enabled, 0, 0, {}, std::move(ctm));
+        }
+
+        static std::vector<SGroupedStartupMemberPower> memberPower(CTiledDRMIntegrationRegistry& registry, const PBackendLifetime& lifetime) {
+            return registry.groupedStartupMemberPowerKey(lifetime.impl_);
+        }
+
+        static std::vector<SGroupedStartupMemberCtm> memberCtm(CTiledDRMIntegrationRegistry& registry, const PBackendLifetime& lifetime) {
+            return registry.groupedStartupMemberCtmKey(lifetime.impl_);
+        }
+
+        static eDetachResult beginDetach(CTiledDRMIntegrationRegistry& registry, const PBackendLifetime& lifetime, eDetachReason reason) {
+            return registry.beginTestDetach(lifetime, reason);
+        }
+
+        static eDetachResult terminalize(CTiledDRMIntegrationRegistry& registry, const PBackendLifetime& lifetime, eDetachTerminalProof proof) {
+            return registry.terminalizeTestDetached(lifetime, proof);
+        }
+
+        static eDetachResult erase(CTiledDRMIntegrationRegistry& registry, const PBackendLifetime& lifetime) {
+            return registry.eraseTestTerminalized(lifetime);
+        }
+    };
+}
+
+using Aquamarine::NDRM::NTiledDRMIntegrationTest::CFixtureAuthority;
+
+namespace {
+    struct SRecordedWrite {
+        uint32_t objectId   = 0;
+        uint32_t propertyId = 0;
+        uint64_t value      = 0;
+    };
+
+    struct STestAtomicOperations final : ITiledAtomicRequestOperations {
+        drmModeAtomicReqPtr allocateRequest() noexcept override {
+            ++allocations;
+            writes.clear();
+            // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): opaque fake request identity
+            return reinterpret_cast<drmModeAtomicReqPtr>(this);
+        }
+
+        void freeRequest(drmModeAtomicReqPtr request) noexcept override {
+            // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): matches allocateRequest
+            if (request == reinterpret_cast<drmModeAtomicReqPtr>(this))
+                ++frees;
+        }
+
+        int addProperty(drmModeAtomicReqPtr, uint32_t objectId, uint32_t propertyId, uint64_t value) noexcept override {
+            writes.push_back({.objectId = objectId, .propertyId = propertyId, .value = value});
+            ++properties;
+            return 0;
+        }
+
+        int submit(int fd, drmModeAtomicReqPtr, uint32_t flags, void*) noexcept override {
+            ++submits;
+            submittedFD    = fd;
+            submittedFlags = flags;
+            submitWrites   = writes;
+            return 0;
+        }
+
+        int createModeBlob(int, const drmModeModeInfo&, uint32_t* blobId) noexcept override {
+            if (!blobId)
+                return -EINVAL;
+            *blobId = nextBlob++;
+            return 0;
+        }
+
+        int destroyModeBlob(int, uint32_t) noexcept override {
+            return 0;
+        }
+
+        int createPropertyBlob(int, const void* data, size_t size, uint32_t* blobId) noexcept override {
+            if (!data || !blobId || size == 0)
+                return -EINVAL;
+            *blobId = nextBlob++;
+            createdBlobs.push_back(*blobId);
+            lastBlobSize = size;
+            return 0;
+        }
+
+        int destroyPropertyBlob(int, uint32_t blobId) noexcept override {
+            destroyedBlobs.push_back(blobId);
+            return 0;
+        }
+
+        size_t                      allocations    = 0;
+        size_t                      frees          = 0;
+        size_t                      properties     = 0;
+        size_t                      submits        = 0;
+        uint32_t                    nextBlob       = 900;
+        int                         submittedFD    = -1;
+        uint32_t                    submittedFlags = 0;
+        size_t                      lastBlobSize   = 0;
+        std::vector<SRecordedWrite> writes;
+        std::vector<SRecordedWrite> submitWrites;
+        std::vector<uint32_t>       createdBlobs;
+        std::vector<uint32_t>       destroyedBlobs;
+    };
+
+    struct STestFramebufferSource final : IGroupedTestFramebufferSource {
+        PGroupedTestFramebuffer acquire(const SGroupedTestFramebufferRequest&) noexcept override {
+            return {};
+        }
+    };
+
+    int value(eGroupedStartupOutputDisposition disposition) {
+        return static_cast<int>(disposition);
+    }
+
+    int value(eScanBeginResult result) {
+        return static_cast<int>(result);
+    }
+
+    int value(eDetachResult result) {
+        return static_cast<int>(result);
+    }
+
+    void teardown(CTiledDRMIntegrationRegistry& registry, const PBackendLifetime& lifetime, int& ret) {
+        EXPECT(value(CFixtureAuthority::beginDetach(registry, lifetime, eDetachReason::BACKEND_SHUTDOWN)), value(eDetachResult::BEGUN));
+        EXPECT(value(CFixtureAuthority::terminalize(registry, lifetime, eDetachTerminalProof::BACKEND_OBJECT_DESTRUCTING_NO_CALLBACKS)), value(eDetachResult::TERMINALIZED));
+        EXPECT(value(CFixtureAuthority::erase(registry, lifetime)), value(eDetachResult::DETACHED));
+    }
+
+    SGroupedStartupOutputRequest baseRequest() {
+        return {
+            .committed        = COutputState::AQ_OUTPUT_STATE_DAMAGE | COutputState::AQ_OUTPUT_STATE_BUFFER,
+            .enabled          = true,
+            .presentationMode = AQ_OUTPUT_PRESENTATION_VSYNC,
+            .drmFormat        = DRM_FORMAT_XRGB8888,
+            .hasMode          = true,
+        };
+    }
+
+    Mat3x3 nightLightCtm() {
+        return Mat3x3{std::array<float, 9>{1.F, 0.F, 0.F, 0.F, 0.82F, 0.F, 0.F, 0.F, 0.55F}};
+    }
+
+    CSharedPointer<SDRMConnector> makeMember(uint32_t connectorId, uint32_t crtcId, uint32_t ctmProp, uint32_t dpmsProp, uint32_t crtcIdProp) {
+        auto connector                  = makeShared<SDRMConnector>();
+        auto crtc                       = makeShared<SDRMCRTC>();
+        connector->id                   = connectorId;
+        crtc->id                        = crtcId;
+        connector->crtc                 = crtc;
+        connector->props.values.crtc_id = crtcIdProp;
+        connector->props.values.dpms    = dpmsProp;
+        crtc->props.values.ctm          = ctmProp;
+        crtc->props.values.mode_id      = ctmProp + 1;
+        crtc->props.values.active       = ctmProp + 2;
+        return connector;
+    }
+
+    bool membersSynchronized(const std::vector<SGroupedStartupMemberCtm>& members, const Mat3x3& expected, bool identity) {
+        if (members.size() != 2)
+            return false;
+        return members[0].applied && members[1].applied && members[0].stateKnown && members[1].stateKnown && members[0].identity == identity && members[1].identity == identity &&
+            members[0].matrix == expected && members[1].matrix == expected && members[0].crtcId != 0 && members[1].crtcId != 0 && members[0].crtcId != members[1].crtcId;
+    }
+
+    size_t countWrites(const std::vector<SRecordedWrite>& writes, uint32_t objectId, uint32_t propertyId) {
+        size_t count = 0;
+        for (const auto& WRITE : writes) {
+            if (WRITE.objectId == objectId && WRITE.propertyId == propertyId)
+                ++count;
+        }
+        return count;
+    }
+
+    uint64_t writeValue(const std::vector<SRecordedWrite>& writes, uint32_t objectId, uint32_t propertyId) {
+        for (const auto& WRITE : writes) {
+            if (WRITE.objectId == objectId && WRITE.propertyId == propertyId)
+                return WRITE.value;
+        }
+        return 0;
+    }
+}
+
+int main() {
+    int ret = 0;
+
+    {
+        auto request = baseRequest();
+        EXPECT(value(classifyGroupedStartupOutputRequest(request)), value(eGroupedStartupOutputDisposition::ENABLE_COMMIT));
+
+        request.committed |= COutputState::AQ_OUTPUT_STATE_EXPLICIT_IN_FENCE | COutputState::AQ_OUTPUT_STATE_CTM;
+        EXPECT(value(classifyGroupedStartupOutputRequest(request)), value(eGroupedStartupOutputDisposition::ENABLE_COMMIT));
+
+        request           = baseRequest();
+        request.committed = COutputState::AQ_OUTPUT_STATE_DAMAGE | COutputState::AQ_OUTPUT_STATE_CTM;
+        EXPECT(value(classifyGroupedStartupOutputRequest(request)), value(eGroupedStartupOutputDisposition::STATE_ONLY));
+
+        request         = baseRequest();
+        request.enabled = false;
+        request.committed |= COutputState::AQ_OUTPUT_STATE_CTM;
+        EXPECT(value(classifyGroupedStartupOutputRequest(request)), value(eGroupedStartupOutputDisposition::DISABLE));
+
+        request = baseRequest();
+        request.committed |= COutputState::AQ_OUTPUT_STATE_GAMMA_LUT;
+        EXPECT(value(classifyGroupedStartupOutputRequest(request)), value(eGroupedStartupOutputDisposition::REJECT));
+
+        request = baseRequest();
+        request.committed |= COutputState::AQ_OUTPUT_STATE_HDR;
+        EXPECT(value(classifyGroupedStartupOutputRequest(request)), value(eGroupedStartupOutputDisposition::REJECT));
+
+        request              = baseRequest();
+        request.adaptiveSync = true;
+        EXPECT(value(classifyGroupedStartupOutputRequest(request)), value(eGroupedStartupOutputDisposition::REJECT));
+
+        request               = baseRequest();
+        request.hasCustomMode = true;
+        EXPECT(value(classifyGroupedStartupOutputRequest(request)), value(eGroupedStartupOutputDisposition::REJECT));
+
+        request                  = baseRequest();
+        request.supportsExplicit = true;
+        request.committed |= COutputState::AQ_OUTPUT_STATE_EXPLICIT_IN_FENCE;
+        EXPECT(value(classifyGroupedStartupOutputRequest(request)), value(eGroupedStartupOutputDisposition::REJECT));
+
+        EXPECT((groupedStartupSupportedStateBits(false) & COutputState::AQ_OUTPUT_STATE_CTM) != 0, true);
+        EXPECT((groupedStartupSupportedStateBits(false) & COutputState::AQ_OUTPUT_STATE_EXPLICIT_IN_FENCE) != 0, true);
+        EXPECT((groupedStartupSupportedStateBits(true) & COutputState::AQ_OUTPUT_STATE_EXPLICIT_IN_FENCE) == 0, true);
+    }
+
+    {
+        CTiledDRMIntegrationRegistry registry;
+        const auto                   LIFETIME = CBackendLifetime::createTestToken(81);
+        auto                         begun    = CFixtureAuthority::begin(registry, LIFETIME);
+        EXPECT(value(begun.result), value(eScanBeginResult::STARTED));
+        if (begun.scan)
+            registry.abortConnectorScan(begun.scan);
+
+        auto                                          operations                 = makeShared<STestAtomicOperations>();
+        auto                                          framebufferSource          = makeShared<STestFramebufferSource>();
+        CSharedPointer<ITiledAtomicRequestOperations> operationsInterface        = operations;
+        CSharedPointer<IGroupedTestFramebufferSource> framebufferSourceInterface = framebufferSource;
+        EXPECT(CFixtureAuthority::configureTestModeset(registry, LIFETIME, 73, operationsInterface, framebufferSourceInterface), true);
+
+        auto left  = makeMember(101, 10, 4000, 1002, 1000);
+        auto right = makeMember(202, 11, 4010, 1012, 1010);
+        EXPECT(CFixtureAuthority::installGroupedStartup(registry, LIFETIME, left, {left, right}), true);
+
+        const CWeakPointer<CDRMBackend> NO_BACKEND;
+        const auto                      NIGHT = nightLightCtm();
+
+        auto                            ctmEnable = CFixtureAuthority::commit(registry, LIFETIME, NO_BACKEND, left, false, true, NIGHT);
+        EXPECT(ctmEnable.claimed, true);
+        EXPECT(ctmEnable.success, true);
+
+        auto afterEnable = CFixtureAuthority::memberCtm(registry, LIFETIME);
+        EXPECT(membersSynchronized(afterEnable, NIGHT, false), true);
+        EXPECT(countWrites(operations->submitWrites, 10, 4000), 1U);
+        EXPECT(countWrites(operations->submitWrites, 11, 4010), 1U);
+        EXPECT(writeValue(operations->submitWrites, 10, 4000) == writeValue(operations->submitWrites, 11, 4010), true);
+        EXPECT(writeValue(operations->submitWrites, 10, 4000) != 0, true);
+        EXPECT(operations->lastBlobSize, sizeof(drm_color_ctm));
+
+        const auto CTM_SUBMITS = operations->submits;
+        auto       followFrame = CFixtureAuthority::commit(registry, LIFETIME, NO_BACKEND, left, false, true);
+        EXPECT(followFrame.claimed, true);
+        EXPECT(followFrame.success, true);
+        EXPECT(operations->submits, CTM_SUBMITS);
+        auto afterFrame = CFixtureAuthority::memberCtm(registry, LIFETIME);
+        EXPECT(membersSynchronized(afterFrame, NIGHT, false), true);
+
+        auto fencePlusCtm = baseRequest();
+        fencePlusCtm.committed |= COutputState::AQ_OUTPUT_STATE_EXPLICIT_IN_FENCE | COutputState::AQ_OUTPUT_STATE_CTM;
+        EXPECT(value(classifyGroupedStartupOutputRequest(fencePlusCtm)), value(eGroupedStartupOutputDisposition::ENABLE_COMMIT));
+
+        auto identity = CFixtureAuthority::commit(registry, LIFETIME, NO_BACKEND, left, false, true, Mat3x3::identity());
+        EXPECT(identity.claimed, true);
+        EXPECT(identity.success, true);
+        auto afterIdentity = CFixtureAuthority::memberCtm(registry, LIFETIME);
+        EXPECT(membersSynchronized(afterIdentity, Mat3x3::identity(), true), true);
+        EXPECT(countWrites(operations->submitWrites, 10, 4000), 1U);
+        EXPECT(countWrites(operations->submitWrites, 11, 4010), 1U);
+        EXPECT(writeValue(operations->submitWrites, 10, 4000) == writeValue(operations->submitWrites, 11, 4010), true);
+
+        auto disable = CFixtureAuthority::commit(registry, LIFETIME, NO_BACKEND, left, false, false);
+        EXPECT(disable.claimed, true);
+        EXPECT(disable.success, true);
+        auto powerOff = CFixtureAuthority::memberPower(registry, LIFETIME);
+        EXPECT(powerOff.size(), 2U);
+        EXPECT(powerOff.size() == 2 && !powerOff[0].enabled && !powerOff[1].enabled, true);
+        EXPECT((operations->submittedFlags & DRM_MODE_ATOMIC_ALLOW_MODESET) != 0, true);
+        EXPECT((operations->submittedFlags & DRM_MODE_ATOMIC_TEST_ONLY) == 0, true);
+
+        auto restore = CFixtureAuthority::commit(registry, LIFETIME, NO_BACKEND, left, false, true);
+        EXPECT(restore.claimed, true);
+        EXPECT(restore.success, true);
+        auto powerOn = CFixtureAuthority::memberPower(registry, LIFETIME);
+        EXPECT(powerOn.size() == 2 && powerOn[0].enabled && powerOn[1].enabled, true);
+        auto afterRestore = CFixtureAuthority::memberCtm(registry, LIFETIME);
+        EXPECT(membersSynchronized(afterRestore, Mat3x3::identity(), true), true);
+
+        auto onlyTestDisable = CFixtureAuthority::commit(registry, LIFETIME, NO_BACKEND, left, true, false);
+        EXPECT(onlyTestDisable.claimed, true);
+        EXPECT(onlyTestDisable.success, true);
+        EXPECT((operations->submittedFlags & DRM_MODE_ATOMIC_TEST_ONLY) != 0, true);
+        auto powerStillOn = CFixtureAuthority::memberPower(registry, LIFETIME);
+        EXPECT(powerStillOn.size() == 2 && powerStillOn[0].enabled && powerStillOn[1].enabled, true);
+        teardown(registry, LIFETIME, ret);
+    }
+
+    {
+        CTiledDRMIntegrationRegistry registry;
+        const auto                   LIFETIME = CBackendLifetime::createTestToken(82);
+        auto                         begun    = CFixtureAuthority::begin(registry, LIFETIME);
+        EXPECT(value(begun.result), value(eScanBeginResult::STARTED));
+        if (begun.scan)
+            registry.abortConnectorScan(begun.scan);
+        auto                            left = makeMember(301, 20, 5000, 2002, 2000);
+        const CWeakPointer<CDRMBackend> NO_BACKEND;
+        auto                            ordinary = CFixtureAuthority::commit(registry, LIFETIME, NO_BACKEND, left, false, true, nightLightCtm());
+        EXPECT(ordinary.claimed, false);
+        EXPECT(ordinary.success, false);
+
+        EXPECT(CFixtureAuthority::installGroupedStartup(registry, LIFETIME, left, {left}), true);
+        auto missingOps = CFixtureAuthority::commit(registry, LIFETIME, NO_BACKEND, left, false, true, nightLightCtm());
+        EXPECT(missingOps.claimed, true);
+        EXPECT(missingOps.success, false);
+        auto missingCtm = CFixtureAuthority::memberCtm(registry, LIFETIME);
+        EXPECT(missingCtm.size() == 1 && !missingCtm[0].applied && !missingCtm[0].stateKnown, true);
+        teardown(registry, LIFETIME, ret);
+    }
+
+    return ret;
+}