File indexing completed on 2024-11-10 04:56:30

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 "core/graphicsbuffer.h"
0011 #include "drm_buffer.h"
0012 #include "drm_commit.h"
0013 #include "drm_commit_thread.h"
0014 #include "drm_connector.h"
0015 #include "drm_crtc.h"
0016 #include "drm_gpu.h"
0017 #include "drm_layer.h"
0018 #include "drm_logging.h"
0019 #include "drm_pipeline.h"
0020 
0021 #include <errno.h>
0022 #include <gbm.h>
0023 
0024 namespace KWin
0025 {
0026 
0027 DrmPipeline::Error DrmPipeline::presentLegacy()
0028 {
0029     if (Error err = applyPendingChangesLegacy(); err != Error::None) {
0030         return err;
0031     }
0032     const auto buffer = m_primaryLayer->currentBuffer();
0033     auto commit = std::make_unique<DrmLegacyCommit>(this, buffer);
0034     if (!commit->doPageflip(m_pending.presentationMode)) {
0035         qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno);
0036         return errnoToError();
0037     }
0038     m_commitThread->setPendingCommit(std::move(commit));
0039     return Error::None;
0040 }
0041 
0042 void DrmPipeline::forceLegacyModeset()
0043 {
0044     legacyModeset();
0045 }
0046 
0047 DrmPipeline::Error DrmPipeline::legacyModeset()
0048 {
0049     if (!m_primaryLayer->checkTestBuffer()) {
0050         return Error::TestBufferFailed;
0051     }
0052     auto commit = std::make_unique<DrmLegacyCommit>(this, m_primaryLayer->currentBuffer());
0053     if (!commit->doModeset(m_connector, m_pending.mode.get())) {
0054         qCWarning(KWIN_DRM) << "Modeset failed!" << strerror(errno);
0055         return errnoToError();
0056     }
0057     return Error::None;
0058 }
0059 
0060 DrmPipeline::Error DrmPipeline::commitPipelinesLegacy(const QList<DrmPipeline *> &pipelines, CommitMode mode)
0061 {
0062     Error err = Error::None;
0063     for (const auto &pipeline : pipelines) {
0064         err = pipeline->applyPendingChangesLegacy();
0065         if (err != Error::None) {
0066             break;
0067         }
0068     }
0069     if (err != Error::None) {
0070         // at least try to revert the config
0071         for (const auto &pipeline : pipelines) {
0072             pipeline->revertPendingChanges();
0073             pipeline->applyPendingChangesLegacy();
0074         }
0075     } else {
0076         for (const auto &pipeline : pipelines) {
0077             pipeline->applyPendingChanges();
0078             if (mode == CommitMode::CommitModeset && pipeline->activePending()) {
0079                 pipeline->pageFlipped(std::chrono::steady_clock::now().time_since_epoch(), PageflipType::Normal, PresentationMode::VSync);
0080             }
0081         }
0082     }
0083     return err;
0084 }
0085 
0086 DrmPipeline::Error DrmPipeline::applyPendingChangesLegacy()
0087 {
0088     if (!m_pending.active && m_pending.crtc) {
0089         drmModeSetCursor(gpu()->fd(), m_pending.crtc->id(), 0, 0, 0);
0090     }
0091     if (activePending()) {
0092         const bool shouldEnableVrr = m_pending.presentationMode == PresentationMode::AdaptiveSync || m_pending.presentationMode == PresentationMode::AdaptiveAsync;
0093         if (m_pending.crtc->vrrEnabled.isValid() && !m_pending.crtc->vrrEnabled.setPropertyLegacy(shouldEnableVrr)) {
0094             qCWarning(KWIN_DRM) << "Setting vrr failed!" << strerror(errno);
0095             return errnoToError();
0096         }
0097         if (m_connector->broadcastRGB.isValid()) {
0098             m_connector->broadcastRGB.setEnumLegacy(DrmConnector::rgbRangeToBroadcastRgb(m_pending.rgbRange));
0099         }
0100         if (m_connector->overscan.isValid()) {
0101             m_connector->overscan.setPropertyLegacy(m_pending.overscan);
0102         } else if (m_connector->underscan.isValid()) {
0103             const uint32_t hborder = calculateUnderscan();
0104             m_connector->underscan.setEnumLegacy(m_pending.overscan != 0 ? DrmConnector::UnderscanOptions::On : DrmConnector::UnderscanOptions::Off);
0105             m_connector->underscanVBorder.setPropertyLegacy(m_pending.overscan);
0106             m_connector->underscanHBorder.setPropertyLegacy(hborder);
0107         }
0108         if (m_connector->scalingMode.isValid() && m_connector->scalingMode.hasEnum(DrmConnector::ScalingMode::None)) {
0109             m_connector->scalingMode.setEnumLegacy(DrmConnector::ScalingMode::None);
0110         }
0111         const auto currentModeContent = m_pending.crtc->queryCurrentMode();
0112         if (m_pending.crtc != m_next.crtc || *m_pending.mode != currentModeContent) {
0113             qCDebug(KWIN_DRM) << "Using legacy path to set mode" << m_pending.mode->nativeMode()->name;
0114             Error err = legacyModeset();
0115             if (err != Error::None) {
0116                 return err;
0117             }
0118         }
0119         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) {
0120             qCWarning(KWIN_DRM) << "Setting gamma failed!" << strerror(errno);
0121             return errnoToError();
0122         }
0123         if (m_connector->contentType.isValid()) {
0124             m_connector->contentType.setEnumLegacy(m_pending.contentType);
0125         }
0126         setCursorLegacy();
0127     }
0128     if (!m_connector->dpms.setPropertyLegacy(activePending() ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF)) {
0129         qCWarning(KWIN_DRM) << "Setting legacy dpms failed!" << strerror(errno);
0130         return errnoToError();
0131     }
0132     return Error::None;
0133 }
0134 
0135 bool DrmPipeline::setCursorLegacy()
0136 {
0137     const auto bo = cursorLayer()->currentBuffer();
0138     uint32_t handle = 0;
0139     if (bo && bo->buffer() && cursorLayer()->isEnabled()) {
0140         const DmaBufAttributes *attributes = bo->buffer()->dmabufAttributes();
0141         if (drmPrimeFDToHandle(gpu()->fd(), attributes->fd[0].get(), &handle) != 0) {
0142             qCWarning(KWIN_DRM) << "drmPrimeFDToHandle() failed";
0143             return false;
0144         }
0145     }
0146 
0147     struct drm_mode_cursor2 arg = {
0148         .flags = DRM_MODE_CURSOR_BO | DRM_MODE_CURSOR_MOVE,
0149         .crtc_id = m_pending.crtc->id(),
0150         .x = int32_t(m_cursorLayer->position().x()),
0151         .y = int32_t(m_cursorLayer->position().y()),
0152         .width = (uint32_t)gpu()->cursorSize().width(),
0153         .height = (uint32_t)gpu()->cursorSize().height(),
0154         .handle = handle,
0155         .hot_x = int32_t(m_cursorLayer->hotspot().x()),
0156         .hot_y = int32_t(m_cursorLayer->hotspot().y()),
0157     };
0158     const int ret = drmIoctl(gpu()->fd(), DRM_IOCTL_MODE_CURSOR2, &arg);
0159 
0160     if (handle != 0) {
0161         drmCloseBufferHandle(gpu()->fd(), handle);
0162     }
0163     return ret == 0;
0164 }
0165 }