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)