platforms/drm: save modelist in DrmConnector

This commit is contained in:
Xaver Hugl 2021-05-25 23:08:31 +02:00
parent ee4b4fc9a3
commit c89961a4e2
7 changed files with 163 additions and 110 deletions

View file

@ -275,9 +275,8 @@ bool DrmGpu::updateOutputs()
output->m_conn = con;
output->m_crtc = crtc;
output->m_primaryPlane = primary;
output->m_mode = connector->modes[0];
if (!output->init(connector.data())) {
if (!output->init()) {
qCWarning(KWIN_DRM) << "Failed to create output for connector " << con->id();
delete output;
continue;

View file

@ -3,6 +3,7 @@
This file is part of the KDE project.
SPDX-FileCopyrightText: 2016 Roman Gilg <subdiff@gmail.com>
SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
@ -35,6 +36,25 @@ DrmConnector::DrmConnector(DrmGpu *gpu, uint32_t connectorId)
DrmConnector::~DrmConnector() = default;
namespace {
quint64 refreshRateForMode(_drmModeModeInfo *m)
{
// Calculate higher precision (mHz) refresh rate
// logic based on Weston, see compositor-drm.c
quint64 refreshRate = (m->clock * 1000000LL / m->htotal + m->vtotal / 2) / m->vtotal;
if (m->flags & DRM_MODE_FLAG_INTERLACE) {
refreshRate *= 2;
}
if (m->flags & DRM_MODE_FLAG_DBLSCAN) {
refreshRate /= 2;
}
if (m->vscan > 1) {
refreshRate /= m->vscan;
}
return refreshRate;
}
}
bool DrmConnector::init()
{
if (!m_conn || !m_conn->count_modes) {
@ -108,16 +128,26 @@ bool DrmConnector::init()
m_physicalSize = overwriteSize;
}
// init modes
for (int i = 0; i < m_conn->count_modes; i++) {
auto mode = m_conn->modes[i];
Mode m;
m.mode = mode;
m.size = QSize(mode.hdisplay, mode.vdisplay);
m.refreshRate = refreshRateForMode(&mode);
m_modes << m;
}
return true;
}
bool DrmConnector::isConnected()
{
DrmScopedPointer<drmModeConnector> con(drmModeGetConnector(gpu()->fd(), id()));
if (!con) {
m_conn.reset(drmModeGetConnector(gpu()->fd(), id()));
if (!m_conn) {
return false;
}
return con->connection == DRM_MODE_CONNECTED;
return m_conn->connection == DRM_MODE_CONNECTED;
}
static QHash<int, QByteArray> s_connectorNames = {
@ -168,6 +198,73 @@ QSize DrmConnector::physicalSize() const
return m_physicalSize;
}
const DrmConnector::Mode &DrmConnector::currentMode() const
{
return m_modes[m_modeIndex];
}
int DrmConnector::currentModeIndex() const
{
return m_modeIndex;
}
const QVector<DrmConnector::Mode> &DrmConnector::modes()
{
return m_modes;
}
void DrmConnector::setModeIndex(int index)
{
m_modeIndex = index;
}
static bool checkIfEqual(drmModeModeInfo one, drmModeModeInfo two)
{
return one.clock == two.clock
&& one.hdisplay == two.hdisplay
&& one.hsync_start == two.hsync_start
&& one.hsync_end == two.hsync_end
&& one.htotal == two.htotal
&& one.hskew == two.hskew
&& one.vdisplay == two.vdisplay
&& one.vsync_start == two.vsync_start
&& one.vsync_end == two.vsync_end
&& one.vtotal == two.vtotal
&& one.vscan == two.vscan
&& one.vrefresh == two.vrefresh;
}
void DrmConnector::findCurrentMode(drmModeModeInfo currentMode)
{
for (int i = 0; i < m_modes.count(); i++) {
if (checkIfEqual(m_modes[i].mode, currentMode)) {
m_modeIndex = i;
return;
}
}
m_modeIndex = 0;
}
AbstractWaylandOutput::SubPixel DrmConnector::subpixel() const
{
switch (m_conn->subpixel) {
case DRM_MODE_SUBPIXEL_UNKNOWN:
return AbstractWaylandOutput::SubPixel::Unknown;
case DRM_MODE_SUBPIXEL_NONE:
return AbstractWaylandOutput::SubPixel::None;
case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
return AbstractWaylandOutput::SubPixel::Horizontal_RGB;
case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
return AbstractWaylandOutput::SubPixel::Horizontal_BGR;
case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
return AbstractWaylandOutput::SubPixel::Vertical_RGB;
case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
return AbstractWaylandOutput::SubPixel::Vertical_BGR;
default:
Q_UNREACHABLE();
}
}
bool DrmConnector::hasOverscan() const
{
return m_props[static_cast<uint32_t>(PropertyIndex::Overscan)] || m_props[static_cast<uint32_t>(PropertyIndex::Underscan)];

View file

@ -3,16 +3,21 @@
This file is part of the KDE project.
SPDX-FileCopyrightText: 2016 Roman Gilg <subdiff@gmail.com>
SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef KWIN_DRM_OBJECT_CONNECTOR_H
#define KWIN_DRM_OBJECT_CONNECTOR_H
#pragma once
#include <QPoint>
#include <QSize>
#include <QSize>
#include "drm_object.h"
#include "edid.h"
#include "drm_pointer.h"
#include "abstract_wayland_output.h"
namespace KWin
{
@ -72,6 +77,19 @@ public:
bool isInternal() const;
QSize physicalSize() const;
struct Mode {
drmModeModeInfo mode;
QSize size;
uint32_t refreshRate;
};
const Mode &currentMode() const;
int currentModeIndex() const;
const QVector<Mode> &modes();
void setModeIndex(int index);
void findCurrentMode(drmModeModeInfo currentMode);
AbstractWaylandOutput::SubPixel subpixel() const;
bool hasOverscan() const;
uint32_t overscan() const;
void setOverscan(uint32_t overscan, const QSize &modeSize);
@ -83,10 +101,9 @@ private:
QVector<uint32_t> m_encoders;
Edid m_edid;
QSize m_physicalSize = QSize(-1, -1);
QVector<Mode> m_modes;
int m_modeIndex = 0;
};
}
#endif

View file

@ -108,4 +108,10 @@ bool DrmCrtc::isVrrEnabled() const
return false;
}
drmModeModeInfo DrmCrtc::queryCurrentMode()
{
m_crtc.reset(drmModeGetCrtc(gpu()->fd(), id()));
return m_crtc->mode;
}
}

View file

@ -64,6 +64,8 @@ public:
bool setVrr(bool enable);
bool isVrrEnabled() const;
drmModeModeInfo queryCurrentMode();
private:
DrmScopedPointer<drmModeCrtc> m_crtc;
QSharedPointer<DrmBuffer> m_currentBuffer;

View file

@ -193,52 +193,13 @@ void DrmOutput::moveCursor()
}
}
namespace {
quint64 refreshRateForMode(_drmModeModeInfo *m)
{
// Calculate higher precision (mHz) refresh rate
// logic based on Weston, see compositor-drm.c
quint64 refreshRate = (m->clock * 1000000LL / m->htotal + m->vtotal / 2) / m->vtotal;
if (m->flags & DRM_MODE_FLAG_INTERLACE) {
refreshRate *= 2;
}
if (m->flags & DRM_MODE_FLAG_DBLSCAN) {
refreshRate /= 2;
}
if (m->vscan > 1) {
refreshRate /= m->vscan;
}
return refreshRate;
}
}
static AbstractWaylandOutput::SubPixel drmSubPixelToKWinSubPixel(drmModeSubPixel subpixel)
{
switch (subpixel) {
case DRM_MODE_SUBPIXEL_UNKNOWN:
return AbstractWaylandOutput::SubPixel::Unknown;
case DRM_MODE_SUBPIXEL_NONE:
return AbstractWaylandOutput::SubPixel::None;
case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
return AbstractWaylandOutput::SubPixel::Horizontal_RGB;
case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
return AbstractWaylandOutput::SubPixel::Horizontal_BGR;
case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
return AbstractWaylandOutput::SubPixel::Vertical_RGB;
case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
return AbstractWaylandOutput::SubPixel::Vertical_BGR;
default:
Q_UNREACHABLE();
}
}
bool DrmOutput::init(drmModeConnector *connector)
bool DrmOutput::init()
{
if (m_gpu->atomicModeSetting() && !m_primaryPlane) {
return false;
}
setSubPixelInternal(drmSubPixelToKWinSubPixel(connector->subpixel));
setSubPixelInternal(m_conn->subpixel());
setInternal(m_conn->isInternal());
setCapabilityInternal(DrmOutput::Capability::Dpms);
if (m_conn->hasOverscan()) {
@ -249,7 +210,7 @@ bool DrmOutput::init(drmModeConnector *connector)
setCapabilityInternal(Capability::Vrr);
setVrrPolicy(RenderLoop::VrrPolicy::Automatic);
}
initOutputDevice(connector);
initOutputDevice();
if (!m_gpu->atomicModeSetting() && !m_crtc->blank(this)) {
// We use legacy mode and the initial output blank failed.
@ -260,27 +221,25 @@ bool DrmOutput::init(drmModeConnector *connector)
return true;
}
void DrmOutput::initOutputDevice(drmModeConnector *connector)
void DrmOutput::initOutputDevice()
{
// read in mode information
QVector<Mode> modes;
modes.reserve(connector->count_modes);
for (int i = 0; i < connector->count_modes; ++i) {
// TODO: in AMS here we could read and store for later every mode's blob_id
// would simplify isCurrentMode(..) and presentAtomically(..) in case of mode set
auto *m = &connector->modes[i];
m_conn->findCurrentMode(m_crtc->queryCurrentMode());
auto modelist = m_conn->modes();
QVector<Mode> modes;
modes.reserve(modelist.count());
for (int i = 0; i < modelist.count(); ++i) {
Mode mode;
if (isCurrentMode(m)) {
if (i == m_conn->currentModeIndex()) {
mode.flags |= ModeFlag::Current;
}
if (m->type & DRM_MODE_TYPE_PREFERRED) {
if (modelist[i].mode.type & DRM_MODE_TYPE_PREFERRED) {
mode.flags |= ModeFlag::Preferred;
}
mode.id = i;
mode.size = QSize(m->hdisplay, m->vdisplay);
mode.refreshRate = refreshRateForMode(m);
mode.size = modelist[i].size;
mode.refreshRate = modelist[i].refreshRate;
modes << mode;
}
@ -290,25 +249,6 @@ void DrmOutput::initOutputDevice(drmModeConnector *connector)
m_conn->physicalSize(), modes, m_conn->edid()->raw());
}
bool DrmOutput::isCurrentMode(const drmModeModeInfo *mode) const
{
return mode->clock == m_mode.clock
&& mode->hdisplay == m_mode.hdisplay
&& mode->hsync_start == m_mode.hsync_start
&& mode->hsync_end == m_mode.hsync_end
&& mode->htotal == m_mode.htotal
&& mode->hskew == m_mode.hskew
&& mode->vdisplay == m_mode.vdisplay
&& mode->vsync_start == m_mode.vsync_start
&& mode->vsync_end == m_mode.vsync_end
&& mode->vtotal == m_mode.vtotal
&& mode->vscan == m_mode.vscan
&& mode->vrefresh == m_mode.vrefresh
&& mode->flags == m_mode.flags
&& mode->type == m_mode.type
&& qstrcmp(mode->name, m_mode.name) == 0;
}
bool DrmOutput::initCursor(const QSize &cursorSize)
{
auto createCursor = [this, cursorSize] (int index) {
@ -548,14 +488,12 @@ void DrmOutput::updateTransform(Transform transform)
void DrmOutput::updateMode(uint32_t width, uint32_t height, uint32_t refreshRate)
{
if (m_mode.hdisplay == width && m_mode.vdisplay == height && refreshRateForMode(&m_mode) == refreshRate) {
if (m_conn->currentMode().size == QSize(width, height) && m_conn->currentMode().refreshRate == refreshRate) {
return;
}
// try to find a fitting mode
DrmScopedPointer<drmModeConnector> connector(drmModeGetConnectorCurrent(m_gpu->fd(), m_conn->id()));
for (int i = 0; i < connector->count_modes; i++) {
auto mode = connector->modes[i];
if (mode.hdisplay == width && mode.vdisplay == height && refreshRateForMode(&mode) == refreshRate) {
auto modelist = m_conn->modes();
for (int i = 0; i < modelist.size(); i++) {
if (modelist[i].size == QSize(width, height) && modelist[i].refreshRate == refreshRate) {
updateMode(i);
return;
}
@ -566,17 +504,10 @@ void DrmOutput::updateMode(uint32_t width, uint32_t height, uint32_t refreshRate
void DrmOutput::updateMode(int modeIndex)
{
// get all modes on the connector
DrmScopedPointer<drmModeConnector> connector(drmModeGetConnector(m_gpu->fd(), m_conn->id()));
if (connector->count_modes <= modeIndex) {
// TODO: error?
if (m_conn->currentModeIndex() == modeIndex) {
return;
}
if (isCurrentMode(&connector->modes[modeIndex])) {
// nothing to do
return;
}
m_mode = connector->modes[modeIndex];
m_conn->setModeIndex(modeIndex);
m_modesetRequested = true;
// aspect ratio might need to be adjusted
m_conn->setOverscan(m_conn->overscan(), modeSize());
@ -585,8 +516,8 @@ void DrmOutput::updateMode(int modeIndex)
void DrmOutput::setCurrentModeInternal()
{
AbstractWaylandOutput::setCurrentModeInternal(QSize(m_mode.hdisplay, m_mode.vdisplay),
refreshRateForMode(&m_mode));
auto mode = m_conn->currentMode();
AbstractWaylandOutput::setCurrentModeInternal(mode.size, mode.refreshRate);
}
void DrmOutput::pageFlipped()
@ -686,7 +617,7 @@ bool DrmOutput::presentAtomically(const QSharedPointer<DrmBuffer> &buffer)
qCDebug(KWIN_DRM) << "Atomic test commit failed. Aborting present.";
// go back to previous state
if (m_lastWorkingState.valid) {
m_mode = m_lastWorkingState.mode;
m_conn->setModeIndex(m_lastWorkingState.modeIndex);
setTransformInternal(m_lastWorkingState.transform);
setGlobalPos(m_lastWorkingState.globalPos);
if (m_primaryPlane) {
@ -713,14 +644,14 @@ bool DrmOutput::presentAtomically(const QSharedPointer<DrmBuffer> &buffer)
}
if (wasModeset) {
// store current mode set as new good state
m_lastWorkingState.mode = m_mode;
m_lastWorkingState.modeIndex = m_conn->currentModeIndex();
m_lastWorkingState.transform = transform();
m_lastWorkingState.globalPos = globalPos();
if (m_primaryPlane) {
m_lastWorkingState.planeTransformations = m_primaryPlane->transformation();
}
m_lastWorkingState.valid = true;
m_renderLoop->setRefreshRate(refreshRateForMode(&m_mode));
m_renderLoop->setRefreshRate(m_conn->currentMode().refreshRate);
}
m_pageFlipPending = true;
return true;
@ -755,7 +686,8 @@ bool DrmOutput::presentLegacy(const QSharedPointer<DrmBuffer> &buffer)
bool DrmOutput::setModeLegacy(DrmBuffer *buffer)
{
uint32_t connId = m_conn->id();
if (drmModeSetCrtc(m_gpu->fd(), m_crtc->id(), buffer->bufferId(), 0, 0, &connId, 1, &m_mode) == 0) {
auto mode = m_conn->currentMode().mode;
if (drmModeSetCrtc(m_gpu->fd(), m_crtc->id(), buffer->bufferId(), 0, 0, &connId, 1, &mode) == 0) {
return true;
} else {
qCWarning(KWIN_DRM) << "Mode setting failed";
@ -801,7 +733,8 @@ bool DrmOutput::doAtomicCommit(AtomicCommitMode mode)
// Do we need to set a new mode?
if (m_modesetRequested) {
if (m_dpmsModePending == DpmsMode::On) {
if (drmModeCreatePropertyBlob(m_gpu->fd(), &m_mode, sizeof(m_mode), &m_blobId) != 0) {
auto mode = m_conn->currentMode();
if (drmModeCreatePropertyBlob(m_gpu->fd(), &mode, sizeof(mode), &m_blobId) != 0) {
qCWarning(KWIN_DRM) << "Failed to create property blob";
errorHandler();
return false;
@ -943,7 +876,7 @@ void DrmOutput::setVrr(bool enable)
bool DrmOutput::isCursorVisible()
{
return m_cursor[m_cursorIndex] && QRect(m_cursorPos, m_cursor[m_cursorIndex]->size()).intersects(QRect(0, 0, m_mode.vdisplay, m_mode.hdisplay));
return m_cursor[m_cursorIndex] && QRect(m_cursorPos, m_cursor[m_cursorIndex]->size()).intersects(QRect(0, 0, modeSize().width(), modeSize().height()));
}
DrmBuffer *DrmOutput::currentBuffer() const

View file

@ -49,7 +49,7 @@ public:
bool hideCursor();
bool updateCursor();
void moveCursor();
bool init(drmModeConnector *connector);
bool init();
bool present(const QSharedPointer<DrmBuffer> &buffer, QRegion damagedRegion);
void pageFlipped();
@ -104,7 +104,7 @@ private:
bool presentLegacy(const QSharedPointer<DrmBuffer> &buffer);
bool setModeLegacy(DrmBuffer *buffer);
void initOutputDevice(drmModeConnector *connector);
void initOutputDevice();
bool isCurrentMode(const drmModeModeInfo *mode) const;
@ -139,7 +139,6 @@ private:
DrmConnector *m_conn = nullptr;
DrmCrtc *m_crtc = nullptr;
bool m_lastGbm = false;
drmModeModeInfo m_mode;
DpmsMode m_dpmsModePending = DpmsMode::On;
RenderLoop *m_renderLoop;
@ -154,7 +153,7 @@ private:
struct {
Transform transform;
drmModeModeInfo mode;
int modeIndex = 0;
DrmPlane::Transformations planeTransformations;
QPoint globalPos;
bool valid = false;