-
-
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
18 changed files
with
785 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,38 @@ | ||
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 | ||
drmoutput.cpp drmoutput.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,190 @@ | ||
// 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(this)) | ||
, m_udev(new Aurora::PlatformSupport::Udev()) | ||
{ | ||
} | ||
|
||
DrmBackend::~DrmBackend() | ||
{ | ||
if (m_udev) { | ||
delete m_udev; | ||
m_udev = nullptr; | ||
} | ||
} | ||
|
||
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); | ||
} | ||
|
||
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 | ||
qCDebug(gLcDrm) << "Found the following video devices:"; | ||
for (auto *udevDevice : qAsConst(udevDevices)) { | ||
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::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,62 @@ | ||
// 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(); | ||
|
||
EGLDisplay eglDisplay() const; | ||
EGLNativeDisplayType platformDisplay() 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; | ||
|
||
DrmGpu *findGpu(dev_t deviceId) const; | ||
DrmGpu *addGpu(const QString &path); | ||
|
||
void createDisplay(); | ||
|
||
private slots: | ||
void addOutput(DrmOutput *output); | ||
void removeOutput(DrmOutput *output); | ||
void updateOutputs(); | ||
}; | ||
|
||
} // 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,46 @@ | ||
// SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini <[email protected]> | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
|
||
#include "drmbackend.h" | ||
#include "drmcursor.h" | ||
#include "drmgpu.h" | ||
#include "drmloggingcategories.h" | ||
|
||
#include <gbm.h> | ||
#include <xf86drm.h> | ||
#include <xf86drmMode.h> | ||
|
||
#ifndef DRM_CAP_CURSOR_WIDTH | ||
# define DRM_CAP_CURSOR_WIDTH 0x8 | ||
#endif | ||
|
||
#ifndef DRM_CAP_CURSOR_HEIGHT | ||
# define DRM_CAP_CURSOR_HEIGHT 0x9 | ||
#endif | ||
|
||
namespace Aurora { | ||
|
||
namespace Platform { | ||
|
||
DrmCursor::DrmCursor(QObject *parent) | ||
: QObject(parent) | ||
{ | ||
const auto fd = DrmBackend::instance()->primaryGpu()->fd(); | ||
auto *gbmDevice = DrmBackend::instance()->primaryGpu()->gbmDevice(); | ||
|
||
uint64_t width, height; | ||
if ((drmGetCap(fd, DRM_CAP_CURSOR_WIDTH, &width) == 0) | ||
&& (drmGetCap(fd, DRM_CAP_CURSOR_HEIGHT, &height) == 0)) { | ||
m_cursorSize.setWidth(width); | ||
m_cursorSize.setHeight(height); | ||
} | ||
|
||
m_gbmBo = gbm_bo_create(gbmDevice, m_cursorSize.width(), m_cursorSize.height(), | ||
GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE); | ||
if (!m_gbmBo) | ||
qCWarning(gLcDrm) << "Failed to create buffer for cursor"; | ||
} | ||
|
||
} // namespace Platform | ||
|
||
} // namespace Aurora |
Oops, something went wrong.