21#include <libxcvt/libxcvt.h>
26static QSize resolutionForMode(
const drmModeModeInfo *info)
28 return QSize(info->hdisplay, info->vdisplay);
31static quint64 refreshRateForMode(_drmModeModeInfo *m)
35 quint64 refreshRate = (m->clock * 1000000LL / m->htotal + m->vtotal / 2) / m->vtotal;
36 if (m->flags & DRM_MODE_FLAG_INTERLACE) {
39 if (m->flags & DRM_MODE_FLAG_DBLSCAN) {
43 refreshRate /= m->vscan;
48static OutputMode::Flags flagsForMode(
const drmModeModeInfo *info, OutputMode::Flags additionalFlags)
50 OutputMode::Flags flags = additionalFlags;
51 if (info->type & DRM_MODE_TYPE_PREFERRED) {
58 :
OutputMode(resolutionForMode(&nativeMode), refreshRateForMode(&nativeMode), flagsForMode(&nativeMode, additionalFlags))
59 , m_connector(connector)
60 , m_nativeMode(nativeMode)
74 return std::chrono::nanoseconds(((m_nativeMode.vsync_end - m_nativeMode.vsync_start) * m_nativeMode.htotal * 1'000'000ULL) / m_nativeMode.clock);
82static inline bool checkIfEqual(
const drmModeModeInfo *one,
const drmModeModeInfo *two)
84 return std::memcmp(one, two,
sizeof(drmModeModeInfo)) == 0;
89 return checkIfEqual(&m_nativeMode, &otherMode.m_nativeMode);
94 return checkIfEqual(&m_nativeMode, &otherMode);
98 :
DrmObject(gpu, connectorId, DRM_MODE_OBJECT_CONNECTOR)
99 , crtcId(this, QByteArrayLiteral(
"CRTC_ID"))
100 , nonDesktop(this, QByteArrayLiteral(
"non-desktop"))
101 , dpms(this, QByteArrayLiteral(
"DPMS"))
102 , edidProp(this, QByteArrayLiteral(
"EDID"))
103 , overscan(this, QByteArrayLiteral(
"overscan"))
104 , vrrCapable(this, QByteArrayLiteral(
"vrr_capable"))
105 , underscan(this, QByteArrayLiteral(
"underscan"), {
106 QByteArrayLiteral(
"off"),
107 QByteArrayLiteral(
"on"),
108 QByteArrayLiteral(
"auto"),
110 , underscanVBorder(
this, QByteArrayLiteral(
"underscan vborder"))
111 , underscanHBorder(
this, QByteArrayLiteral(
"underscan hborder"))
112 , broadcastRGB(
this, QByteArrayLiteral(
"Broadcast RGB"), {
113 QByteArrayLiteral(
"Automatic"),
114 QByteArrayLiteral(
"Full"),
115 QByteArrayLiteral(
"Limited 16:235"),
117 , maxBpc(
this, QByteArrayLiteral(
"max bpc"))
118 , linkStatus(
this, QByteArrayLiteral(
"link-status"), {
119 QByteArrayLiteral(
"Good"),
120 QByteArrayLiteral(
"Bad"),
122 , contentType(
this, QByteArrayLiteral(
"content type"), {
123 QByteArrayLiteral(
"No Data"),
124 QByteArrayLiteral(
"Graphics"),
125 QByteArrayLiteral(
"Photo"),
126 QByteArrayLiteral(
"Cinema"),
127 QByteArrayLiteral(
"Game"),
129 , panelOrientation(
this, QByteArrayLiteral(
"panel orientation"), {
130 QByteArrayLiteral(
"Normal"),
131 QByteArrayLiteral(
"Upside Down"),
132 QByteArrayLiteral(
"Left Side Up"),
133 QByteArrayLiteral(
"Right Side Up"),
135 , hdrMetadata(
this, QByteArrayLiteral(
"HDR_OUTPUT_METADATA"))
136 , scalingMode(
this, QByteArrayLiteral(
"scaling mode"), {
137 QByteArrayLiteral(
"None"),
138 QByteArrayLiteral(
"Full"),
139 QByteArrayLiteral(
"Center"),
140 QByteArrayLiteral(
"Full aspect"),
142 , colorspace(
this, QByteArrayLiteral(
"Colorspace"), {
143 QByteArrayLiteral(
"Default"),
144 QByteArrayLiteral(
"BT709_YCC"),
145 QByteArrayLiteral(
"opRGB"),
146 QByteArrayLiteral(
"BT2020_RGB"),
147 QByteArrayLiteral(
"BT2020_YCC"),
149 , path(
this, QByteArrayLiteral(
"PATH"))
151 , m_pipeline(m_conn ? std::make_unique<DrmPipeline>(
this) : nullptr)
154 for (
int i = 0; i < m_conn->count_encoders; ++i) {
157 qCWarning(KWIN_DRM) <<
"failed to get encoder" << m_conn->encoders[i];
160 m_possibleCrtcs |= enc->possible_crtcs;
163 qCWarning(KWIN_DRM) <<
"drmModeGetConnector failed!" << strerror(errno);
169 return !m_driverModes.empty() && m_conn && m_conn->connection == DRM_MODE_CONNECTED;
174 const char *
connectorName = drmModeGetConnectorTypeName(m_conn->connector_type);
178 return QStringLiteral(
"%1-%2").arg(
connectorName).arg(m_conn->connector_type_id);
192 return m_conn->connector_type == DRM_MODE_CONNECTOR_LVDS || m_conn->connector_type == DRM_MODE_CONNECTOR_eDP
193 || m_conn->connector_type == DRM_MODE_CONNECTOR_DSI;
198 return m_physicalSize;
213 const auto it = std::find_if(m_modes.constBegin(), m_modes.constEnd(), [&modeInfo](
const auto &mode) {
214 return checkIfEqual(mode->nativeMode(), &modeInfo);
216 return it == m_modes.constEnd() ? nullptr : *it;
221 switch (m_conn->subpixel) {
222 case DRM_MODE_SUBPIXEL_UNKNOWN:
224 case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
226 case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
228 case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
230 case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
232 case DRM_MODE_SUBPIXEL_NONE:
242 m_conn.reset(connector);
243 }
else if (!m_conn) {
274 qCWarning(KWIN_DRM) <<
"Couldn't parse EDID for connector" <<
this;
276 }
else if (m_conn->connection == DRM_MODE_CONNECTED) {
277 qCDebug(KWIN_DRM) <<
"Could not find edid for connector" <<
this;
282 m_physicalSize = QSize(m_conn->mmWidth, m_conn->mmHeight);
288 bool equal = m_conn->count_modes == m_driverModes.count();
289 for (
int i = 0; equal && i < m_conn->count_modes; i++) {
290 equal &= checkIfEqual(m_driverModes[i]->nativeMode(), &m_conn->modes[i]);
292 if (!equal && m_conn->count_modes > 0) {
294 m_driverModes.clear();
295 for (
int i = 0; i < m_conn->count_modes; i++) {
296 m_driverModes.append(std::make_shared<DrmConnectorMode>(
this, m_conn->modes[i], OutputMode::Flags()));
299 m_modes.append(m_driverModes);
301 m_modes.append(generateCommonModes());
303 if (m_pipeline->mode()) {
304 if (
const auto mode =
findMode(*m_pipeline->mode()->nativeMode())) {
305 m_pipeline->setMode(mode);
307 m_pipeline->setMode(m_modes.constFirst());
310 m_pipeline->setMode(m_modes.constFirst());
312 m_pipeline->applyPendingChanges();
313 if (m_pipeline->output()) {
314 m_pipeline->output()->updateModes();
320 QByteArray value = QByteArray(
static_cast<const char *
>(blob->data), blob->length);
321 if (value.startsWith(
"mst:")) {
324 const ssize_t firstHyphen = value.indexOf(
'-');
325 if (firstHyphen > 0) {
326 m_mstPath = value.mid(firstHyphen);
328 qCWarning(KWIN_DRM) <<
"Unexpected format in path property:" << value;
331 qCWarning(KWIN_DRM) <<
"Unknown path type detected:" << value;
340 return (m_possibleCrtcs & (1 << crtc->
pipeIndex()));
355 return m_pipeline.get();
363static const QList<QSize> s_commonModes = {
384QList<std::shared_ptr<DrmConnectorMode>> DrmConnector::generateCommonModes()
386 QList<std::shared_ptr<DrmConnectorMode>> ret;
388 uint32_t maxSizeRefreshRate = 0;
389 for (
const auto &mode : std::as_const(m_driverModes)) {
390 if (mode->size().width() >= maxSize.width() && mode->size().height() >= maxSize.height() && mode->refreshRate() >= maxSizeRefreshRate) {
391 maxSize = mode->size();
392 maxSizeRefreshRate = mode->refreshRate();
395 const uint64_t maxBandwidthEstimation = maxSize.width() * maxSize.height() * uint64_t(maxSizeRefreshRate);
396 for (
const auto &size : s_commonModes) {
397 const uint64_t bandwidthEstimation = size.width() * size.height() * 60000ull;
398 if (size.width() > maxSize.width() || size.height() > maxSize.height() || bandwidthEstimation > maxBandwidthEstimation) {
401 const auto generatedMode = generateMode(size, 60);
402 if (std::any_of(m_driverModes.cbegin(), m_driverModes.cend(), [generatedMode](
const auto &mode) {
403 return mode->size() == generatedMode->size() && mode->refreshRate() == generatedMode->refreshRate();
407 ret << generatedMode;
412std::shared_ptr<DrmConnectorMode> DrmConnector::generateMode(
const QSize &size,
float refreshRate)
414 auto modeInfo = libxcvt_gen_mode_info(size.width(), size.height(), refreshRate,
false,
false);
416 drmModeModeInfo mode{
417 .clock = uint32_t(modeInfo->dot_clock),
418 .hdisplay = uint16_t(modeInfo->hdisplay),
419 .hsync_start = modeInfo->hsync_start,
420 .hsync_end = modeInfo->hsync_end,
421 .htotal = modeInfo->htotal,
422 .vdisplay = uint16_t(modeInfo->vdisplay),
423 .vsync_start = modeInfo->vsync_start,
424 .vsync_end = modeInfo->vsync_end,
425 .vtotal = modeInfo->vtotal,
427 .vrefresh = uint32_t(modeInfo->vrefresh),
428 .flags = modeInfo->mode_flags,
429 .type = DRM_MODE_TYPE_USERDEF,
432 sprintf(mode.name,
"%dx%d@%d", size.width(), size.height(), mode.vrefresh);
440 QDebugStateSaver saver(s);
443 QString connState = QStringLiteral(
"Disconnected");
444 if (!obj->m_conn || obj->m_conn->connection == DRM_MODE_UNKNOWNCONNECTION) {
445 connState = QStringLiteral(
"Unknown Connection");
446 }
else if (obj->m_conn->connection == DRM_MODE_CONNECTED) {
447 connState = QStringLiteral(
"Connected");
450 s.nospace() <<
"DrmConnector(id=" << obj->
id() <<
", gpu=" << obj->
gpu() <<
", name=" << obj->
modelName() <<
", connection=" << connState <<
", countMode=" << (obj->m_conn ? obj->m_conn->count_modes : 0)
453 s <<
"DrmConnector(0x0)";
476 switch (orientation) {
void addProperty(const DrmProperty &prop, uint64_t value)
static std::shared_ptr< DrmBlob > create(DrmGpu *gpu, const void *data, uint32_t dataSize)
static DrmContentType kwinToDrmContentType(ContentType type)
DrmEnumProperty< BroadcastRgbOptions > broadcastRGB
DrmProperty underscanHBorder
bool isNonDesktop() const
Output::SubPixel subpixel() const
DrmConnector(DrmGpu *gpu, uint32_t connectorId)
static BroadcastRgbOptions rgbRangeToBroadcastRgb(Output::RgbRange rgbRange)
DrmEnumProperty< DrmContentType > contentType
DrmEnumProperty< LinkStatus > linkStatus
static Output::RgbRange broadcastRgbToRgbRange(BroadcastRgbOptions rgbRange)
std::shared_ptr< DrmConnectorMode > findMode(const drmModeModeInfo &modeInfo) const
QByteArray mstPath() const
DrmEnumProperty< ScalingMode > scalingMode
void disable(DrmAtomicCommit *commit) override
QString connectorName() const
bool isCrtcSupported(DrmCrtc *crtc) const
bool updateProperties() override
QSize physicalSize() const
DrmPipeline * pipeline() const
const Edid * edid() const
static OutputTransform toKWinTransform(PanelOrientation orientation)
QString modelName() const
DrmEnumProperty< Colorspace > colorspace
QList< std::shared_ptr< DrmConnectorMode > > modes() const
DrmEnumProperty< PanelOrientation > panelOrientation
DrmProperty underscanVBorder
DrmEnumProperty< UnderscanOptions > underscan
std::shared_ptr< DrmBlob > blob()
bool operator==(const DrmConnectorMode &otherMode)
DrmConnectorMode(DrmConnector *connector, drmModeModeInfo nativeMode, Flags additionalFlags)
drmModeModeInfo * nativeMode()
std::chrono::nanoseconds vblankTime() const
bool hasEnum(Enum value) const
DrmPropertyList queryProperties() const
void update(DrmPropertyList &propertyList)
drmModePropertyBlobRes * immutableBlob() const
QSize physicalSize() const
QByteArray serialNumber() const
QString nameString() const
drmModeConnectorPtr drmModeGetConnector(int fd, uint32_t connectorId)
drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id)
QDebug & operator<<(QDebug &s, const KWin::DrmConnector *obj)