diff options
Diffstat (limited to 'xbmc/windowing/gbm/drm')
-rw-r--r-- | xbmc/windowing/gbm/drm/CMakeLists.txt | 21 | ||||
-rw-r--r-- | xbmc/windowing/gbm/drm/DRMAtomic.cpp | 344 | ||||
-rw-r--r-- | xbmc/windowing/gbm/drm/DRMAtomic.h | 81 | ||||
-rw-r--r-- | xbmc/windowing/gbm/drm/DRMConnector.cpp | 99 | ||||
-rw-r--r-- | xbmc/windowing/gbm/drm/DRMConnector.h | 53 | ||||
-rw-r--r-- | xbmc/windowing/gbm/drm/DRMCrtc.cpp | 26 | ||||
-rw-r--r-- | xbmc/windowing/gbm/drm/DRMCrtc.h | 46 | ||||
-rw-r--r-- | xbmc/windowing/gbm/drm/DRMEncoder.cpp | 23 | ||||
-rw-r--r-- | xbmc/windowing/gbm/drm/DRMEncoder.h | 43 | ||||
-rw-r--r-- | xbmc/windowing/gbm/drm/DRMLegacy.cpp | 141 | ||||
-rw-r--r-- | xbmc/windowing/gbm/drm/DRMLegacy.h | 39 | ||||
-rw-r--r-- | xbmc/windowing/gbm/drm/DRMObject.cpp | 133 | ||||
-rw-r--r-- | xbmc/windowing/gbm/drm/DRMObject.h | 71 | ||||
-rw-r--r-- | xbmc/windowing/gbm/drm/DRMPlane.cpp | 118 | ||||
-rw-r--r-- | xbmc/windowing/gbm/drm/DRMPlane.h | 58 | ||||
-rw-r--r-- | xbmc/windowing/gbm/drm/DRMUtils.cpp | 740 | ||||
-rw-r--r-- | xbmc/windowing/gbm/drm/DRMUtils.h | 103 | ||||
-rw-r--r-- | xbmc/windowing/gbm/drm/OffScreenModeSetting.cpp | 52 | ||||
-rw-r--r-- | xbmc/windowing/gbm/drm/OffScreenModeSetting.h | 38 |
19 files changed, 2229 insertions, 0 deletions
diff --git a/xbmc/windowing/gbm/drm/CMakeLists.txt b/xbmc/windowing/gbm/drm/CMakeLists.txt new file mode 100644 index 0000000..8bd07ea --- /dev/null +++ b/xbmc/windowing/gbm/drm/CMakeLists.txt @@ -0,0 +1,21 @@ +set(SOURCES DRMAtomic.cpp + DRMConnector.cpp + DRMCrtc.cpp + DRMEncoder.cpp + DRMLegacy.cpp + DRMObject.cpp + DRMPlane.cpp + DRMUtils.cpp + OffScreenModeSetting.cpp) + +set(HEADERS DRMAtomic.h + DRMConnector.h + DRMCrtc.h + DRMEncoder.h + DRMLegacy.h + DRMObject.h + DRMPlane.h + DRMUtils.h + OffScreenModeSetting.h) + +core_add_library(windowing_gbm_drm) diff --git a/xbmc/windowing/gbm/drm/DRMAtomic.cpp b/xbmc/windowing/gbm/drm/DRMAtomic.cpp new file mode 100644 index 0000000..5d61a69 --- /dev/null +++ b/xbmc/windowing/gbm/drm/DRMAtomic.cpp @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "DRMAtomic.h" + +#include "ServiceBroker.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIWindowManager.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include "settings/lib/Setting.h" +#include "utils/log.h" + +#include <errno.h> +#include <string.h> + +#include <drm_fourcc.h> +#include <drm_mode.h> +#include <unistd.h> + +using namespace KODI::WINDOWING::GBM; + +namespace +{ + +const auto SETTING_VIDEOSCREEN_HW_SCALING_FILTER = "videoscreen.hwscalingfilter"; + +uint32_t GetScalingFactor(uint32_t srcWidth, + uint32_t srcHeight, + uint32_t destWidth, + uint32_t destHeight) +{ + uint32_t factor_W = destWidth / srcWidth; + uint32_t factor_H = destHeight / srcHeight; + if (factor_W != factor_H) + return (factor_W < factor_H) ? factor_W : factor_H; + return factor_W; +} + +} // namespace + +bool CDRMAtomic::SetScalingFilter(CDRMObject* object, const char* name, const char* type) +{ + bool result; + uint64_t value; + std::tie(result, value) = m_gui_plane->GetPropertyValue(name, type); + if (!result) + return false; + + if (!AddProperty(object, name, value)) + return false; + + uint32_t mar_scale_factor = + GetScalingFactor(m_width, m_height, m_mode->hdisplay, m_mode->vdisplay); + AddProperty(object, "CRTC_W", (mar_scale_factor * m_width)); + AddProperty(object, "CRTC_H", (mar_scale_factor * m_height)); + + return true; +} + +void CDRMAtomic::DrmAtomicCommit(int fb_id, int flags, bool rendered, bool videoLayer) +{ + uint32_t blob_id; + + if (flags & DRM_MODE_ATOMIC_ALLOW_MODESET) + { + if (!AddProperty(m_connector, "CRTC_ID", m_crtc->GetCrtcId())) + return; + + if (drmModeCreatePropertyBlob(m_fd, m_mode, sizeof(*m_mode), &blob_id) != 0) + return; + + if (m_active && m_orig_crtc && m_orig_crtc->GetCrtcId() != m_crtc->GetCrtcId()) + { + // if using a different CRTC than the original, disable original to avoid EINVAL + if (!AddProperty(m_orig_crtc, "MODE_ID", 0)) + return; + + if (!AddProperty(m_orig_crtc, "ACTIVE", 0)) + return; + } + + if (!AddProperty(m_crtc, "MODE_ID", blob_id)) + return; + + if (!AddProperty(m_crtc, "ACTIVE", m_active ? 1 : 0)) + return; + } + + if (rendered) + { + AddProperty(m_gui_plane, "FB_ID", fb_id); + AddProperty(m_gui_plane, "CRTC_ID", m_crtc->GetCrtcId()); + AddProperty(m_gui_plane, "SRC_X", 0); + AddProperty(m_gui_plane, "SRC_Y", 0); + AddProperty(m_gui_plane, "SRC_W", m_width << 16); + AddProperty(m_gui_plane, "SRC_H", m_height << 16); + AddProperty(m_gui_plane, "CRTC_X", 0); + AddProperty(m_gui_plane, "CRTC_Y", 0); + //! @todo: disabled until upstream kernel changes are merged + // if (DisplayHardwareScalingEnabled()) + // { + // SetScalingFilter(m_gui_plane, "SCALING_FILTER", "Nearest Neighbor"); + // } + // else + { + AddProperty(m_gui_plane, "CRTC_W", m_mode->hdisplay); + AddProperty(m_gui_plane, "CRTC_H", m_mode->vdisplay); + } + + } + else if (videoLayer && !CServiceBroker::GetGUI()->GetWindowManager().HasVisibleControls()) + { + // disable gui plane when video layer is active and gui has no visible controls + AddProperty(m_gui_plane, "FB_ID", 0); + AddProperty(m_gui_plane, "CRTC_ID", 0); + } + + if (CServiceBroker::GetLogging().CanLogComponent(LOGWINDOWING)) + m_req->LogAtomicRequest(); + + auto ret = drmModeAtomicCommit(m_fd, m_req->Get(), flags | DRM_MODE_ATOMIC_TEST_ONLY, nullptr); + if (ret < 0) + { + CLog::Log(LOGERROR, + "CDRMAtomic::{} - test commit failed: ({}) - falling back to last successful atomic " + "request", + __FUNCTION__, strerror(errno)); + + auto oldRequest = m_atomicRequestQueue.front().get(); + CDRMAtomicRequest::LogAtomicDiff(m_req, oldRequest); + m_req = oldRequest; + + // update the old atomic request with the new fb id to avoid tearing + if (rendered) + AddProperty(m_gui_plane, "FB_ID", fb_id); + } + + ret = drmModeAtomicCommit(m_fd, m_req->Get(), flags, nullptr); + if (ret < 0) + { + CLog::Log(LOGERROR, "CDRMAtomic::{} - atomic commit failed: {}", __FUNCTION__, + strerror(errno)); + } + + if (flags & DRM_MODE_ATOMIC_ALLOW_MODESET) + { + if (drmModeDestroyPropertyBlob(m_fd, blob_id) != 0) + CLog::Log(LOGERROR, "CDRMAtomic::{} - failed to destroy property blob: {}", __FUNCTION__, + strerror(errno)); + } + + if (m_atomicRequestQueue.size() > 1) + m_atomicRequestQueue.pop_back(); + + m_atomicRequestQueue.emplace_back(std::make_unique<CDRMAtomicRequest>()); + m_req = m_atomicRequestQueue.back().get(); +} + +void CDRMAtomic::FlipPage(struct gbm_bo *bo, bool rendered, bool videoLayer) +{ + struct drm_fb *drm_fb = nullptr; + + if (rendered) + { + if (videoLayer) + m_gui_plane->SetFormat(CDRMUtils::FourCCWithAlpha(m_gui_plane->GetFormat())); + else + m_gui_plane->SetFormat(CDRMUtils::FourCCWithoutAlpha(m_gui_plane->GetFormat())); + + drm_fb = CDRMUtils::DrmFbGetFromBo(bo); + if (!drm_fb) + { + CLog::Log(LOGERROR, "CDRMAtomic::{} - Failed to get a new FBO", __FUNCTION__); + return; + } + } + + uint32_t flags = 0; + + if (m_need_modeset) + { + flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + m_need_modeset = false; + CLog::Log(LOGDEBUG, "CDRMAtomic::{} - Execute modeset at next commit", __FUNCTION__); + } + + DrmAtomicCommit(!drm_fb ? 0 : drm_fb->fb_id, flags, rendered, videoLayer); +} + +bool CDRMAtomic::InitDrm() +{ + if (!CDRMUtils::OpenDrm(true)) + return false; + + auto ret = drmSetClientCap(m_fd, DRM_CLIENT_CAP_ATOMIC, 1); + if (ret) + { + CLog::Log(LOGERROR, "CDRMAtomic::{} - no atomic modesetting support: {}", __FUNCTION__, + strerror(errno)); + return false; + } + + m_atomicRequestQueue.emplace_back(std::make_unique<CDRMAtomicRequest>()); + m_req = m_atomicRequestQueue.back().get(); + + if (!CDRMUtils::InitDrm()) + return false; + + for (auto& plane : m_planes) + { + AddProperty(plane.get(), "FB_ID", 0); + AddProperty(plane.get(), "CRTC_ID", 0); + } + + CLog::Log(LOGDEBUG, "CDRMAtomic::{} - initialized atomic DRM", __FUNCTION__); + + //! @todo: disabled until upstream kernel changes are merged + // if (m_gui_plane->SupportsProperty("SCALING_FILTER")) + // { + // const std::shared_ptr<CSettings> settings = + // CServiceBroker::GetSettingsComponent()->GetSettings(); + // settings->GetSetting(SETTING_VIDEOSCREEN_HW_SCALING_FILTER)->SetVisible(true); + // } + + return true; +} + +void CDRMAtomic::DestroyDrm() +{ + CDRMUtils::DestroyDrm(); +} + +bool CDRMAtomic::SetVideoMode(const RESOLUTION_INFO& res, struct gbm_bo *bo) +{ + m_need_modeset = true; + + return true; +} + +bool CDRMAtomic::SetActive(bool active) +{ + m_need_modeset = true; + m_active = active; + + return true; +} + +bool CDRMAtomic::AddProperty(CDRMObject* object, const char* name, uint64_t value) +{ + return m_req->AddProperty(object, name, value); +} + +bool CDRMAtomic::DisplayHardwareScalingEnabled() +{ + auto settings = CServiceBroker::GetSettingsComponent()->GetSettings(); + + if (settings && settings->GetBool(SETTING_VIDEOSCREEN_HW_SCALING_FILTER)) + return true; + + return false; +} + +CDRMAtomic::CDRMAtomicRequest::CDRMAtomicRequest() : m_atomicRequest(drmModeAtomicAlloc()) +{ +} + +bool CDRMAtomic::CDRMAtomicRequest::AddProperty(CDRMObject* object, const char* name, uint64_t value) +{ + uint32_t propertyId = object->GetPropertyId(name); + if (propertyId == 0) + return false; + + int ret = drmModeAtomicAddProperty(m_atomicRequest.get(), object->GetId(), propertyId, value); + if (ret < 0) + return false; + + m_atomicRequestItems[object][propertyId] = value; + return true; +} + +void CDRMAtomic::CDRMAtomicRequest::LogAtomicDiff(CDRMAtomicRequest* current, + CDRMAtomicRequest* old) +{ + std::map<CDRMObject*, std::map<uint32_t, uint64_t>> atomicDiff; + + for (const auto& object : current->m_atomicRequestItems) + { + auto sameObject = old->m_atomicRequestItems.find(object.first); + if (sameObject != old->m_atomicRequestItems.end()) + { + std::map<uint32_t, uint64_t> propertyDiff; + + std::set_difference(current->m_atomicRequestItems[object.first].begin(), + current->m_atomicRequestItems[object.first].end(), + old->m_atomicRequestItems[object.first].begin(), + old->m_atomicRequestItems[object.first].end(), + std::inserter(propertyDiff, propertyDiff.begin())); + + atomicDiff[object.first] = propertyDiff; + } + else + { + atomicDiff[object.first] = current->m_atomicRequestItems[object.first]; + } + } + + CLog::Log(LOGDEBUG, "CDRMAtomicRequest::{} - DRM Atomic Request Diff:", __FUNCTION__); + + LogAtomicRequest(LOGERROR, atomicDiff); +} + +void CDRMAtomic::CDRMAtomicRequest::LogAtomicRequest() +{ + CLog::Log(LOGDEBUG, "CDRMAtomicRequest::{} - DRM Atomic Request:", __FUNCTION__); + LogAtomicRequest(LOGDEBUG, m_atomicRequestItems); +} + +void CDRMAtomic::CDRMAtomicRequest::LogAtomicRequest( + uint8_t logLevel, std::map<CDRMObject*, std::map<uint32_t, uint64_t>>& atomicRequestItems) +{ + std::string message; + for (const auto& object : atomicRequestItems) + { + message.append("\nObject: " + object.first->GetTypeName() + + "\tID: " + std::to_string(object.first->GetId())); + for (const auto& property : object.second) + message.append("\n Property: " + object.first->GetPropertyName(property.first) + + "\tID: " + std::to_string(property.first) + + "\tValue: " + std::to_string(property.second)); + } + + CLog::Log(logLevel, "{}", message); +} + +void CDRMAtomic::CDRMAtomicRequest::DrmModeAtomicReqDeleter::operator()(drmModeAtomicReqPtr p) const +{ + drmModeAtomicFree(p); +}; diff --git a/xbmc/windowing/gbm/drm/DRMAtomic.h b/xbmc/windowing/gbm/drm/DRMAtomic.h new file mode 100644 index 0000000..ca2cd9a --- /dev/null +++ b/xbmc/windowing/gbm/drm/DRMAtomic.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "DRMUtils.h" + +#include <cstdint> +#include <deque> +#include <map> +#include <memory> + +namespace KODI +{ +namespace WINDOWING +{ +namespace GBM +{ + +class CDRMAtomic : public CDRMUtils +{ +public: + CDRMAtomic() = default; + ~CDRMAtomic() override = default; + void FlipPage(struct gbm_bo* bo, bool rendered, bool videoLayer) override; + bool SetVideoMode(const RESOLUTION_INFO& res, struct gbm_bo* bo) override; + bool SetActive(bool active) override; + bool InitDrm() override; + void DestroyDrm() override; + bool AddProperty(CDRMObject* object, const char* name, uint64_t value); + + bool DisplayHardwareScalingEnabled(); + +private: + void DrmAtomicCommit(int fb_id, int flags, bool rendered, bool videoLayer); + + bool SetScalingFilter(CDRMObject* object, const char* name, const char* type); + + bool m_need_modeset; + bool m_active = true; + + class CDRMAtomicRequest + { + public: + CDRMAtomicRequest(); + ~CDRMAtomicRequest() = default; + CDRMAtomicRequest(const CDRMAtomicRequest& right) = delete; + + drmModeAtomicReqPtr Get() const { return m_atomicRequest.get(); } + + bool AddProperty(CDRMObject* object, const char* name, uint64_t value); + void LogAtomicRequest(); + + static void LogAtomicDiff(CDRMAtomicRequest* current, CDRMAtomicRequest* old); + + private: + static void LogAtomicRequest( + uint8_t logLevel, std::map<CDRMObject*, std::map<uint32_t, uint64_t>>& atomicRequestItems); + + std::map<CDRMObject*, std::map<uint32_t, uint64_t>> m_atomicRequestItems; + + struct DrmModeAtomicReqDeleter + { + void operator()(drmModeAtomicReqPtr p) const; + }; + + std::unique_ptr<drmModeAtomicReq, DrmModeAtomicReqDeleter> m_atomicRequest; + }; + + CDRMAtomicRequest* m_req = nullptr; + std::deque<std::unique_ptr<CDRMAtomicRequest>> m_atomicRequestQueue; +}; + +} +} +} diff --git a/xbmc/windowing/gbm/drm/DRMConnector.cpp b/xbmc/windowing/gbm/drm/DRMConnector.cpp new file mode 100644 index 0000000..fc76b90 --- /dev/null +++ b/xbmc/windowing/gbm/drm/DRMConnector.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2005-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "DRMConnector.h" + +#include "utils/XTimeUtils.h" +#include "utils/log.h" + +#include <map> + +using namespace KODI::WINDOWING::GBM; + +using namespace std::chrono_literals; + +namespace +{ + +std::map<int, std::string> connectorTypeNames = { + {DRM_MODE_CONNECTOR_Unknown, "unknown"}, + {DRM_MODE_CONNECTOR_VGA, "VGA"}, + {DRM_MODE_CONNECTOR_DVII, "DVI-I"}, + {DRM_MODE_CONNECTOR_DVID, "DVI-D"}, + {DRM_MODE_CONNECTOR_DVIA, "DVI-A"}, + {DRM_MODE_CONNECTOR_Composite, "composite"}, + {DRM_MODE_CONNECTOR_SVIDEO, "s-video"}, + {DRM_MODE_CONNECTOR_LVDS, "LVDS"}, + {DRM_MODE_CONNECTOR_Component, "component"}, + {DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN"}, + {DRM_MODE_CONNECTOR_DisplayPort, "DP"}, + {DRM_MODE_CONNECTOR_HDMIA, "HDMI-A"}, + {DRM_MODE_CONNECTOR_HDMIB, "HDMI-B"}, + {DRM_MODE_CONNECTOR_TV, "TV"}, + {DRM_MODE_CONNECTOR_eDP, "eDP"}, + {DRM_MODE_CONNECTOR_VIRTUAL, "Virtual"}, + {DRM_MODE_CONNECTOR_DSI, "DSI"}, + {DRM_MODE_CONNECTOR_DPI, "DPI"}, +}; + +std::map<drmModeConnection, std::string> connectorStatusNames = { + {DRM_MODE_CONNECTED, "connected"}, + {DRM_MODE_DISCONNECTED, "disconnected"}, + {DRM_MODE_UNKNOWNCONNECTION, "unknown"}, +}; + +} // namespace + +CDRMConnector::CDRMConnector(int fd, uint32_t connector) + : CDRMObject(fd), m_connector(drmModeGetConnector(m_fd, connector)) +{ + if (!m_connector) + throw std::runtime_error("drmModeGetConnector failed: " + std::string{strerror(errno)}); + + if (!GetProperties(m_connector->connector_id, DRM_MODE_OBJECT_CONNECTOR)) + throw std::runtime_error("failed to get properties for connector: " + + std::to_string(m_connector->connector_id)); +} + +bool CDRMConnector::CheckConnector() +{ + unsigned retryCnt = 7; + while (!IsConnected() && retryCnt > 0) + { + CLog::Log(LOGDEBUG, "CDRMConnector::{} - connector is disconnected", __FUNCTION__); + retryCnt--; + KODI::TIME::Sleep(1s); + + m_connector.reset(drmModeGetConnector(m_fd, m_connector->connector_id)); + } + + return m_connector->connection == DRM_MODE_CONNECTED; +} + +std::string CDRMConnector::GetType() +{ + auto typeName = connectorTypeNames.find(m_connector->connector_type); + if (typeName == connectorTypeNames.end()) + return connectorTypeNames[DRM_MODE_CONNECTOR_Unknown]; + + return typeName->second; +} + +std::string CDRMConnector::GetStatus() +{ + auto statusName = connectorStatusNames.find(m_connector->connection); + if (statusName == connectorStatusNames.end()) + return connectorStatusNames[DRM_MODE_UNKNOWNCONNECTION]; + + return statusName->second; +} + +std::string CDRMConnector::GetName() +{ + return GetType() + "-" + std::to_string(m_connector->connector_type_id); +} diff --git a/xbmc/windowing/gbm/drm/DRMConnector.h b/xbmc/windowing/gbm/drm/DRMConnector.h new file mode 100644 index 0000000..5a29222 --- /dev/null +++ b/xbmc/windowing/gbm/drm/DRMConnector.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "DRMObject.h" + +#include <string> + +namespace KODI +{ +namespace WINDOWING +{ +namespace GBM +{ + +class CDRMConnector : public CDRMObject +{ +public: + explicit CDRMConnector(int fd, uint32_t connector); + CDRMConnector(const CDRMConnector&) = delete; + CDRMConnector& operator=(const CDRMConnector&) = delete; + ~CDRMConnector() = default; + + std::string GetType(); + std::string GetStatus(); + std::string GetName(); + + uint32_t GetEncoderId() const { return m_connector->encoder_id; } + uint32_t* GetConnectorId() const { return &m_connector->connector_id; } + int GetModesCount() const { return m_connector->count_modes; } + drmModeModeInfoPtr GetModeForIndex(int index) const { return &m_connector->modes[index]; } + + bool IsConnected() { return m_connector->connection == DRM_MODE_CONNECTED; } + bool CheckConnector(); + +private: + struct DrmModeConnectorDeleter + { + void operator()(drmModeConnector* p) { drmModeFreeConnector(p); } + }; + + std::unique_ptr<drmModeConnector, DrmModeConnectorDeleter> m_connector; +}; + +} // namespace GBM +} // namespace WINDOWING +} // namespace KODI diff --git a/xbmc/windowing/gbm/drm/DRMCrtc.cpp b/xbmc/windowing/gbm/drm/DRMCrtc.cpp new file mode 100644 index 0000000..426fd95 --- /dev/null +++ b/xbmc/windowing/gbm/drm/DRMCrtc.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2005-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "DRMCrtc.h" + +#include <cstring> +#include <errno.h> +#include <stdexcept> +#include <string> + +using namespace KODI::WINDOWING::GBM; + +CDRMCrtc::CDRMCrtc(int fd, uint32_t crtc) : CDRMObject(fd), m_crtc(drmModeGetCrtc(m_fd, crtc)) +{ + if (!m_crtc) + throw std::runtime_error("drmModeGetCrtc failed: " + std::string{strerror(errno)}); + + if (!GetProperties(m_crtc->crtc_id, DRM_MODE_OBJECT_CRTC)) + throw std::runtime_error("failed to get properties for crtc: " + + std::to_string(m_crtc->crtc_id)); +} diff --git a/xbmc/windowing/gbm/drm/DRMCrtc.h b/xbmc/windowing/gbm/drm/DRMCrtc.h new file mode 100644 index 0000000..a1aadc2 --- /dev/null +++ b/xbmc/windowing/gbm/drm/DRMCrtc.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2005-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "DRMObject.h" + +namespace KODI +{ +namespace WINDOWING +{ +namespace GBM +{ + +class CDRMCrtc : public CDRMObject +{ +public: + explicit CDRMCrtc(int fd, uint32_t crtc); + CDRMCrtc(const CDRMCrtc&) = delete; + CDRMCrtc& operator=(const CDRMCrtc&) = delete; + ~CDRMCrtc() = default; + + uint32_t GetCrtcId() const { return m_crtc->crtc_id; } + uint32_t GetBufferId() const { return m_crtc->buffer_id; } + uint32_t GetX() const { return m_crtc->x; } + uint32_t GetY() const { return m_crtc->y; } + drmModeModeInfoPtr GetMode() const { return &m_crtc->mode; } + bool GetModeValid() const { return m_crtc->mode_valid != 0; } + +private: + struct DrmModeCrtcDeleter + { + void operator()(drmModeCrtc* p) { drmModeFreeCrtc(p); } + }; + + std::unique_ptr<drmModeCrtc, DrmModeCrtcDeleter> m_crtc; +}; + +} // namespace GBM +} // namespace WINDOWING +} // namespace KODI diff --git a/xbmc/windowing/gbm/drm/DRMEncoder.cpp b/xbmc/windowing/gbm/drm/DRMEncoder.cpp new file mode 100644 index 0000000..e4289c6 --- /dev/null +++ b/xbmc/windowing/gbm/drm/DRMEncoder.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2005-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "DRMEncoder.h" + +#include <cstring> +#include <errno.h> +#include <stdexcept> +#include <string> + +using namespace KODI::WINDOWING::GBM; + +CDRMEncoder::CDRMEncoder(int fd, uint32_t encoder) + : CDRMObject(fd), m_encoder(drmModeGetEncoder(m_fd, encoder)) +{ + if (!m_encoder) + throw std::runtime_error("drmModeGetEncoder failed: " + std::string{strerror(errno)}); +} diff --git a/xbmc/windowing/gbm/drm/DRMEncoder.h b/xbmc/windowing/gbm/drm/DRMEncoder.h new file mode 100644 index 0000000..aceb276 --- /dev/null +++ b/xbmc/windowing/gbm/drm/DRMEncoder.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "DRMObject.h" + +namespace KODI +{ +namespace WINDOWING +{ +namespace GBM +{ + +class CDRMEncoder : public CDRMObject +{ +public: + explicit CDRMEncoder(int fd, uint32_t encoder); + CDRMEncoder(const CDRMEncoder&) = delete; + CDRMEncoder& operator=(const CDRMEncoder&) = delete; + ~CDRMEncoder() = default; + + uint32_t GetEncoderId() const { return m_encoder->encoder_id; } + uint32_t GetCrtcId() const { return m_encoder->crtc_id; } + uint32_t GetPossibleCrtcs() const { return m_encoder->possible_crtcs; } + +private: + struct DrmModeEncoderDeleter + { + void operator()(drmModeEncoder* p) { drmModeFreeEncoder(p); } + }; + + std::unique_ptr<drmModeEncoder, DrmModeEncoderDeleter> m_encoder; +}; + +} // namespace GBM +} // namespace WINDOWING +} // namespace KODI diff --git a/xbmc/windowing/gbm/drm/DRMLegacy.cpp b/xbmc/windowing/gbm/drm/DRMLegacy.cpp new file mode 100644 index 0000000..418d067 --- /dev/null +++ b/xbmc/windowing/gbm/drm/DRMLegacy.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "DRMLegacy.h" + +#include "guilib/gui3d.h" +#include "settings/Settings.h" +#include "utils/log.h" + +#include <errno.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <drm_mode.h> +#include <fcntl.h> +#include <poll.h> +#include <unistd.h> + +using namespace KODI::WINDOWING::GBM; + +static int flip_happening = 0; + +bool CDRMLegacy::SetVideoMode(const RESOLUTION_INFO& res, struct gbm_bo *bo) +{ + struct drm_fb *drm_fb = DrmFbGetFromBo(bo); + + auto ret = drmModeSetCrtc(m_fd, m_crtc->GetCrtcId(), drm_fb->fb_id, 0, 0, + m_connector->GetConnectorId(), 1, m_mode); + + if(ret < 0) + { + CLog::Log(LOGERROR, "CDRMLegacy::{} - failed to set crtc mode: {}x{}{} @ {} Hz", __FUNCTION__, + m_mode->hdisplay, m_mode->vdisplay, + m_mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "", m_mode->vrefresh); + + return false; + } + + CLog::Log(LOGDEBUG, "CDRMLegacy::{} - set crtc mode: {}x{}{} @ {} Hz", __FUNCTION__, + m_mode->hdisplay, m_mode->vdisplay, m_mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "", + m_mode->vrefresh); + + return true; +} + +void CDRMLegacy::PageFlipHandler(int fd, unsigned int frame, unsigned int sec, + unsigned int usec, void *data) +{ + (void) fd, (void) frame, (void) sec, (void) usec; + + int *flip_happening = static_cast<int *>(data); + *flip_happening = 0; +} + +bool CDRMLegacy::WaitingForFlip() +{ + if (!flip_happening) + return false; + + struct pollfd drm_fds = + { + m_fd, + POLLIN, + 0, + }; + + drmEventContext drm_evctx{}; + drm_evctx.version = DRM_EVENT_CONTEXT_VERSION; + drm_evctx.page_flip_handler = PageFlipHandler; + + while(flip_happening) + { + auto ret = poll(&drm_fds, 1, -1); + + if(ret < 0) + return true; + + if(drm_fds.revents & (POLLHUP | POLLERR)) + return true; + + if(drm_fds.revents & POLLIN) + drmHandleEvent(m_fd, &drm_evctx); + } + + return false; +} + +bool CDRMLegacy::QueueFlip(struct gbm_bo *bo) +{ + struct drm_fb *drm_fb = DrmFbGetFromBo(bo); + + auto ret = drmModePageFlip(m_fd, m_crtc->GetCrtcId(), drm_fb->fb_id, DRM_MODE_PAGE_FLIP_EVENT, + &flip_happening); + + if(ret) + { + CLog::Log(LOGDEBUG, "CDRMLegacy::{} - failed to queue DRM page flip", __FUNCTION__); + return false; + } + + return true; +} + +void CDRMLegacy::FlipPage(struct gbm_bo *bo, bool rendered, bool videoLayer) +{ + if (rendered || videoLayer) + { + flip_happening = QueueFlip(bo); + WaitingForFlip(); + } +} + +bool CDRMLegacy::InitDrm() +{ + if (!CDRMUtils::OpenDrm(true)) + return false; + + if (!CDRMUtils::InitDrm()) + return false; + + CLog::Log(LOGDEBUG, "CDRMLegacy::{} - initialized legacy DRM", __FUNCTION__); + return true; +} + +bool CDRMLegacy::SetActive(bool active) +{ + if (!m_connector->SetProperty("DPMS", active ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF)) + { + CLog::Log(LOGDEBUG, "CDRMLegacy::{} - failed to set DPMS property", __FUNCTION__); + return false; + } + + return true; +} diff --git a/xbmc/windowing/gbm/drm/DRMLegacy.h b/xbmc/windowing/gbm/drm/DRMLegacy.h new file mode 100644 index 0000000..2b7ff45 --- /dev/null +++ b/xbmc/windowing/gbm/drm/DRMLegacy.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "DRMUtils.h" + +namespace KODI +{ +namespace WINDOWING +{ +namespace GBM +{ + +class CDRMLegacy : public CDRMUtils +{ +public: + CDRMLegacy() = default; + ~CDRMLegacy() override = default; + void FlipPage(struct gbm_bo* bo, bool rendered, bool videoLayer) override; + bool SetVideoMode(const RESOLUTION_INFO& res, struct gbm_bo* bo) override; + bool SetActive(bool active) override; + bool InitDrm() override; + +private: + bool WaitingForFlip(); + bool QueueFlip(struct gbm_bo *bo); + static void PageFlipHandler(int fd, unsigned int frame, unsigned int sec, + unsigned int usec, void *data); +}; + +} +} +} diff --git a/xbmc/windowing/gbm/drm/DRMObject.cpp b/xbmc/windowing/gbm/drm/DRMObject.cpp new file mode 100644 index 0000000..599bb61 --- /dev/null +++ b/xbmc/windowing/gbm/drm/DRMObject.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2005-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "DRMObject.h" + +#include "utils/log.h" + +#include <algorithm> +#include <array> + +using namespace KODI::WINDOWING::GBM; + +namespace +{ + +constexpr std::array<std::pair<uint32_t, const char*>, 8> DrmModeObjectTypes = { + {{DRM_MODE_OBJECT_CRTC, "crtc"}, + {DRM_MODE_OBJECT_CONNECTOR, "connector"}, + {DRM_MODE_OBJECT_ENCODER, "encoder"}, + {DRM_MODE_OBJECT_MODE, "mode"}, + {DRM_MODE_OBJECT_PROPERTY, "property"}, + {DRM_MODE_OBJECT_FB, "framebuffer"}, + {DRM_MODE_OBJECT_BLOB, "blob"}, + {DRM_MODE_OBJECT_PLANE, "plane"}}}; +} + +CDRMObject::CDRMObject(int fd) : m_fd(fd) +{ +} + +std::string CDRMObject::GetTypeName() const +{ + auto name = std::find_if(DrmModeObjectTypes.begin(), DrmModeObjectTypes.end(), + [this](const auto& p) { return p.first == m_type; }); + if (name != DrmModeObjectTypes.end()) + return name->second; + + return "invalid type"; +} + +std::string CDRMObject::GetPropertyName(uint32_t propertyId) const +{ + auto prop = std::find_if(m_propsInfo.begin(), m_propsInfo.end(), + [&propertyId](const auto& p) { return p->prop_id == propertyId; }); + if (prop != m_propsInfo.end()) + return prop->get()->name; + + return "invalid property"; +} + +uint32_t CDRMObject::GetPropertyId(const std::string& name) const +{ + auto property = std::find_if(m_propsInfo.begin(), m_propsInfo.end(), + [&name](const auto& prop) { return prop->name == name; }); + + if (property != m_propsInfo.end()) + return property->get()->prop_id; + + return 0; +} + +bool CDRMObject::GetProperties(uint32_t id, uint32_t type) +{ + m_props.reset(drmModeObjectGetProperties(m_fd, id, type)); + if (!m_props) + return false; + + m_id = id; + m_type = type; + + for (uint32_t i = 0; i < m_props->count_props; i++) + m_propsInfo.emplace_back(std::unique_ptr<drmModePropertyRes, DrmModePropertyResDeleter>( + drmModeGetProperty(m_fd, m_props->props[i]))); + + return true; +} + +//! @todo: improve with c++17 +std::tuple<bool, uint64_t> CDRMObject::GetPropertyValue(const std::string& name, + const std::string& valueName) const +{ + auto property = std::find_if(m_propsInfo.begin(), m_propsInfo.end(), + [&name](const auto& prop) { return prop->name == name; }); + + if (property == m_propsInfo.end()) + return std::make_tuple(false, 0); + + auto prop = property->get(); + + if (!static_cast<bool>(drm_property_type_is(prop, DRM_MODE_PROP_ENUM))) + return std::make_tuple(false, 0); + + for (int j = 0; j < prop->count_enums; j++) + { + if (prop->enums[j].name != valueName) + continue; + + return std::make_tuple(true, prop->enums[j].value); + } + + return std::make_tuple(false, 0); +} + +bool CDRMObject::SetProperty(const std::string& name, uint64_t value) +{ + auto property = std::find_if(m_propsInfo.begin(), m_propsInfo.end(), + [&name](const auto& prop) { return prop->name == name; }); + + if (property != m_propsInfo.end()) + { + int ret = drmModeObjectSetProperty(m_fd, m_id, m_type, property->get()->prop_id, value); + if (ret == 0) + return true; + } + + return false; +} + +bool CDRMObject::SupportsProperty(const std::string& name) +{ + auto property = std::find_if(m_propsInfo.begin(), m_propsInfo.end(), + [&name](const auto& prop) { return prop->name == name; }); + + if (property != m_propsInfo.end()) + return true; + + return false; +} diff --git a/xbmc/windowing/gbm/drm/DRMObject.h b/xbmc/windowing/gbm/drm/DRMObject.h new file mode 100644 index 0000000..e2ae326 --- /dev/null +++ b/xbmc/windowing/gbm/drm/DRMObject.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2005-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include <cstddef> +#include <cstdint> +#include <memory> +#include <vector> + +#include <xf86drmMode.h> + +namespace KODI +{ +namespace WINDOWING +{ +namespace GBM +{ + +class CDRMObject +{ +public: + CDRMObject(const CDRMObject&) = delete; + CDRMObject& operator=(const CDRMObject&) = delete; + virtual ~CDRMObject() = default; + + std::string GetTypeName() const; + std::string GetPropertyName(uint32_t propertyId) const; + + uint32_t GetId() const { return m_id; } + uint32_t GetPropertyId(const std::string& name) const; + std::tuple<bool, uint64_t> GetPropertyValue(const std::string& name, + const std::string& valueName) const; + + bool SetProperty(const std::string& name, uint64_t value); + bool SupportsProperty(const std::string& name); + +protected: + explicit CDRMObject(int fd); + + bool GetProperties(uint32_t id, uint32_t type); + + struct DrmModeObjectPropertiesDeleter + { + void operator()(drmModeObjectProperties* p) { drmModeFreeObjectProperties(p); } + }; + + std::unique_ptr<drmModeObjectProperties, DrmModeObjectPropertiesDeleter> m_props; + + struct DrmModePropertyResDeleter + { + void operator()(drmModePropertyRes* p) { drmModeFreeProperty(p); } + }; + + std::vector<std::unique_ptr<drmModePropertyRes, DrmModePropertyResDeleter>> m_propsInfo; + + int m_fd{-1}; + +private: + uint32_t m_id{0}; + uint32_t m_type{0}; +}; + +} // namespace GBM +} // namespace WINDOWING +} // namespace KODI diff --git a/xbmc/windowing/gbm/drm/DRMPlane.cpp b/xbmc/windowing/gbm/drm/DRMPlane.cpp new file mode 100644 index 0000000..90b5660 --- /dev/null +++ b/xbmc/windowing/gbm/drm/DRMPlane.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2005-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "DRMPlane.h" + +#include "DRMUtils.h" +#include "utils/DRMHelpers.h" +#include "utils/StringUtils.h" +#include "utils/log.h" + +using namespace KODI::WINDOWING::GBM; + +CDRMPlane::CDRMPlane(int fd, uint32_t plane) : CDRMObject(fd), m_plane(drmModeGetPlane(m_fd, plane)) +{ + if (!m_plane) + throw std::runtime_error("drmModeGetPlane failed: " + std::string{strerror(errno)}); + + if (!GetProperties(m_plane->plane_id, DRM_MODE_OBJECT_PLANE)) + throw std::runtime_error("failed to get properties for plane: " + + std::to_string(m_plane->plane_id)); +} + +bool CDRMPlane::SupportsFormat(uint32_t format) +{ + for (uint32_t i = 0; i < m_plane->count_formats; i++) + if (m_plane->formats[i] == format) + return true; + + return false; +} + +bool CDRMPlane::SupportsFormatAndModifier(uint32_t format, uint64_t modifier) +{ + /* + * Some broadcom modifiers have parameters encoded which need to be + * masked out before comparing with reported modifiers. + */ + if (modifier >> 56 == DRM_FORMAT_MOD_VENDOR_BROADCOM) + modifier = fourcc_mod_broadcom_mod(modifier); + + if (modifier == DRM_FORMAT_MOD_LINEAR) + { + if (!SupportsFormat(format)) + { + CLog::Log(LOGDEBUG, "CDRMPlane::{} - format not supported: {}", __FUNCTION__, + DRMHELPERS::FourCCToString(format)); + return false; + } + } + else + { + auto formatModifiers = &m_modifiers_map[format]; + if (formatModifiers->empty()) + { + CLog::Log(LOGDEBUG, "CDRMPlane::{} - format not supported: {}", __FUNCTION__, + DRMHELPERS::FourCCToString(format)); + return false; + } + + auto formatModifier = std::find(formatModifiers->begin(), formatModifiers->end(), modifier); + if (formatModifier == formatModifiers->end()) + { + CLog::Log(LOGDEBUG, "CDRMPlane::{} - modifier ({}) not supported for format ({})", + __FUNCTION__, DRMHELPERS::ModifierToString(modifier), + DRMHELPERS::FourCCToString(format)); + return false; + } + } + + CLog::Log(LOGDEBUG, "CDRMPlane::{} - found plane format ({}) and modifier ({})", __FUNCTION__, + DRMHELPERS::FourCCToString(format), DRMHELPERS::ModifierToString(modifier)); + + return true; +} + +void CDRMPlane::FindModifiers() +{ + auto property = std::find_if(m_propsInfo.begin(), m_propsInfo.end(), [](auto& prop) { + return StringUtils::EqualsNoCase(prop->name, "IN_FORMATS"); + }); + + uint64_t blob_id = 0; + if (property != m_propsInfo.end()) + blob_id = m_props->prop_values[std::distance(m_propsInfo.begin(), property)]; + + if (blob_id == 0) + return; + + drmModePropertyBlobPtr blob = drmModeGetPropertyBlob(m_fd, blob_id); + if (!blob) + return; + + drm_format_modifier_blob* header = static_cast<drm_format_modifier_blob*>(blob->data); + uint32_t* formats = + reinterpret_cast<uint32_t*>(reinterpret_cast<char*>(header) + header->formats_offset); + drm_format_modifier* mod = reinterpret_cast<drm_format_modifier*>( + reinterpret_cast<char*>(header) + header->modifiers_offset); + + for (uint32_t i = 0; i < header->count_formats; i++) + { + std::vector<uint64_t> modifiers; + for (uint32_t j = 0; j < header->count_modifiers; j++) + { + if (mod[j].formats & 1ULL << i) + modifiers.emplace_back(mod[j].modifier); + } + + m_modifiers_map.emplace(formats[i], modifiers); + } + + if (blob) + drmModeFreePropertyBlob(blob); +} diff --git a/xbmc/windowing/gbm/drm/DRMPlane.h b/xbmc/windowing/gbm/drm/DRMPlane.h new file mode 100644 index 0000000..e077fc3 --- /dev/null +++ b/xbmc/windowing/gbm/drm/DRMPlane.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "DRMObject.h" + +#include <map> + +#include <drm_fourcc.h> + +namespace KODI +{ +namespace WINDOWING +{ +namespace GBM +{ + +class CDRMPlane : public CDRMObject +{ +public: + explicit CDRMPlane(int fd, uint32_t plane); + CDRMPlane(const CDRMPlane&) = delete; + CDRMPlane& operator=(const CDRMPlane&) = delete; + ~CDRMPlane() = default; + + uint32_t GetPlaneId() const { return m_plane->plane_id; } + uint32_t GetPossibleCrtcs() const { return m_plane->possible_crtcs; } + + void FindModifiers(); + + void SetFormat(const uint32_t newFormat) { m_format = newFormat; } + uint32_t GetFormat() const { return m_format; } + std::vector<uint64_t>& GetModifiersForFormat(uint32_t format) { return m_modifiers_map[format]; } + + bool SupportsFormat(uint32_t format); + bool SupportsFormatAndModifier(uint32_t format, uint64_t modifier); + +private: + struct DrmModePlaneDeleter + { + void operator()(drmModePlane* p) { drmModeFreePlane(p); } + }; + + std::unique_ptr<drmModePlane, DrmModePlaneDeleter> m_plane; + + std::map<uint32_t, std::vector<uint64_t>> m_modifiers_map; + uint32_t m_format{DRM_FORMAT_XRGB8888}; +}; + +} // namespace GBM +} // namespace WINDOWING +} // namespace KODI diff --git a/xbmc/windowing/gbm/drm/DRMUtils.cpp b/xbmc/windowing/gbm/drm/DRMUtils.cpp new file mode 100644 index 0000000..6b61403 --- /dev/null +++ b/xbmc/windowing/gbm/drm/DRMUtils.cpp @@ -0,0 +1,740 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "DRMUtils.h" + +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include "utils/DRMHelpers.h" +#include "utils/StringUtils.h" +#include "utils/log.h" +#include "windowing/GraphicContext.h" + +#include "PlatformDefs.h" + +using namespace KODI::WINDOWING::GBM; + +namespace +{ +const std::string SETTING_VIDEOSCREEN_LIMITGUISIZE = "videoscreen.limitguisize"; + +void DrmFbDestroyCallback(gbm_bo* bo, void* data) +{ + drm_fb* fb = static_cast<drm_fb*>(data); + + if (fb->fb_id > 0) + { + CLog::Log(LOGDEBUG, "CDRMUtils::{} - removing framebuffer: {}", __FUNCTION__, fb->fb_id); + int drm_fd = gbm_device_get_fd(gbm_bo_get_device(bo)); + drmModeRmFB(drm_fd, fb->fb_id); + } + + delete fb; +} +} + +CDRMUtils::~CDRMUtils() +{ + DestroyDrm(); +} + +bool CDRMUtils::SetMode(const RESOLUTION_INFO& res) +{ + if (!m_connector->CheckConnector()) + return false; + + m_mode = m_connector->GetModeForIndex(std::atoi(res.strId.c_str())); + m_width = res.iWidth; + m_height = res.iHeight; + + CLog::Log(LOGDEBUG, "CDRMUtils::{} - found crtc mode: {}x{}{} @ {} Hz", __FUNCTION__, + m_mode->hdisplay, m_mode->vdisplay, m_mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "", + m_mode->vrefresh); + + return true; +} + +drm_fb * CDRMUtils::DrmFbGetFromBo(struct gbm_bo *bo) +{ + { + struct drm_fb *fb = static_cast<drm_fb *>(gbm_bo_get_user_data(bo)); + if(fb) + { + if (m_gui_plane->GetFormat() == fb->format) + return fb; + else + DrmFbDestroyCallback(bo, gbm_bo_get_user_data(bo)); + } + } + + struct drm_fb *fb = new drm_fb; + fb->bo = bo; + fb->format = m_gui_plane->GetFormat(); + + uint32_t width, + height, + handles[4] = {0}, + strides[4] = {0}, + offsets[4] = {0}; + + uint64_t modifiers[4] = {0}; + + width = gbm_bo_get_width(bo); + height = gbm_bo_get_height(bo); + +#if defined(HAS_GBM_MODIFIERS) + for (int i = 0; i < gbm_bo_get_plane_count(bo); i++) + { + handles[i] = gbm_bo_get_handle_for_plane(bo, i).u32; + strides[i] = gbm_bo_get_stride_for_plane(bo, i); + offsets[i] = gbm_bo_get_offset(bo, i); + modifiers[i] = gbm_bo_get_modifier(bo); + } +#else + handles[0] = gbm_bo_get_handle(bo).u32; + strides[0] = gbm_bo_get_stride(bo); + memset(offsets, 0, 16); +#endif + + uint32_t flags = 0; + + if (modifiers[0] && modifiers[0] != DRM_FORMAT_MOD_INVALID) + { + flags |= DRM_MODE_FB_MODIFIERS; + CLog::Log(LOGDEBUG, "CDRMUtils::{} - using modifier: {}", __FUNCTION__, + DRMHELPERS::ModifierToString(modifiers[0])); + } + + int ret = drmModeAddFB2WithModifiers(m_fd, + width, + height, + fb->format, + handles, + strides, + offsets, + modifiers, + &fb->fb_id, + flags); + + if(ret < 0) + { + ret = drmModeAddFB2(m_fd, + width, + height, + fb->format, + handles, + strides, + offsets, + &fb->fb_id, + flags); + + if (ret < 0) + { + delete (fb); + CLog::Log(LOGDEBUG, "CDRMUtils::{} - failed to add framebuffer: {} ({})", __FUNCTION__, strerror(errno), errno); + return nullptr; + } + } + + gbm_bo_set_user_data(bo, fb, DrmFbDestroyCallback); + + return fb; +} + +bool CDRMUtils::FindPreferredMode() +{ + if (m_mode) + return true; + + for (int i = 0, area = 0; i < m_connector->GetModesCount(); i++) + { + drmModeModeInfo* current_mode = m_connector->GetModeForIndex(i); + + if(current_mode->type & DRM_MODE_TYPE_PREFERRED) + { + m_mode = current_mode; + CLog::Log(LOGDEBUG, "CDRMUtils::{} - found preferred mode: {}x{}{} @ {} Hz", __FUNCTION__, + m_mode->hdisplay, m_mode->vdisplay, + m_mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "", m_mode->vrefresh); + break; + } + + auto current_area = current_mode->hdisplay * current_mode->vdisplay; + if (current_area > area) + { + m_mode = current_mode; + area = current_area; + } + } + + if(!m_mode) + { + CLog::Log(LOGDEBUG, "CDRMUtils::{} - failed to find preferred mode", __FUNCTION__); + return false; + } + + return true; +} + +bool CDRMUtils::FindPlanes() +{ + for (size_t i = 0; i < m_crtcs.size(); i++) + { + if (!(m_encoder->GetPossibleCrtcs() & (1 << i))) + continue; + + auto videoPlane = std::find_if(m_planes.begin(), m_planes.end(), [&i](auto& plane) { + if (plane->GetPossibleCrtcs() & (1 << i)) + { + return plane->SupportsFormat(DRM_FORMAT_NV12); + } + return false; + }); + + uint32_t videoPlaneId{0}; + + if (videoPlane != m_planes.end()) + videoPlaneId = videoPlane->get()->GetPlaneId(); + + auto guiPlane = + std::find_if(m_planes.begin(), m_planes.end(), [&i, &videoPlaneId](auto& plane) { + if (plane->GetPossibleCrtcs() & (1 << i)) + { + return (plane->GetPlaneId() != videoPlaneId && + (videoPlaneId == 0 || plane->SupportsFormat(DRM_FORMAT_ARGB8888)) && + (plane->SupportsFormat(DRM_FORMAT_XRGB2101010) || + plane->SupportsFormat(DRM_FORMAT_XRGB8888))); + } + return false; + }); + + if (videoPlane != m_planes.end() && guiPlane != m_planes.end()) + { + m_crtc = m_crtcs[i].get(); + m_video_plane = videoPlane->get(); + m_gui_plane = guiPlane->get(); + break; + } + + if (guiPlane != m_planes.end()) + { + if (!m_crtc && m_encoder->GetCrtcId() == m_crtcs[i]->GetCrtcId()) + { + m_crtc = m_crtcs[i].get(); + m_gui_plane = guiPlane->get(); + m_video_plane = nullptr; + } + } + } + + CLog::Log(LOGINFO, "CDRMUtils::{} - using crtc: {}", __FUNCTION__, m_crtc->GetCrtcId()); + + // video plane may not be available + if (m_video_plane) + CLog::Log(LOGDEBUG, "CDRMUtils::{} - using video plane {}", __FUNCTION__, + m_video_plane->GetPlaneId()); + + if (m_gui_plane->SupportsFormat(DRM_FORMAT_XRGB2101010)) + { + m_gui_plane->SetFormat(DRM_FORMAT_XRGB2101010); + CLog::Log(LOGDEBUG, "CDRMUtils::{} - using 10bit gui plane {}", __FUNCTION__, + m_gui_plane->GetPlaneId()); + } + else + { + m_gui_plane->SetFormat(DRM_FORMAT_XRGB8888); + CLog::Log(LOGDEBUG, "CDRMUtils::{} - using gui plane {}", __FUNCTION__, + m_gui_plane->GetPlaneId()); + } + + return true; +} + +void CDRMUtils::PrintDrmDeviceInfo(drmDevicePtr device) +{ + std::string message; + + // clang-format off + message.append(fmt::format("CDRMUtils::{} - DRM Device Info:", __FUNCTION__)); + message.append(fmt::format("\n available_nodes: {:#04x}", device->available_nodes)); + message.append("\n nodes:"); + + for (int i = 0; i < DRM_NODE_MAX; i++) + { + if (device->available_nodes & 1 << i) + message.append(fmt::format("\n nodes[{}]: {}", i, device->nodes[i])); + } + + message.append(fmt::format("\n bustype: {:#04x}", device->bustype)); + + if (device->bustype == DRM_BUS_PCI) + { + message.append("\n pci:"); + message.append(fmt::format("\n domain: {:#04x}", device->businfo.pci->domain)); + message.append(fmt::format("\n bus: {:#02x}", device->businfo.pci->bus)); + message.append(fmt::format("\n dev: {:#02x}", device->businfo.pci->dev)); + message.append(fmt::format("\n func: {:#1}", device->businfo.pci->func)); + + message.append("\n deviceinfo:"); + message.append("\n pci:"); + message.append(fmt::format("\n vendor_id: {:#04x}", device->deviceinfo.pci->vendor_id)); + message.append(fmt::format("\n device_id: {:#04x}", device->deviceinfo.pci->device_id)); + message.append(fmt::format("\n subvendor_id: {:#04x}", device->deviceinfo.pci->subvendor_id)); + message.append(fmt::format("\n subdevice_id: {:#04x}", device->deviceinfo.pci->subdevice_id)); + } + else if (device->bustype == DRM_BUS_USB) + { + message.append("\n usb:"); + message.append(fmt::format("\n bus: {:#03x}", device->businfo.usb->bus)); + message.append(fmt::format("\n dev: {:#03x}", device->businfo.usb->dev)); + + message.append("\n deviceinfo:"); + message.append("\n usb:"); + message.append(fmt::format("\n vendor: {:#04x}", device->deviceinfo.usb->vendor)); + message.append(fmt::format("\n product: {:#04x}", device->deviceinfo.usb->product)); + } + else if (device->bustype == DRM_BUS_PLATFORM) + { + message.append("\n platform:"); + message.append(fmt::format("\n fullname: {}", device->businfo.platform->fullname)); + } + else if (device->bustype == DRM_BUS_HOST1X) + { + message.append("\n host1x:"); + message.append(fmt::format("\n fullname: {}", device->businfo.host1x->fullname)); + } + else + message.append("\n unhandled bus type"); + // clang-format on + + CLog::Log(LOGDEBUG, "{}", message); +} + +bool CDRMUtils::OpenDrm(bool needConnector) +{ + int numDevices = drmGetDevices2(0, nullptr, 0); + if (numDevices <= 0) + { + CLog::Log(LOGERROR, "CDRMUtils::{} - no drm devices found: ({})", __FUNCTION__, + strerror(errno)); + return false; + } + + CLog::Log(LOGDEBUG, "CDRMUtils::{} - drm devices found: {}", __FUNCTION__, numDevices); + + std::vector<drmDevicePtr> devices(numDevices); + + int ret = drmGetDevices2(0, devices.data(), devices.size()); + if (ret < 0) + { + CLog::Log(LOGERROR, "CDRMUtils::{} - drmGetDevices2 return an error: ({})", __FUNCTION__, + strerror(errno)); + return false; + } + + for (const auto device : devices) + { + if (!(device->available_nodes & 1 << DRM_NODE_PRIMARY)) + continue; + + close(m_fd); + m_fd = open(device->nodes[DRM_NODE_PRIMARY], O_RDWR | O_CLOEXEC); + if (m_fd < 0) + continue; + + if (needConnector) + { + auto resources = drmModeGetResources(m_fd); + if (!resources) + continue; + + m_connectors.clear(); + for (int i = 0; i < resources->count_connectors; i++) + m_connectors.emplace_back(std::make_unique<CDRMConnector>(m_fd, resources->connectors[i])); + + drmModeFreeResources(resources); + + if (!FindConnector()) + continue; + } + + CLog::Log(LOGDEBUG, "CDRMUtils::{} - opened device: {}", __FUNCTION__, + device->nodes[DRM_NODE_PRIMARY]); + + PrintDrmDeviceInfo(device); + + const char* renderPath = drmGetRenderDeviceNameFromFd(m_fd); + + if (!renderPath) + renderPath = drmGetDeviceNameFromFd2(m_fd); + + if (!renderPath) + renderPath = drmGetDeviceNameFromFd(m_fd); + + if (renderPath) + { + m_renderDevicePath = renderPath; + m_renderFd = open(renderPath, O_RDWR | O_CLOEXEC); + if (m_renderFd != 0) + CLog::Log(LOGDEBUG, "CDRMUtils::{} - opened render node: {}", __FUNCTION__, renderPath); + } + + drmFreeDevices(devices.data(), devices.size()); + return true; + } + + drmFreeDevices(devices.data(), devices.size()); + return false; +} + +bool CDRMUtils::InitDrm() +{ + if (m_fd < 0) + return false; + + /* caps need to be set before allocating connectors, encoders, crtcs, and planes */ + int ret = drmSetClientCap(m_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); + if (ret) + { + CLog::Log(LOGERROR, "CDRMUtils::{} - failed to set universal planes capability: {}", + __FUNCTION__, strerror(errno)); + return false; + } + + ret = drmSetClientCap(m_fd, DRM_CLIENT_CAP_STEREO_3D, 1); + if (ret) + { + CLog::Log(LOGERROR, "CDRMUtils::{} - failed to set stereo 3d capability: {}", __FUNCTION__, + strerror(errno)); + return false; + } + +#if defined(DRM_CLIENT_CAP_ASPECT_RATIO) + ret = drmSetClientCap(m_fd, DRM_CLIENT_CAP_ASPECT_RATIO, 0); + if (ret != 0) + CLog::Log(LOGERROR, "CDRMUtils::{} - aspect ratio capability is not supported: {}", + __FUNCTION__, strerror(errno)); +#endif + + auto resources = drmModeGetResources(m_fd); + if (!resources) + { + CLog::Log(LOGERROR, "CDRMUtils::{} - failed to get drm resources: {}", __FUNCTION__, strerror(errno)); + return false; + } + + m_connectors.clear(); + for (int i = 0; i < resources->count_connectors; i++) + m_connectors.emplace_back(std::make_unique<CDRMConnector>(m_fd, resources->connectors[i])); + + m_encoders.clear(); + for (int i = 0; i < resources->count_encoders; i++) + m_encoders.emplace_back(std::make_unique<CDRMEncoder>(m_fd, resources->encoders[i])); + + m_crtcs.clear(); + for (int i = 0; i < resources->count_crtcs; i++) + m_crtcs.emplace_back(std::make_unique<CDRMCrtc>(m_fd, resources->crtcs[i])); + + drmModeFreeResources(resources); + + auto planeResources = drmModeGetPlaneResources(m_fd); + if (!planeResources) + { + CLog::Log(LOGERROR, "CDRMUtils::{} - failed to get drm plane resources: {}", __FUNCTION__, strerror(errno)); + return false; + } + + m_planes.clear(); + for (uint32_t i = 0; i < planeResources->count_planes; i++) + { + m_planes.emplace_back(std::make_unique<CDRMPlane>(m_fd, planeResources->planes[i])); + m_planes[i]->FindModifiers(); + } + + drmModeFreePlaneResources(planeResources); + + if (!FindConnector()) + return false; + + if (!FindEncoder()) + return false; + + if (!FindCrtc()) + return false; + + if (!FindPlanes()) + return false; + + if (!FindPreferredMode()) + return false; + + ret = drmSetMaster(m_fd); + if (ret < 0) + { + CLog::Log(LOGDEBUG, + "CDRMUtils::{} - failed to set drm master, will try to authorize instead: {}", + __FUNCTION__, strerror(errno)); + + drm_magic_t magic; + + ret = drmGetMagic(m_fd, &magic); + if (ret < 0) + { + CLog::Log(LOGERROR, "CDRMUtils::{} - failed to get drm magic: {}", __FUNCTION__, + strerror(errno)); + return false; + } + + ret = drmAuthMagic(m_fd, magic); + if (ret < 0) + { + CLog::Log(LOGERROR, "CDRMUtils::{} - failed to authorize drm magic: {}", __FUNCTION__, + strerror(errno)); + return false; + } + + CLog::Log(LOGINFO, "CDRMUtils::{} - successfully authorized drm magic", __FUNCTION__); + } + + return true; +} + +bool CDRMUtils::FindConnector() +{ + auto settingsComponent = CServiceBroker::GetSettingsComponent(); + if (!settingsComponent) + return false; + + auto settings = settingsComponent->GetSettings(); + if (!settings) + return false; + + std::vector<std::unique_ptr<CDRMConnector>>::iterator connector; + + std::string connectorName = settings->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR); + if (connectorName != "Default") + { + connector = std::find_if(m_connectors.begin(), m_connectors.end(), + [&connectorName](auto& connector) + { + return connector->GetEncoderId() > 0 && connector->IsConnected() && + connector->GetName() == connectorName; + }); + } + + if (connector == m_connectors.end()) + { + CLog::Log(LOGDEBUG, "CDRMUtils::{} - failed to find specified connector: {}, trying default", + __FUNCTION__, connectorName); + connectorName = "Default"; + } + + if (connectorName == "Default") + { + connector = std::find_if(m_connectors.begin(), m_connectors.end(), + [](auto& connector) + { return connector->GetEncoderId() > 0 && connector->IsConnected(); }); + } + + if (connector == m_connectors.end()) + { + CLog::Log(LOGDEBUG, "CDRMUtils::{} - failed to find connected connector", __FUNCTION__); + return false; + } + + CLog::Log(LOGINFO, "CDRMUtils::{} - using connector: {}", __FUNCTION__, + connector->get()->GetName()); + + m_connector = connector->get(); + return true; +} + +bool CDRMUtils::FindEncoder() +{ + auto encoder = std::find_if(m_encoders.begin(), m_encoders.end(), [this](auto& encoder) { + return encoder->GetEncoderId() == m_connector->GetEncoderId(); + }); + + if (encoder == m_encoders.end()) + { + CLog::Log(LOGDEBUG, "CDRMUtils::{} - failed to find encoder for connector id: {}", __FUNCTION__, + *m_connector->GetConnectorId()); + return false; + } + + CLog::Log(LOGINFO, "CDRMUtils::{} - using encoder: {}", __FUNCTION__, + encoder->get()->GetEncoderId()); + + m_encoder = encoder->get(); + return true; +} + +bool CDRMUtils::FindCrtc() +{ + for (size_t i = 0; i < m_crtcs.size(); i++) + { + if (m_encoder->GetPossibleCrtcs() & (1 << i)) + { + if (m_crtcs[i]->GetCrtcId() == m_encoder->GetCrtcId()) + { + m_orig_crtc = m_crtcs[i].get(); + if (m_orig_crtc->GetModeValid()) + { + m_mode = m_orig_crtc->GetMode(); + CLog::Log(LOGDEBUG, "CDRMUtils::{} - original crtc mode: {}x{}{} @ {} Hz", __FUNCTION__, + m_mode->hdisplay, m_mode->vdisplay, + m_mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "", m_mode->vrefresh); + } + return true; + } + } + } + + return false; +} + +bool CDRMUtils::RestoreOriginalMode() +{ + if(!m_orig_crtc) + { + return false; + } + + auto ret = drmModeSetCrtc(m_fd, m_orig_crtc->GetCrtcId(), m_orig_crtc->GetBufferId(), + m_orig_crtc->GetX(), m_orig_crtc->GetY(), m_connector->GetConnectorId(), + 1, m_orig_crtc->GetMode()); + + if(ret) + { + CLog::Log(LOGERROR, "CDRMUtils::{} - failed to set original crtc mode", __FUNCTION__); + return false; + } + + CLog::Log(LOGDEBUG, "CDRMUtils::{} - set original crtc mode", __FUNCTION__); + + return true; +} + +void CDRMUtils::DestroyDrm() +{ + RestoreOriginalMode(); + + if (drmAuthMagic(m_fd, 0) == EINVAL) + drmDropMaster(m_fd); + + close(m_renderFd); + close(m_fd); + + m_connector = nullptr; + m_encoder = nullptr; + m_crtc = nullptr; + m_orig_crtc = nullptr; + m_video_plane = nullptr; + m_gui_plane = nullptr; +} + +RESOLUTION_INFO CDRMUtils::GetResolutionInfo(drmModeModeInfoPtr mode) +{ + RESOLUTION_INFO res; + res.iScreenWidth = mode->hdisplay; + res.iScreenHeight = mode->vdisplay; + res.iWidth = res.iScreenWidth; + res.iHeight = res.iScreenHeight; + + int limit = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt( + SETTING_VIDEOSCREEN_LIMITGUISIZE); + if (limit > 0 && res.iScreenWidth > 1920 && res.iScreenHeight > 1080) + { + switch (limit) + { + case 1: // 720p + res.iWidth = 1280; + res.iHeight = 720; + break; + case 2: // 1080p / 720p (>30hz) + res.iWidth = mode->vrefresh > 30 ? 1280 : 1920; + res.iHeight = mode->vrefresh > 30 ? 720 : 1080; + break; + case 3: // 1080p + res.iWidth = 1920; + res.iHeight = 1080; + break; + case 4: // Unlimited / 1080p (>30hz) + res.iWidth = mode->vrefresh > 30 ? 1920 : res.iScreenWidth; + res.iHeight = mode->vrefresh > 30 ? 1080 : res.iScreenHeight; + break; + } + } + + if (mode->clock % 5 != 0) + res.fRefreshRate = static_cast<float>(mode->vrefresh) * (1000.0f/1001.0f); + else + res.fRefreshRate = mode->vrefresh; + res.iSubtitles = res.iHeight; + res.fPixelRatio = 1.0f; + res.bFullScreen = true; + + if (mode->flags & DRM_MODE_FLAG_3D_MASK) + { + if (mode->flags & DRM_MODE_FLAG_3D_TOP_AND_BOTTOM) + res.dwFlags = D3DPRESENTFLAG_MODE3DTB; + else if (mode->flags & DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF) + res.dwFlags = D3DPRESENTFLAG_MODE3DSBS; + } + else if (mode->flags & DRM_MODE_FLAG_INTERLACE) + res.dwFlags = D3DPRESENTFLAG_INTERLACED; + else + res.dwFlags = D3DPRESENTFLAG_PROGRESSIVE; + + res.strMode = + StringUtils::Format("{}x{}{} @ {:.6f} Hz", res.iScreenWidth, res.iScreenHeight, + res.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "", res.fRefreshRate); + return res; +} + +RESOLUTION_INFO CDRMUtils::GetCurrentMode() +{ + return GetResolutionInfo(m_mode); +} + +std::vector<RESOLUTION_INFO> CDRMUtils::GetModes() +{ + std::vector<RESOLUTION_INFO> resolutions; + resolutions.reserve(m_connector->GetModesCount()); + + for (auto i = 0; i < m_connector->GetModesCount(); i++) + { + RESOLUTION_INFO res = GetResolutionInfo(m_connector->GetModeForIndex(i)); + res.strId = std::to_string(i); + resolutions.push_back(res); + } + + return resolutions; +} + +std::vector<std::string> CDRMUtils::GetConnectedConnectorNames() +{ + std::vector<std::string> connectorNames; + for (const auto& connector : m_connectors) + { + if (connector->IsConnected()) + connectorNames.emplace_back(connector->GetName()); + } + + return connectorNames; +} + +uint32_t CDRMUtils::FourCCWithAlpha(uint32_t fourcc) +{ + return (fourcc & 0xFFFFFF00) | static_cast<uint32_t>('A'); +} + +uint32_t CDRMUtils::FourCCWithoutAlpha(uint32_t fourcc) +{ + return (fourcc & 0xFFFFFF00) | static_cast<uint32_t>('X'); +} diff --git a/xbmc/windowing/gbm/drm/DRMUtils.h b/xbmc/windowing/gbm/drm/DRMUtils.h new file mode 100644 index 0000000..5327e35 --- /dev/null +++ b/xbmc/windowing/gbm/drm/DRMUtils.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "DRMConnector.h" +#include "DRMCrtc.h" +#include "DRMEncoder.h" +#include "DRMPlane.h" +#include "windowing/Resolution.h" +#include "windowing/gbm/GBMUtils.h" + +#include <vector> + +#include <gbm.h> +#include <xf86drm.h> + +namespace KODI +{ +namespace WINDOWING +{ +namespace GBM +{ + +struct drm_fb +{ + struct gbm_bo *bo = nullptr; + uint32_t fb_id; + uint32_t format; +}; + +class CDRMUtils +{ +public: + CDRMUtils() = default; + virtual ~CDRMUtils(); + virtual void FlipPage(struct gbm_bo* bo, bool rendered, bool videoLayer) {} + virtual bool SetVideoMode(const RESOLUTION_INFO& res, struct gbm_bo* bo) { return false; } + virtual bool SetActive(bool active) { return false; } + virtual bool InitDrm(); + virtual void DestroyDrm(); + + int GetFileDescriptor() const { return m_fd; } + int GetRenderNodeFileDescriptor() const { return m_renderFd; } + const char* GetRenderDevicePath() const { return m_renderDevicePath; } + CDRMPlane* GetVideoPlane() const { return m_video_plane; } + CDRMPlane* GetGuiPlane() const { return m_gui_plane; } + CDRMCrtc* GetCrtc() const { return m_crtc; } + CDRMConnector* GetConnector() const { return m_connector; } + + std::vector<std::string> GetConnectedConnectorNames(); + + virtual RESOLUTION_INFO GetCurrentMode(); + virtual std::vector<RESOLUTION_INFO> GetModes(); + virtual bool SetMode(const RESOLUTION_INFO& res); + + static uint32_t FourCCWithAlpha(uint32_t fourcc); + static uint32_t FourCCWithoutAlpha(uint32_t fourcc); + +protected: + bool OpenDrm(bool needConnector); + drm_fb* DrmFbGetFromBo(struct gbm_bo *bo); + + int m_fd; + CDRMConnector* m_connector{nullptr}; + CDRMEncoder* m_encoder{nullptr}; + CDRMCrtc* m_crtc{nullptr}; + CDRMCrtc* m_orig_crtc{nullptr}; + CDRMPlane* m_video_plane{nullptr}; + CDRMPlane* m_gui_plane{nullptr}; + drmModeModeInfo *m_mode = nullptr; + + int m_width = 0; + int m_height = 0; + + std::vector<std::unique_ptr<CDRMPlane>> m_planes; + +private: + bool FindConnector(); + bool FindEncoder(); + bool FindCrtc(); + bool FindPlanes(); + bool FindPreferredMode(); + bool RestoreOriginalMode(); + RESOLUTION_INFO GetResolutionInfo(drmModeModeInfoPtr mode); + void PrintDrmDeviceInfo(drmDevicePtr device); + + int m_renderFd; + const char* m_renderDevicePath{nullptr}; + + std::vector<std::unique_ptr<CDRMConnector>> m_connectors; + std::vector<std::unique_ptr<CDRMEncoder>> m_encoders; + std::vector<std::unique_ptr<CDRMCrtc>> m_crtcs; +}; + +} +} +} diff --git a/xbmc/windowing/gbm/drm/OffScreenModeSetting.cpp b/xbmc/windowing/gbm/drm/OffScreenModeSetting.cpp new file mode 100644 index 0000000..dc69fdc --- /dev/null +++ b/xbmc/windowing/gbm/drm/OffScreenModeSetting.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "OffScreenModeSetting.h" + +#include "utils/log.h" + +using namespace KODI::WINDOWING::GBM; + +namespace +{ +constexpr int DISPLAY_WIDTH = 1280; +constexpr int DISPLAY_HEIGHT = 720; +constexpr float DISPLAY_REFRESH = 50.0f; +} // namespace + +bool COffScreenModeSetting::InitDrm() +{ + if (!CDRMUtils::OpenDrm(false)) + return false; + + CLog::Log(LOGDEBUG, "COffScreenModeSetting::{} - initialized offscreen DRM", __FUNCTION__); + return true; +} + +std::vector<RESOLUTION_INFO> COffScreenModeSetting::GetModes() +{ + std::vector<RESOLUTION_INFO> resolutions; + resolutions.push_back(GetCurrentMode()); + return resolutions; +} + +RESOLUTION_INFO COffScreenModeSetting::GetCurrentMode() +{ + RESOLUTION_INFO res; + res.iScreenWidth = DISPLAY_WIDTH; + res.iWidth = DISPLAY_WIDTH; + res.iScreenHeight = DISPLAY_HEIGHT; + res.iHeight = DISPLAY_HEIGHT; + res.fRefreshRate = DISPLAY_REFRESH; + res.iSubtitles = res.iHeight; + res.fPixelRatio = 1.0f; + res.bFullScreen = true; + res.strId = "0"; + + return res; +} diff --git a/xbmc/windowing/gbm/drm/OffScreenModeSetting.h b/xbmc/windowing/gbm/drm/OffScreenModeSetting.h new file mode 100644 index 0000000..4270d4e --- /dev/null +++ b/xbmc/windowing/gbm/drm/OffScreenModeSetting.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "DRMUtils.h" + +namespace KODI +{ +namespace WINDOWING +{ +namespace GBM +{ + +class COffScreenModeSetting : public CDRMUtils +{ +public: + COffScreenModeSetting() = default; + ~COffScreenModeSetting() override = default; + void FlipPage(struct gbm_bo *bo, bool rendered, bool videoLayer) override {} + bool SetVideoMode(const RESOLUTION_INFO& res, struct gbm_bo *bo) override { return false; } + bool SetActive(bool active) override { return false; } + bool InitDrm() override; + void DestroyDrm() override {} + + RESOLUTION_INFO GetCurrentMode() override; + std::vector<RESOLUTION_INFO> GetModes() override; + bool SetMode(const RESOLUTION_INFO& res) override { return true; } +}; + +} +} +} |