Commit 9cd046f2 authored by Xaver Hugl's avatar Xaver Hugl
Browse files

platforms/drm: add GPU hotplug support

parent 0284aeb7
......@@ -38,6 +38,10 @@ public:
return QRegion();
}
DrmGpu *gpu() const {
return m_gpu;
}
static AbstractEglDrmBackend *renderingBackend() {
return static_cast<AbstractEglDrmBackend*>(primaryBackend());
}
......
......@@ -199,35 +199,7 @@ bool DrmBackend::initialize()
}
for (unsigned int gpu_index = 0; gpu_index < devices.size(); gpu_index++) {
auto device = std::move(devices.at(gpu_index));
auto devNode = QByteArray(device->devNode());
int fd = session()->openRestricted(devNode.constData());
if (fd < 0) {
qCWarning(KWIN_DRM) << "failed to open drm device at" << devNode;
return false;
}
// try to make a simple drm get resource call, if it fails it is not useful for us
drmModeRes *resources = drmModeGetResources(fd);
if (!resources) {
qCDebug(KWIN_DRM) << "Skipping KMS incapable drm device node at" << devNode;
session()->closeRestricted(fd);
continue;
}
drmModeFreeResources(resources);
DrmGpu *gpu = new DrmGpu(this, devNode, fd, device->sysNum());
if (!gpu->useEglStreams() || gpu_index == 0) {
m_gpus.append(gpu);
m_active = true;
connect(gpu, &DrmGpu::outputAdded, this, &DrmBackend::addOutput);
connect(gpu, &DrmGpu::outputRemoved, this, &DrmBackend::removeOutput);
if (gpu->useEglStreams()) {
break;
}
} else {
delete gpu;
}
addGpu(std::move(devices.at(gpu_index)));
}
// trying to activate Atomic Mode Setting (this means also Universal Planes)
......@@ -264,25 +236,73 @@ void DrmBackend::handleUdevEvent()
if (!session()->isActive()) {
continue;
}
bool drm = false;
for (auto gpu : m_gpus) {
DrmGpu *drmGpu = nullptr;
for (const auto &gpu : qAsConst(m_gpus)) {
if (gpu->drmId() == device->sysNum()) {
drm = true;
drmGpu = gpu;
break;
}
}
if (!drm) {
continue;
}
if (device->hasProperty("HOTPLUG", "1")) {
qCDebug(KWIN_DRM) << "Received hot plug event for monitored drm device";
updateOutputs();
updateCursor();
if (device->action() == QStringLiteral("add")) {
if (m_gpus.isEmpty() || !primaryGpu()->useEglStreams()) {
if (const auto &gpu = addGpu(std::move(device))) {
updateOutputs();
updateCursor();
emit gpuAdded(gpu);
}
}
} else if (drmGpu) {
if (device->action() == QStringLiteral("change")) {
qCDebug(KWIN_DRM) << "Received hot plug event for monitored drm device";
updateOutputs();
updateCursor();
} else if (device->action() == QStringLiteral("remove")) {
if (primaryGpu() == drmGpu) {
qCCritical(KWIN_DRM) << "Primary gpu has been removed! Quitting...";
kwinApp()->quit();
return;
} else {
emit gpuRemoved(drmGpu);
m_gpus.removeOne(drmGpu);
delete drmGpu;
updateOutputs();
updateCursor();
}
}
}
}
}
DrmGpu *DrmBackend::addGpu(std::unique_ptr<UdevDevice> device)
{
int fd = session()->openRestricted(device->devNode());
if (fd < 0) {
qCWarning(KWIN_DRM) << "failed to open drm device at" << device->devNode();
return nullptr;
}
// try to make a simple drm get resource call, if it fails it is not useful for us
drmModeRes *resources = drmModeGetResources(fd);
if (!resources) {
qCDebug(KWIN_DRM) << "Skipping KMS incapable drm device node at" << device->devNode();
session()->closeRestricted(fd);
return nullptr;
}
drmModeFreeResources(resources);
DrmGpu *gpu = new DrmGpu(this, device->devNode(), fd, device->sysNum());
if (!gpu->useEglStreams() || m_gpus.isEmpty()) {
m_gpus.append(gpu);
m_active = true;
connect(gpu, &DrmGpu::outputAdded, this, &DrmBackend::addOutput);
connect(gpu, &DrmGpu::outputRemoved, this, &DrmBackend::removeOutput);
return gpu;
} else {
delete gpu;
return nullptr;
}
}
void DrmBackend::addOutput(DrmOutput *o)
{
m_outputs.append(o);
......@@ -584,9 +604,9 @@ OpenGLBackend *DrmBackend::createOpenGLBackend()
#endif
#if HAVE_GBM
auto backend0 = new EglGbmBackend(this, m_gpus.at(0));
AbstractEglBackend::setPrimaryBackend(backend0);
EglMultiBackend *backend = new EglMultiBackend(backend0);
auto primaryBackend = new EglGbmBackend(this, m_gpus.at(0));
AbstractEglBackend::setPrimaryBackend(primaryBackend);
EglMultiBackend *backend = new EglMultiBackend(this, primaryBackend);
for (int i = 1; i < m_gpus.count(); i++) {
auto backendi = new EglGbmBackend(this, m_gpus.at(i));
backend->addBackend(backendi);
......@@ -650,4 +670,9 @@ DmaBufTexture *DrmBackend::createDmaBufTexture(const QSize &size)
#endif
}
DrmGpu *DrmBackend::primaryGpu() const
{
return m_gpus.isEmpty() ? nullptr : m_gpus[0];
}
}
......@@ -36,6 +36,7 @@ namespace KWin
class Udev;
class UdevMonitor;
class UdevDevice;
class DrmOutput;
class DrmPlane;
......@@ -79,9 +80,15 @@ public:
QString supportInformation() const override;
DrmGpu *primaryGpu() const;
public Q_SLOTS:
void turnOutputsOn();
Q_SIGNALS:
void gpuRemoved(DrmGpu *gpu);
void gpuAdded(DrmGpu *gpu);
protected:
void doHideCursor() override;
void doShowCursor() override;
......@@ -104,6 +111,8 @@ private:
DrmOutput *findOutput(quint32 connector);
void updateOutputsEnabled();
void handleUdevEvent();
DrmGpu *addGpu(std::unique_ptr<UdevDevice> device);
QScopedPointer<Udev> m_udev;
QScopedPointer<UdevMonitor> m_udevMonitor;
Session *m_session = nullptr;
......
......@@ -81,10 +81,13 @@ DrmGpu::DrmGpu(DrmBackend *backend, QByteArray devNode, int fd, int drmId)
DrmGpu::~DrmGpu()
{
waitIdle();
const auto outputs = m_outputs;
for (const auto &output : outputs) {
removeOutput(output);
}
if (m_eglDisplay != EGL_NO_DISPLAY) {
eglTerminate(m_eglDisplay);
}
qDeleteAll(m_outputs);
qDeleteAll(m_crtcs);
qDeleteAll(m_connectors);
qDeleteAll(m_planes);
......@@ -293,15 +296,7 @@ bool DrmGpu::updateOutputs()
m_outputs = connectedOutputs;
for(DrmOutput *removedOutput : removedOutputs) {
emit outputRemoved(removedOutput);
removedOutput->teardown();
removedOutput->m_crtc = nullptr;
m_connectors.removeOne(removedOutput->m_conn);
delete removedOutput->m_conn;
removedOutput->m_conn = nullptr;
if (removedOutput->m_primaryPlane) {
m_unusedPlanes << removedOutput->m_primaryPlane;
}
removeOutput(removedOutput);
}
qDeleteAll(oldConnectors);
......@@ -418,4 +413,18 @@ void DrmGpu::dispatchEvents()
drmHandleEvent(m_fd, &context);
}
void DrmGpu::removeOutput(DrmOutput *output)
{
m_outputs.removeOne(output);
emit outputRemoved(output);
output->teardown();
output->m_crtc = nullptr;
m_connectors.removeOne(output->m_conn);
delete output->m_conn;
output->m_conn = nullptr;
if (output->m_primaryPlane) {
m_unusedPlanes << output->m_primaryPlane;
}
}
}
......@@ -114,6 +114,7 @@ private:
void dispatchEvents();
DrmPlane *getCompatiblePlane(DrmPlane::TypeIndex typeIndex, DrmCrtc *crtc);
DrmOutput *findOutput(quint32 connector);
void removeOutput(DrmOutput *output);
DrmBackend* const m_backend;
AbstractEglBackend *m_eglBackend;
......
......@@ -9,13 +9,20 @@
#include "egl_multi_backend.h"
#include "logging.h"
#include "egl_gbm_backend.h"
#include "drm_backend.h"
#include "drm_gpu.h"
namespace KWin
{
EglMultiBackend::EglMultiBackend(AbstractEglDrmBackend *backend0) : OpenGLBackend()
EglMultiBackend::EglMultiBackend(DrmBackend *backend, AbstractEglDrmBackend *primaryEglBackend)
: OpenGLBackend()
, m_platform(backend)
{
m_backends.append(backend0);
connect(m_platform, &DrmBackend::gpuAdded, this, &EglMultiBackend::addGpu);
connect(m_platform, &DrmBackend::gpuRemoved, this, &EglMultiBackend::removeGpu);
m_backends.append(primaryEglBackend);
setIsDirectRendering(true);
}
......@@ -33,23 +40,11 @@ void EglMultiBackend::init()
for (auto b : qAsConst(m_backends)) {
b->init();
}
// if any don't support it set it to not supported
setSupportsBufferAge(true);
setSupportsPartialUpdate(true);
setSupportsSwapBuffersWithDamage(true);
for (auto b : qAsConst(m_backends)) {
if (!b->supportsBufferAge()) {
setSupportsBufferAge(false);
}
if (!b->supportsPartialUpdate()) {
setSupportsPartialUpdate(false);
}
if (!b->supportsSwapBuffersWithDamage()) {
setSupportsSwapBuffersWithDamage(false);
}
}
// we only care about the rendering GPU here
// we only care about the rendering GPU
setSupportsSurfacelessContext(m_backends[0]->supportsSurfacelessContext());
setSupportsBufferAge(m_backends[0]->supportsBufferAge());
setSupportsPartialUpdate(m_backends[0]->supportsPartialUpdate());
setSupportsSwapBuffersWithDamage(m_backends[0]->supportsSwapBuffersWithDamage());
// these are client extensions and the same for all egl backends
setExtensions(m_backends[0]->extensions());
......@@ -139,4 +134,23 @@ bool EglMultiBackend::directScanoutAllowed(int screenId) const
return backend->directScanoutAllowed(internalScreenId);
}
void EglMultiBackend::addGpu(DrmGpu *gpu)
{
// secondary GPUs are atm guaranteed to be gbm
auto backend = new EglGbmBackend(m_platform, gpu);
backend->init();
m_backends.append(backend);
}
void EglMultiBackend::removeGpu(DrmGpu *gpu)
{
auto it = std::find_if(m_backends.constBegin(), m_backends.constEnd(), [gpu](auto backend) {
return backend->gpu() == gpu;
});
if (it != m_backends.constEnd()) {
m_backends.removeOne(*it);
delete *it;
}
}
}
......@@ -15,10 +15,11 @@
namespace KWin
{
class EglMultiBackend : public OpenGLBackend
class EglMultiBackend : public QObject, public OpenGLBackend
{
Q_OBJECT
public:
EglMultiBackend(AbstractEglDrmBackend *backend0);
EglMultiBackend(DrmBackend *backend, AbstractEglDrmBackend *primaryEglBackend);
~EglMultiBackend();
void init() override;
......@@ -39,7 +40,12 @@ public:
bool directScanoutAllowed(int screen) const override;
public Q_SLOTS:
void addGpu(DrmGpu *gpu);
void removeGpu(DrmGpu *gpu);
private:
DrmBackend *m_platform;
QVector<AbstractEglDrmBackend*> m_backends;
AbstractEglDrmBackend *findBackend(int screenId, int& internalScreenId) const;
......
......@@ -253,6 +253,11 @@ bool UdevDevice::isBootVga() const
return systAttrValue && qstrcmp(systAttrValue, "1") == 0;
}
QString UdevDevice::action() const
{
return QString::fromLocal8Bit(udev_device_get_action(m_device));
}
UdevMonitor::UdevMonitor(Udev *udev)
: m_monitor(udev_monitor_new_from_netlink(*udev, "udev"))
{
......
......@@ -32,6 +32,7 @@ public:
int sysNum() const;
const char *property(const char *key);
bool hasProperty(const char *key, const char *value);
QString action() const;
QMap<QByteArray, QByteArray> properties() const;
bool isBootVga() const;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment