-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Closes: #43
- Loading branch information
Showing
25 changed files
with
1,596 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"Keys": [ "drm" ] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.