summaryrefslogtreecommitdiffstats
path: root/xbmc/windowing/gbm/drm
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/windowing/gbm/drm')
-rw-r--r--xbmc/windowing/gbm/drm/CMakeLists.txt21
-rw-r--r--xbmc/windowing/gbm/drm/DRMAtomic.cpp344
-rw-r--r--xbmc/windowing/gbm/drm/DRMAtomic.h81
-rw-r--r--xbmc/windowing/gbm/drm/DRMConnector.cpp99
-rw-r--r--xbmc/windowing/gbm/drm/DRMConnector.h53
-rw-r--r--xbmc/windowing/gbm/drm/DRMCrtc.cpp26
-rw-r--r--xbmc/windowing/gbm/drm/DRMCrtc.h46
-rw-r--r--xbmc/windowing/gbm/drm/DRMEncoder.cpp23
-rw-r--r--xbmc/windowing/gbm/drm/DRMEncoder.h43
-rw-r--r--xbmc/windowing/gbm/drm/DRMLegacy.cpp141
-rw-r--r--xbmc/windowing/gbm/drm/DRMLegacy.h39
-rw-r--r--xbmc/windowing/gbm/drm/DRMObject.cpp133
-rw-r--r--xbmc/windowing/gbm/drm/DRMObject.h71
-rw-r--r--xbmc/windowing/gbm/drm/DRMPlane.cpp118
-rw-r--r--xbmc/windowing/gbm/drm/DRMPlane.h58
-rw-r--r--xbmc/windowing/gbm/drm/DRMUtils.cpp740
-rw-r--r--xbmc/windowing/gbm/drm/DRMUtils.h103
-rw-r--r--xbmc/windowing/gbm/drm/OffScreenModeSetting.cpp52
-rw-r--r--xbmc/windowing/gbm/drm/OffScreenModeSetting.h38
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; }
+};
+
+}
+}
+}