Skip to content

Commit

Permalink
Device integration for DRM/KMS
Browse files Browse the repository at this point in the history
Closes: #43
  • Loading branch information
plfiorini committed Jan 21, 2024
1 parent 1a2ed7c commit b9de2c5
Show file tree
Hide file tree
Showing 29 changed files with 2,713 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ if(FEATURE_aurora_qpa)
add_subdirectory(src/plugins/platforms/eglfs)
# add_subdirectory(src/platformsupport/libinput)
endif()
if(FEATURE_aurora_deviceintegration_drm)
add_subdirectory(src/plugins/deviceintegration/drm)
endif()
if(FEATURE_aurora_deviceintegration_wayland)
add_subdirectory(src/plugins/deviceintegration/wayland)
endif()
Expand Down
29 changes: 29 additions & 0 deletions features.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,35 @@ endif()
add_feature_info("Aurora::VulkanServerBuffer" FEATURE_aurora_vulkan_server_buffer "Build Wayland compositor with Vulkan-based server buffer integration")
set(LIRI_FEATURE_aurora_vulkan_server_buffer "$<IF:${FEATURE_aurora_vulkan_server_buffer},1,0>")

# Device Integration: drm
option(FEATURE_aurora_deviceintegration_drm "Device Integration: drm" ON)
if(FEATURE_aurora_deviceintegration_drm)
find_package(EGL QUIET)

if(NOT TARGET EGL::EGL)
message(WARNING "You need EGL for Aurora::DeviceIntegration::DRM")
set(FEATURE_aurora_deviceintegration_drm OFF)
endif()
if(NOT TARGET PkgConfig::Libudev)
message(WARNING "You need udev for Aurora::DeviceIntegration::DRM")
set(FEATURE_aurora_deviceintegration_drm OFF)
endif()
if(NOT TARGET PkgConfig::Libinput)
message(WARNING "You need libinput for Aurora::DeviceIntegration::DRM")
set(FEATURE_aurora_deviceintegration_drm OFF)
endif()
if(NOT TARGET PkgConfig::Libdrm)
message(WARNING "You need libdrm for Aurora::DeviceIntegration::DRM")
set(FEATURE_aurora_deviceintegration_drm OFF)
endif()
if(NOT TARGET PkgConfig::Gbm)
message(WARNING "You need gbm for Aurora::DeviceIntegration::DRM")
set(FEATURE_aurora_deviceintegration_drm OFF)
endif()
endif()
add_feature_info("Aurora::DeviceIntegration::DRM" FEATURE_aurora_deviceintegration_drm "Build DRM/KMS device integration")
set(LIRI_FEATURE_aurora_deviceintegration_drm "$<IF:${FEATURE_aurora_deviceintegration_drm},1,0>")

# Device Integration: wayland
option(FEATURE_aurora_deviceintegration_wayland "Device Integration: wayland" ON)
if(FEATURE_aurora_deviceintegration_wayland)
Expand Down
54 changes: 54 additions & 0 deletions src/plugins/deviceintegration/drm/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
include(ECMQtDeclareLoggingCategory)
ecm_qt_declare_logging_category(
AuroraDeviceIntegrationDrm_SOURCES
HEADER "drmloggingcategories.h"
IDENTIFIER "Aurora::Platform::gLcDrm"
CATEGORY_NAME "aurora.platform.drm"
DEFAULT_SEVERITY "Info"
DESCRIPTION "Aurora device integration for DRM/KMS"
)

qt6_add_plugin(AuroraDeviceIntegrationDrm
SHARED
CLASS_NAME DrmIntegrationPlugin
MANUAL_FINALIZATION
drm.json
drmbackend.cpp drmbackend.h
drmblob.cpp drmblob.h
drmcrtc.cpp drmcrtc.h
drmcursor.cpp drmcursor.h
drmgpu.cpp drmgpu.h
drmintegration.cpp drmintegration.h
drmintegrationplugin.cpp drmintegrationplugin.h
drmobject.cpp drmobject.h
drmoutput.cpp drmoutput.h
drmplane.cpp drmplane.h
drmpointer.h
drmproperty.cpp drmproperty.h
drmwindow.cpp drmwindow.h
${AuroraDeviceIntegrationDrm_SOURCES}
)

set_target_properties(AuroraDeviceIntegrationDrm
PROPERTIES OUTPUT_NAME drm
)

target_link_libraries(AuroraDeviceIntegrationDrm
PUBLIC
Qt6::Core
Qt6::Gui
Liri::AuroraCore
Liri::AuroraPlatform
EGL::EGL
PkgConfig::Libdrm
PkgConfig::Gbm
PRIVATE
Liri::AuroraPlatformPrivate
)

qt6_finalize_target(AuroraDeviceIntegrationDrm)

install(
TARGETS AuroraDeviceIntegrationDrm
DESTINATION ${KDE_INSTALL_PLUGINDIR}/aurora/deviceintegration
)
3 changes: 3 additions & 0 deletions src/plugins/deviceintegration/drm/drm.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"Keys": [ "drm" ]
}
227 changes: 227 additions & 0 deletions src/plugins/deviceintegration/drm/drmbackend.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini <[email protected]>
// SPDX-License-Identifier: GPL-3.0-or-later

#include <LiriAuroraUdev/UdevEnumerate>

#include "drmbackend.h"
#include "drmgpu.h"
#include "drmloggingcategories.h"
#include "drmoutput.h"

#include <sys/stat.h>

#include <gbm.h>
#include <libdrm/drm_mode.h>
#include <xf86drm.h>
#include <xf86drmMode.h>

#ifndef EGL_EXT_platform_base
typedef EGLDisplay(EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC)(EGLenum platform,
void *native_display,
const EGLint *attrib_list);
#endif

#ifndef EGL_PLATFORM_GBM_KHR
# define EGL_PLATFORM_GBM_KHR 0x31D7
#endif

namespace Aurora {

namespace Platform {

Q_GLOBAL_STATIC(DrmBackend, gDrmBackend)

DrmBackend::DrmBackend(QObject *parent)
: QObject(parent)
, m_session(Session::create(Session::Type::Logind, this))
, m_udev(new Aurora::PlatformSupport::Udev())
{
}

DrmBackend::~DrmBackend()
{
if (m_udev) {
delete m_udev;
m_udev = nullptr;
}
}

Session *DrmBackend::session() const
{
return m_session;
}

EGLDisplay DrmBackend::eglDisplay() const
{
return m_eglDisplay;
}

EGLNativeDisplayType DrmBackend::platformDisplay() const
{
if (m_gpu)
return reinterpret_cast<EGLNativeDisplayType>(m_gpu->gbmDevice());
return EGL_CAST(EGLNativeDisplayType, 0);
}

bool DrmBackend::isAtomicEnabled() const
{
return m_enableAtomic;
}

DrmGpu *DrmBackend::primaryGpu() const
{
return m_gpu;
}

void DrmBackend::initialize()
{
if (m_initialized)
return;

m_initialized = true;

// Session
connect(m_session, &Session::devicePaused, this, [this](dev_t deviceId) {
if (auto *gpu = findGpu(deviceId))
gpu->setActive(false);
});
connect(m_session, &Session::deviceResumed, this, [this](dev_t deviceId) {
if (auto *gpu = findGpu(deviceId))
gpu->setActive(true);
});

// Find all GPUs
Aurora::PlatformSupport::UdevEnumerate enumerate(
Aurora::PlatformSupport::UdevDevice::PrimaryVideoDevice
| Aurora::PlatformSupport::UdevDevice::GenericVideoDevice,
m_udev);
auto udevDevices = enumerate.scan();
if (Q_UNLIKELY(udevDevices.isEmpty()))
qFatal("Could not find DRM device!");

// Create GPUs
if (!udevDevices.isEmpty()) {
qCDebug(gLcDrm, "Found the following video devices for the \"%s\" seat:",
qPrintable(m_session->seat()));
for (auto *udevDevice : qAsConst(udevDevices)) {
if (udevDevice->seat() == m_session->seat()) {
const auto path = udevDevice->deviceNode();
qCDebug(gLcDrm) << '\t' << path.toLocal8Bit().constData();
addGpu(path);
}
}
}
if (Q_UNLIKELY(m_gpus.isEmpty()))
qFatal("No suitable DRM device have been found");

// Select the first one
m_gpu = m_gpus.first();

// Create EGL display
createDisplay();
}

void DrmBackend::destroy()
{
}

DrmBackend *DrmBackend::instance()
{
return gDrmBackend();
}

DrmGpu *DrmBackend::addGpu(const QString &path)
{
// Open the DRM device
int fd = m_session->openRestricted(path);
if (fd < 0) {
qCWarning(gLcDrm) << "Failed to open DRM device" << path;
return nullptr;
}

// Make a simpel DRM call to check if the device is usable
drmModeResPtr resources = drmModeGetResources(fd);
if (!resources) {
qCDebug(gLcDrm) << "Skipping KMS incapable DRM device" << path;
m_session->closeRestricted(fd);
return nullptr;
}
drmModeFreeResources(resources);

struct stat sbuf;
if (fstat(fd, &sbuf) < 0) {
qCDebug(gLcDrm, "Failed to fstat \"%s\": %s", qPrintable(path), strerror(errno));
m_session->closeRestricted(fd);
return nullptr;
}

qCInfo(gLcDrm) << "Adding DRM device:" << path;

auto *gpu = new DrmGpu(path, fd, sbuf.st_rdev);
m_gpus.append(gpu);
emit gpuAdded(gpu);
return gpu;
}

DrmGpu *DrmBackend::findGpu(dev_t deviceId) const
{
auto it = std::find_if(m_gpus.begin(), m_gpus.end(),
[deviceId](const auto *gpu) { return gpu->deviceId() == deviceId; });
return it == m_gpus.end() ? nullptr : *it;
}

void DrmBackend::createDisplay()
{
EGLNativeDisplayType nativeDisplay = platformDisplay();

PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplay = nullptr;
const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
if (extensions
&& (strstr(extensions, "EGL_KHR_platform_gbm")
|| strstr(extensions, "EGL_MESA_platform_gbm"))) {
getPlatformDisplay = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
eglGetProcAddress("eglGetPlatformDisplayEXT"));
}

if (getPlatformDisplay) {
m_eglDisplay = getPlatformDisplay(EGL_PLATFORM_GBM_KHR, nativeDisplay, nullptr);
} else {
qCDebug(gLcDrm, "No eglGetPlatformDisplay for GBM, falling back to eglGetDisplay");
m_eglDisplay = eglGetDisplay(nativeDisplay);
}
}

void DrmBackend::updateOutputs()
{
for (auto it = m_gpus.begin(); it != m_gpus.end(); ++it) {
auto *gpu = (*it);
if (gpu->isRemoved())
gpu->removeOutputs();
else
gpu->updateOutputs();
}

for (auto it = m_gpus.begin(); it != m_gpus.end();) {
auto *gpu = (*it);

if (gpu->isRemoved() || (gpu != m_gpu && gpu->hasDrmOutputs())) {
qCDebug(gLcDrm, "Removing GPU \"%s\"", qPrintable(gpu->deviceNode()));
it = m_gpus.erase(it);
emit gpuRemoved(gpu);
} else {
it++;
}
}
}

void DrmBackend::addOutput(DrmOutput *output)
{
}

void DrmBackend::removeOutput(DrmOutput *output)
{
}

} // namespace Platform

} // namespace Aurora
Loading

0 comments on commit b9de2c5

Please sign in to comment.