File indexing completed on 2025-04-20 10:57:36

0001 /*
0002  *    KWin - the KDE window manager
0003  *    This file is part of the KDE project.
0004  *
0005  *    SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
0006  *
0007  *    SPDX-License-Identifier: GPL-2.0-or-later
0008  */
0009 
0010 #include "drm_buffer.h"
0011 #include "drm_connector.h"
0012 #include "drm_crtc.h"
0013 #include "drm_gpu.h"
0014 #include "drm_layer.h"
0015 #include "drm_logging.h"
0016 #include "drm_pipeline.h"
0017 
0018 #include <errno.h>
0019 #include <gbm.h>
0020 
0021 namespace KWin
0022 {
0023 
0024 DrmPipeline::Error DrmPipeline::presentLegacy()
0025 {
0026     if (!m_pending.crtc->current()) {
0027         Error err = legacyModeset();
0028         if (err != Error::None) {
0029             return err;
0030         }
0031     }
0032     const auto buffer = m_pending.layer->currentBuffer();
0033     uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT;
0034     if (m_pending.syncMode == RenderLoopPrivate::SyncMode::Async || m_pending.syncMode == RenderLoopPrivate::SyncMode::AdaptiveAsync) {
0035         flags |= DRM_MODE_PAGE_FLIP_ASYNC;
0036     }
0037     if (drmModePageFlip(gpu()->fd(), m_pending.crtc->id(), buffer->framebufferId(), flags, gpu()) != 0) {
0038         qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno);
0039         return errnoToError();
0040     }
0041     m_pageflipPending = true;
0042     m_pending.crtc->setNext(buffer);
0043     return Error::None;
0044 }
0045 
0046 DrmPipeline::Error DrmPipeline::legacyModeset()
0047 {
0048     uint32_t connId = m_connector->id();
0049     if (!m_pending.layer->checkTestBuffer()) {
0050         return Error::TestBufferFailed;
0051     }
0052     const auto buffer = m_pending.layer->currentBuffer();
0053     if (drmModeSetCrtc(gpu()->fd(), m_pending.crtc->id(), buffer->framebufferId(), 0, 0, &connId, 1, m_pending.mode->nativeMode()) != 0) {
0054         qCWarning(KWIN_DRM) << "Modeset failed!" << strerror(errno);
0055         return errnoToError();
0056     }
0057     // make sure the buffer gets kept alive, or the modeset gets reverted by the kernel
0058     if (m_pending.crtc->current()) {
0059         m_pending.crtc->setNext(buffer);
0060     } else {
0061         m_pending.crtc->setCurrent(buffer);
0062     }
0063     return Error::None;
0064 }
0065 
0066 DrmPipeline::Error DrmPipeline::commitPipelinesLegacy(const QVector<DrmPipeline *> &pipelines, CommitMode mode)
0067 {
0068     Error err = Error::None;
0069     for (const auto &pipeline : pipelines) {
0070         err = pipeline->applyPendingChangesLegacy();
0071         if (err != Error::None) {
0072             break;
0073         }
0074     }
0075     if (err != Error::None) {
0076         // at least try to revert the config
0077         for (const auto &pipeline : pipelines) {
0078             pipeline->revertPendingChanges();
0079             pipeline->applyPendingChangesLegacy();
0080         }
0081     } else {
0082         for (const auto &pipeline : pipelines) {
0083             pipeline->applyPendingChanges();
0084             pipeline->m_current = pipeline->m_pending;
0085             if (mode == CommitMode::CommitModeset && pipeline->activePending()) {
0086                 pipeline->pageFlipped(std::chrono::steady_clock::now().time_since_epoch());
0087             }
0088         }
0089     }
0090     return err;
0091 }
0092 
0093 DrmPipeline::Error DrmPipeline::applyPendingChangesLegacy()
0094 {
0095     if (!m_pending.active && m_pending.crtc) {
0096         drmModeSetCursor(gpu()->fd(), m_pending.crtc->id(), 0, 0, 0);
0097     }
0098     if (activePending()) {
0099         auto vrr = m_pending.crtc->getProp(DrmCrtc::PropertyIndex::VrrEnabled);
0100         if (vrr && !vrr->setPropertyLegacy(m_pending.syncMode == RenderLoopPrivate::SyncMode::Adaptive || m_pending.syncMode == RenderLoopPrivate::SyncMode::AdaptiveAsync)) {
0101             qCWarning(KWIN_DRM) << "Setting vrr failed!" << strerror(errno);
0102             return errnoToError();
0103         }
0104         if (const auto &rgbRange = m_connector->getProp(DrmConnector::PropertyIndex::Broadcast_RGB)) {
0105             rgbRange->setEnumLegacy(m_pending.rgbRange);
0106         }
0107         if (const auto overscan = m_connector->getProp(DrmConnector::PropertyIndex::Overscan)) {
0108             overscan->setPropertyLegacy(m_pending.overscan);
0109         } else if (const auto underscan = m_connector->getProp(DrmConnector::PropertyIndex::Underscan)) {
0110             const uint32_t hborder = calculateUnderscan();
0111             underscan->setEnumLegacy(m_pending.overscan != 0 ? DrmConnector::UnderscanOptions::On : DrmConnector::UnderscanOptions::Off);
0112             m_connector->getProp(DrmConnector::PropertyIndex::Underscan_vborder)->setPropertyLegacy(m_pending.overscan);
0113             m_connector->getProp(DrmConnector::PropertyIndex::Underscan_hborder)->setPropertyLegacy(hborder);
0114         }
0115         if (const auto scaling = m_connector->getProp(DrmConnector::PropertyIndex::ScalingMode); scaling && scaling->hasEnum(DrmConnector::ScalingMode::None)) {
0116             scaling->setEnumLegacy(DrmConnector::ScalingMode::None);
0117         }
0118         if (m_pending.crtc != m_current.crtc || m_pending.mode != m_current.mode) {
0119             Error err = legacyModeset();
0120             if (err != Error::None) {
0121                 return err;
0122             }
0123         }
0124         if (m_pending.gamma && drmModeCrtcSetGamma(gpu()->fd(), m_pending.crtc->id(), m_pending.gamma->lut().size(), m_pending.gamma->lut().red(), m_pending.gamma->lut().green(), m_pending.gamma->lut().blue()) != 0) {
0125             qCWarning(KWIN_DRM) << "Setting gamma failed!" << strerror(errno);
0126             return errnoToError();
0127         }
0128         if (const auto contentType = m_connector->getProp(DrmConnector::PropertyIndex::ContentType)) {
0129             contentType->setEnumLegacy(m_pending.contentType);
0130         }
0131         setCursorLegacy();
0132         moveCursorLegacy();
0133     }
0134     if (!m_connector->getProp(DrmConnector::PropertyIndex::Dpms)->setPropertyLegacy(activePending() ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF)) {
0135         qCWarning(KWIN_DRM) << "Setting legacy dpms failed!" << strerror(errno);
0136         return errnoToError();
0137     }
0138     return Error::None;
0139 }
0140 
0141 bool DrmPipeline::setCursorLegacy()
0142 {
0143     const auto bo = cursorLayer()->currentBuffer();
0144     const uint32_t handle = bo && bo->buffer() && cursorLayer()->isVisible() ? bo->buffer()->handles()[0] : 0;
0145 
0146     struct drm_mode_cursor2 arg = {
0147         .flags = DRM_MODE_CURSOR_BO | DRM_MODE_CURSOR_MOVE,
0148         .crtc_id = m_pending.crtc->id(),
0149         .x = m_pending.cursorLayer->position().x(),
0150         .y = m_pending.cursorLayer->position().y(),
0151         .width = (uint32_t)gpu()->cursorSize().width(),
0152         .height = (uint32_t)gpu()->cursorSize().height(),
0153         .handle = handle,
0154         .hot_x = m_pending.cursorHotspot.x(),
0155         .hot_y = m_pending.cursorHotspot.y(),
0156     };
0157     return drmIoctl(gpu()->fd(), DRM_IOCTL_MODE_CURSOR2, &arg) == 0;
0158 }
0159 
0160 bool DrmPipeline::moveCursorLegacy()
0161 {
0162     return drmModeMoveCursor(gpu()->fd(), m_pending.crtc->id(), cursorLayer()->position().x(), cursorLayer()->position().y()) == 0;
0163 }
0164 
0165 }