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 Nov 10, 2023
1 parent 191eb80 commit 3d5d13f
Show file tree
Hide file tree
Showing 25 changed files with 1,596 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ if(FEATURE_aurora_qpa)
add_subdirectory(src/plugins/platforms/eglfs/deviceintegration/eglfs_x11)
endif()
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 @@ -471,6 +471,35 @@ if(FEATURE_aurora_vulkan_server_buffer)
endif()
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
42 changes: 42 additions & 0 deletions src/plugins/deviceintegration/drm/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
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"
)

liri_add_plugin(AuroraDeviceIntegrationDrm
TYPE
"aurora/deviceintegration"
OUTPUT_NAME
drm
SOURCES
drm.json
drmbackend.cpp drmbackend.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}
PUBLIC_LIBRARIES
Qt::Core
Qt::Gui
Liri::AuroraCore
Liri::AuroraPlatform
Liri::AuroraPlatformPrivate
Liri::AuroraUdev
EGL::EGL
PkgConfig::Libdrm
PkgConfig::Gbm
)

liri_finalize_plugin(AuroraDeviceIntegrationDrm)
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
68 changes: 68 additions & 0 deletions src/plugins/deviceintegration/drm/drmbackend.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini <[email protected]>
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include <QObject>

#include <LiriAuroraPlatform/Session>
#include <LiriAuroraUdev/Udev>

#include <EGL/egl.h>

namespace Aurora {

namespace Platform {

class DrmGpu;
class DrmOutput;

class DrmBackend : public QObject
{
Q_OBJECT
public:
explicit DrmBackend(QObject *parent = nullptr);
~DrmBackend();

Session *session() const;

EGLDisplay eglDisplay() const;
EGLNativeDisplayType platformDisplay() const;

bool isAtomicEnabled() const;

DrmGpu *primaryGpu() const;

void initialize();
void destroy();

static DrmBackend *instance();

signals:
void gpuAdded(DrmGpu *gpu);
void gpuRemoved(DrmGpu *gpu);

private:
bool m_initialized = false;
Session *m_session = nullptr;
Aurora::PlatformSupport::Udev *m_udev = nullptr;
DrmGpu *m_gpu = nullptr;
QList<DrmGpu *> m_gpus;
EGLDisplay m_eglDisplay = EGL_NO_DISPLAY;
bool m_enableAtomic = true;

DrmGpu *findGpu(dev_t deviceId) const;
DrmGpu *addGpu(const QString &path);

void createDisplay();
void updateOutputs();

private slots:
void addOutput(DrmOutput *output);
void removeOutput(DrmOutput *output);
void updateOutputs();
};

} // namespace Platform

} // namespace Aurora
Loading

0 comments on commit 3d5d13f

Please sign in to comment.