31 changes: 31 additions & 0 deletions mythtv/libs/libmythui/platforms/drm/mythdrmframebuffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef MYTHDRMFRAMEBUFFER_H
#define MYTHDRMFRAMEBUFFER_H

// MythTV
#include "platforms/drm/mythdrmresources.h"

using DRMFb = std::shared_ptr<class MythDRMFramebuffer>;
using DRMFbs = std::vector<DRMFb>;

class MythDRMFramebuffer
{
public:
DRMFb Create(int FD, uint32_t Id);

uint32_t m_id { 0 };
uint32_t m_width { 0 };
uint32_t m_height { 0 };
uint32_t m_format { 0 };
uint64_t m_modifiers { 0 };
DRMArray m_handles { 0 };
DRMArray m_pitches { 0 };
DRMArray m_offsets { 0 };

protected:
MythDRMFramebuffer(int FD, uint32_t Id);

private:
Q_DISABLE_COPY(MythDRMFramebuffer)
};

#endif
29 changes: 29 additions & 0 deletions mythtv/libs/libmythui/platforms/drm/mythdrmmode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// MythTV
#include "platforms/drm/mythdrmmode.h"

/*! \class MythDRMMode
* \brief A simple object representing a DRM video mode.
*/
DRMMode MythDRMMode::Create(drmModeModeInfoPtr Mode, int Index)
{
if (auto mode = std::shared_ptr<MythDRMMode>(new MythDRMMode(Mode, Index)); mode && mode->m_rate > 1.0)
return mode;
return nullptr;
}

MythDRMMode::MythDRMMode(drmModeModeInfoPtr Mode, int Index)
{
if (Mode)
{
m_index = Index;
m_rate = (Mode->clock * 1000.0) / (Mode->htotal * Mode->vtotal);
m_width = Mode->hdisplay;
m_height = Mode->vdisplay;
m_flags = Mode->flags;
m_name = Mode->name;
if (Mode->flags & DRM_MODE_FLAG_INTERLACE)
m_rate *= 2.0;
if (Mode->flags & DRM_MODE_FLAG_DBLSCAN)
m_rate /= 2.0;
}
}
29 changes: 29 additions & 0 deletions mythtv/libs/libmythui/platforms/drm/mythdrmmode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#ifndef MYTHDRMMODE_H
#define MYTHDRMMODE_H

// MythTV
#include "platforms/drm/mythdrmresources.h"

using DRMMode = std::shared_ptr<class MythDRMMode>;
using DRMModes = std::vector<DRMMode>;

class MUI_PUBLIC MythDRMMode
{
public:
static DRMMode Create(drmModeModeInfoPtr Mode, int Index);

int m_index { 0 };
double m_rate { 0.0 };
uint16_t m_width { 0 };
uint16_t m_height { 0 };
uint32_t m_flags { 0 };
QString m_name;

protected:
explicit MythDRMMode(drmModeModeInfoPtr Mode, int Index);

private:
Q_DISABLE_COPY(MythDRMMode)
};

#endif
210 changes: 210 additions & 0 deletions mythtv/libs/libmythui/platforms/drm/mythdrmplane.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
// MythTV
#include "mythlogging.h"
#include "platforms/drm/mythdrmplane.h"

// libdrm
extern "C" {
#include <drm_fourcc.h>
}

#define LOC QString("DRMPlane: ")

/*! \class MythDRMPlane
* \brief A wrapper around a DRM plane object.
*
* The full list of available planes can be retrieved with GetPlanes.
*/
QString MythDRMPlane::PlaneTypeToString(uint64_t Type)
{
if (Type == DRM_PLANE_TYPE_OVERLAY) return "Overlay";
if (Type == DRM_PLANE_TYPE_CURSOR) return "Cursor";
if (Type == DRM_PLANE_TYPE_PRIMARY) return "Primary";
return "Unknown";
}

DRMPlane MythDRMPlane::Create(int FD, uint32_t Id, uint32_t Index)
{
if (FD && Id)
if (auto plane = std::shared_ptr<MythDRMPlane>(new MythDRMPlane(FD, Id, Index)); plane && plane->m_id)
return plane;

return nullptr;
}

MythDRMPlane::MythDRMPlane(int FD, uint32_t Id, uint32_t Index)
{
if (auto plane = drmModeGetPlane(FD, Id); plane)
{
auto id = plane->plane_id;
m_index = Index;
m_id = id;
m_fbId = plane->fb_id;
m_crtcId = plane->crtc_id;
m_possibleCrtcs = plane->possible_crtcs;

for (uint32_t j = 0; j < plane->count_formats; ++j)
{
m_formats.emplace_back(plane->formats[j]);
if (FormatIsVideo(plane->formats[j]))
m_videoFormats.emplace_back(plane->formats[j]);
}

m_properties = MythDRMProperty::GetProperties(FD, m_id, DRM_MODE_OBJECT_PLANE);

// Add generic properties
if (auto type = MythDRMProperty::GetProperty("type", m_properties); type)
if (auto enumt = dynamic_cast<MythDRMEnumProperty*>(type.get()); enumt)
m_type = enumt->m_value;

// Add video specific properties
if (!m_videoFormats.empty())
{
m_fbIdProp = MythDRMProperty::GetProperty("FB_ID", m_properties);
m_crtcIdProp = MythDRMProperty::GetProperty("CRTC_ID", m_properties);
m_srcXProp = MythDRMProperty::GetProperty("SRC_X", m_properties);
m_srcYProp = MythDRMProperty::GetProperty("SRC_Y", m_properties);
m_srcWProp = MythDRMProperty::GetProperty("SRC_W", m_properties);
m_srcHProp = MythDRMProperty::GetProperty("SRC_H", m_properties);
m_crtcXProp = MythDRMProperty::GetProperty("CRTC_X", m_properties);
m_crtcYProp = MythDRMProperty::GetProperty("CRTC_Y", m_properties);
m_crtcWProp = MythDRMProperty::GetProperty("CRTC_W", m_properties);
m_crtcHProp = MythDRMProperty::GetProperty("CRTC_H", m_properties);
}

drmModeFreePlane(plane);
}
}

DRMPlanes MythDRMPlane::GetPlanes(int FD, int CRTCFilter)
{
DRMPlanes result;
auto planes = drmModeGetPlaneResources(FD);
if (!planes)
{
LOG(VB_GENERAL, LOG_ERR, QString(drmGetDeviceNameFromFd2(FD)) + ": Failed to retrieve planes");
return result;
}

for (uint32_t index = 0; index < planes->count_planes; ++index)
{
if (auto plane = MythDRMPlane::Create(FD, planes->planes[index], index); plane)
{
if ((CRTCFilter > -1) && !(plane->m_possibleCrtcs & (1 << CRTCFilter)))
continue;
result.emplace_back(plane);
}
}

drmModeFreePlaneResources(planes);
return result;
}

DRMPlanes MythDRMPlane::FilterPrimaryPlanes(const DRMPlanes &Planes)
{
DRMPlanes result;
for (const auto & plane : Planes)
if ((plane->m_type == DRM_PLANE_TYPE_PRIMARY) && !plane->m_videoFormats.empty())
result.emplace_back(plane);

return result;
}

DRMPlanes MythDRMPlane::FilterOverlayPlanes(const DRMPlanes &Planes)
{
DRMPlanes result;
for (const auto & plane : Planes)
if ((plane->m_type == DRM_PLANE_TYPE_OVERLAY) && HasOverlayFormat(plane->m_formats))
result.emplace_back(plane);

return result;
}

QString MythDRMPlane::FormatToString(uint32_t Format)
{
switch (Format)
{
// Note: These match the string literals in QKmsDevice config parsing (but
// must be used lower case in the KMS config file)
case DRM_FORMAT_RGB565: return "RGB565";
case DRM_FORMAT_XRGB8888: return "XRGB8888";
case DRM_FORMAT_XBGR8888: return "XBGR8888";
case DRM_FORMAT_ARGB8888: return "ARGB8888";
case DRM_FORMAT_RGBA8888: return "RGBA8888";
case DRM_FORMAT_ABGR8888: return "ABGR8888";
case DRM_FORMAT_BGRA8888: return "BGRA8888";
case DRM_FORMAT_XRGB2101010: return "XRGB2101010";
case DRM_FORMAT_XBGR2101010: return "XBGR2101010";
case DRM_FORMAT_ARGB2101010: return "ARGB2101010";
case DRM_FORMAT_ABGR2101010: return "ABGR2101010";
// Not supported by Qt as overlay formats
case DRM_FORMAT_XRGB16161616F: return "XRGB16F";
case DRM_FORMAT_XBGR16161616F: return "XBGR16F";
case DRM_FORMAT_ARGB16161616F: return "ARGB16F";
case DRM_FORMAT_ABGR16161616F: return "ABGR16F";
default: break;
}
return QString::asprintf("%c%c%c%c", Format, Format >> 8, Format >> 16, Format >> 24);
}

QString MythDRMPlane::FormatsToString(FOURCCVec Formats)
{
QStringList formats;
for (auto format : Formats)
formats.append(FormatToString(format));
return formats.join(",");
}

bool MythDRMPlane::FormatIsVideo(uint32_t Format)
{
static const FOURCCVec s_yuvFormats =
{
// Bi-planar
DRM_FORMAT_NV12, DRM_FORMAT_NV21, DRM_FORMAT_NV16, DRM_FORMAT_NV61,
DRM_FORMAT_NV24, DRM_FORMAT_NV42, DRM_FORMAT_P210, DRM_FORMAT_P010,
DRM_FORMAT_P012, DRM_FORMAT_P016, DRM_FORMAT_P030, DRM_FORMAT_NV15,
DRM_FORMAT_NV20,
// Tri-planar formats
DRM_FORMAT_YUV410, DRM_FORMAT_YVU410, DRM_FORMAT_YUV411, DRM_FORMAT_YVU411,
DRM_FORMAT_YUV420, DRM_FORMAT_YVU420, DRM_FORMAT_YUV422, DRM_FORMAT_YVU422,
DRM_FORMAT_YUV444, DRM_FORMAT_YVU444,
// Packed formats
DRM_FORMAT_YUYV, DRM_FORMAT_YVYU, DRM_FORMAT_UYVY, DRM_FORMAT_VYUY
};

return std::find(s_yuvFormats.cbegin(), s_yuvFormats.cend(), Format) != s_yuvFormats.cend();
}

/*! \brief Enusure list of supplied formats contains a format that is suitable for OpenGL/Vulkan.
*/
bool MythDRMPlane::HasOverlayFormat(FOURCCVec Formats)
{
// Formats as deemed suitable by QKmsDevice
static const FOURCCVec s_rgbFormats =
{
DRM_FORMAT_XRGB8888, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888,
DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888, DRM_FORMAT_RGB565,
DRM_FORMAT_BGR565, DRM_FORMAT_XRGB2101010, DRM_FORMAT_XBGR2101010,
DRM_FORMAT_ARGB2101010, DRM_FORMAT_ABGR2101010
};

for (auto format : Formats)
if (std::any_of(s_rgbFormats.cbegin(), s_rgbFormats.cend(), [&format](auto Format) { return Format == format; }))
return true;

return false;
}

uint32_t MythDRMPlane::GetAlphaFormat(FOURCCVec Formats)
{
// N.B. Prioritised list
static const FOURCCVec s_alphaFormats =
{
DRM_FORMAT_ARGB8888, DRM_FORMAT_ABGR8888, DRM_FORMAT_ARGB2101010, DRM_FORMAT_ABGR2101010
};

for (auto format : s_alphaFormats)
if (std::any_of(Formats.cbegin(), Formats.cend(), [&format](auto Format) { return Format == format; }))
return format;

return DRM_FORMAT_INVALID;
}
67 changes: 67 additions & 0 deletions mythtv/libs/libmythui/platforms/drm/mythdrmplane.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#ifndef MYTHDRMPLANE_H
#define MYTHDRMPLANE_H

// MythTV
#include "platforms/drm/mythdrmproperty.h"

#ifndef DRM_FORMAT_NV15
#define DRM_FORMAT_NV15 fourcc_code('N', 'V', '1', '5')
#endif

#ifndef DRM_FORMAT_NV20
#define DRM_FORMAT_NV20 fourcc_code('N', 'V', '2', '0')
#endif

#ifndef DRM_FORMAT_P030
#define DRM_FORMAT_P030 fourcc_code('P', '0', '3', '0')
#endif

using FOURCCVec = std::vector<uint32_t>;
using DRMPlane = std::shared_ptr<class MythDRMPlane>;
using DRMPlanes = std::vector<DRMPlane>;

class MUI_PUBLIC MythDRMPlane
{
public:
static DRMPlane Create(int FD, uint32_t Id, uint32_t Index);
static DRMPlanes GetPlanes(int FD, int CRTCFilter = -1);

static QString PlaneTypeToString (uint64_t Type);
static DRMPlanes FilterPrimaryPlanes (const DRMPlanes& Planes);
static DRMPlanes FilterOverlayPlanes (const DRMPlanes& Planes);
static QString FormatToString (uint32_t Format);
static QString FormatsToString (FOURCCVec Formats);
static bool FormatIsVideo (uint32_t Format);
static bool HasOverlayFormat (FOURCCVec Formats);
static uint32_t GetAlphaFormat (FOURCCVec Formats);

uint32_t m_id { 0 };
uint32_t m_index { 0 };
uint64_t m_type { DRM_PLANE_TYPE_PRIMARY };
uint32_t m_possibleCrtcs { 0 };
uint32_t m_fbId { 0 };
uint32_t m_crtcId { 0 };
DRMProps m_properties;

DRMProp m_fbIdProp { nullptr };
DRMProp m_crtcIdProp { nullptr };
DRMProp m_srcXProp { nullptr };
DRMProp m_srcYProp { nullptr };
DRMProp m_srcWProp { nullptr };
DRMProp m_srcHProp { nullptr };
DRMProp m_crtcXProp { nullptr };
DRMProp m_crtcYProp { nullptr };
DRMProp m_crtcWProp { nullptr };
DRMProp m_crtcHProp { nullptr };

FOURCCVec m_formats;
FOURCCVec m_videoFormats;

protected:
MythDRMPlane(int FD, uint32_t Id, uint32_t Index);

private:
Q_DISABLE_COPY(MythDRMPlane)
};

#endif
137 changes: 137 additions & 0 deletions mythtv/libs/libmythui/platforms/drm/mythdrmproperty.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// MythTV
#include "platforms/drm/mythdrmproperty.h"

// Qt
#include <QStringList>

/*! \class MythDRMProperty
* \brief A wrapper around a DRM object property.
*
* Retrieve a list of the properties available for an object with GetProperties
* and retrieve a single, named property from a given list with GetProperty.
*
* \note Property values represent the initial property state and will not reflect
* subsequent changes unless explicitly updated.
*/
MythDRMProperty::MythDRMProperty(Type mType, uint32_t Id, uint32_t Flags, const char * Name)
: m_type(mType),
m_id(Id),
m_readOnly(Flags & DRM_MODE_PROP_IMMUTABLE),
m_name(Name)
{
}

DRMProps MythDRMProperty::GetProperties(int FD, const uint32_t ObjectId, uint32_t ObjectType)
{
DRMProps result;
if (auto props = drmModeObjectGetProperties(FD, ObjectId, ObjectType); props)
{
for (uint32_t index = 0; index < props->count_props; ++index)
{
if (auto prop = drmModeGetProperty(FD, props->props[index]); prop)
{
auto key = QString(prop->name);
auto value = props->prop_values[index];
if (prop->flags & DRM_MODE_PROP_RANGE)
result.emplace_back(std::shared_ptr<MythDRMProperty>(new MythDRMRangeProperty(value, prop)));
else if (prop->flags & DRM_MODE_PROP_ENUM)
result.emplace_back(std::shared_ptr<MythDRMProperty>(new MythDRMEnumProperty(value, prop)));
else if (prop->flags & DRM_MODE_PROP_BITMASK)
result.emplace_back(std::shared_ptr<MythDRMProperty>(new MythDRMBitmaskProperty(value, prop)));
else if (prop->flags & DRM_MODE_PROP_BLOB)
result.emplace_back(std::shared_ptr<MythDRMProperty>(new MythDRMBlobProperty(FD, value, prop)));
else if (prop->flags & DRM_MODE_PROP_OBJECT)
result.emplace_back(std::shared_ptr<MythDRMProperty>(new MythDRMObjectProperty(prop)));
else if (prop->flags & DRM_MODE_PROP_SIGNED_RANGE)
result.emplace_back(std::shared_ptr<MythDRMProperty>(new MythDRMSignedRangeProperty(value, prop)));
drmModeFreeProperty(prop);
}
}
drmModeFreeObjectProperties(props);
}
return result;
}

DRMProp MythDRMProperty::GetProperty(const QString& Name, const DRMProps &Properties)
{
for (const auto & prop : Properties)
if (Name.compare(prop->m_name, Qt::CaseInsensitive) == 0)
return prop;

return nullptr;
}

MythDRMRangeProperty::MythDRMRangeProperty(uint64_t Value, drmModePropertyPtr Property)
: MythDRMProperty(Range, Property->prop_id, Property->flags, Property->name),
m_value(Value)
{
for (int i = 0; i < Property->count_values; ++i)
{
auto value = Property->values[i];
if (value < m_min)
m_min = value;
if (value > m_max)
m_max = value;
}
}

QString MythDRMRangeProperty::ToString()
{
return QString("%1 Value: %2 Range: %3<->%4").arg(m_name).arg(m_value).arg(m_min).arg(m_max);
}

MythDRMSignedRangeProperty::MythDRMSignedRangeProperty(uint64_t Value, drmModePropertyPtr Property)
: MythDRMProperty(SignedRange, Property->prop_id, Property->flags, Property->name),
m_value(static_cast<int64_t>(Value))
{
for (int i = 0; i < Property->count_values; ++i)
{
auto value = static_cast<int64_t>(Property->values[i]);
if (value < m_min)
m_min = value;
if (value > m_max)
m_max = value;
}
}

QString MythDRMSignedRangeProperty::ToString()
{
return QString("%1 Value: %2 Range: %3<->%4").arg(m_name).arg(m_value).arg(m_min).arg(m_max);
}

MythDRMEnumProperty::MythDRMEnumProperty(uint64_t Value, drmModePropertyPtr Property)
: MythDRMProperty(Enum, Property->prop_id, Property->flags, Property->name),
m_value(Value)
{
for (int i = 0; i < Property->count_enums; ++i)
m_enums.emplace(Property->enums[i].value, Property->enums[i].name);
}

QString MythDRMEnumProperty::ToString()
{
QStringList values;
for (const auto & value : m_enums)
values.append(QString("%1(%2)").arg(value.first).arg(value.second));
return QString("%1 Value: %2 Values: %3").arg(m_name).arg(m_value).arg(values.join(", "));
}

MythDRMBitmaskProperty::MythDRMBitmaskProperty(uint64_t Value, drmModePropertyPtr Property)
: MythDRMEnumProperty(Value, Property)
{
m_type = Bitmask;
}

MythDRMBlobProperty::MythDRMBlobProperty(int FD, uint64_t Value, drmModePropertyPtr Property)
: MythDRMProperty(Blob, Property->prop_id, Property->flags, Property->name)
{
if (auto blob = drmModeGetPropertyBlob(FD, static_cast<uint32_t>(Value)); blob)
{
m_blob = QByteArray(reinterpret_cast<const char *>(blob->data), static_cast<int>(blob->length));
drmModeFreePropertyBlob(blob);
}
}

MythDRMObjectProperty::MythDRMObjectProperty(drmModePropertyPtr Property)
: MythDRMProperty(Object, Property->prop_id, Property->flags, Property->name)
{
}
89 changes: 89 additions & 0 deletions mythtv/libs/libmythui/platforms/drm/mythdrmproperty.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#ifndef MYTHDRMPROPERTY_H
#define MYTHDRMPROPERTY_H

// MythTV
#include "platforms/drm/mythdrmresources.h"

using DRMProp = std::shared_ptr<class MythDRMProperty>;
using DRMProps = std::vector<DRMProp>;

class MUI_PUBLIC MythDRMProperty
{
public:
enum Type
{
Invalid = 0,
Range,
Enum,
Bitmask,
Blob,
Object,
SignedRange
};

static DRMProps GetProperties(int FD, uint32_t ObjectId, uint32_t ObjectType);
static DRMProp GetProperty (const QString& Name, const DRMProps& Properties);
virtual ~MythDRMProperty() = default;
virtual QString ToString() { return ""; }

Type m_type { Invalid };
uint32_t m_id { 0 };
bool m_readOnly { true };
QString m_name;

protected:
MythDRMProperty(Type mType, uint32_t Id, uint32_t Flags, const char * Name);

private:
Q_DISABLE_COPY(MythDRMProperty)
};

class MUI_PUBLIC MythDRMRangeProperty : public MythDRMProperty
{
public:
MythDRMRangeProperty(uint64_t Value, drmModePropertyPtr Property);
QString ToString() override;
uint64_t m_value { 0 };
uint64_t m_min { 0 };
uint64_t m_max { 0 };
};

class MUI_PUBLIC MythDRMSignedRangeProperty : public MythDRMProperty
{
public:
MythDRMSignedRangeProperty(uint64_t Value, drmModePropertyPtr Property);
QString ToString() override;
int64_t m_value { 0 };
int64_t m_min { 0 };
int64_t m_max { 0 };
};

class MUI_PUBLIC MythDRMEnumProperty : public MythDRMProperty
{
public:
MythDRMEnumProperty(uint64_t Value, drmModePropertyPtr Property);
QString ToString() override;
uint64_t m_value { 0 };
std::map<uint64_t,QString> m_enums;
};

class MUI_PUBLIC MythDRMBitmaskProperty : public MythDRMEnumProperty
{
public:
MythDRMBitmaskProperty(uint64_t Value, drmModePropertyPtr Property);
};

class MUI_PUBLIC MythDRMBlobProperty : public MythDRMProperty
{
public:
MythDRMBlobProperty(int FD, uint64_t Value, drmModePropertyPtr Property);
QByteArray m_blob;
};

class MUI_PUBLIC MythDRMObjectProperty : public MythDRMProperty
{
public:
MythDRMObjectProperty(drmModePropertyPtr Property);
};

#endif
28 changes: 28 additions & 0 deletions mythtv/libs/libmythui/platforms/drm/mythdrmresources.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// MythTV
#include "platforms/drm/mythdrmresources.h"

/*! \class MythDRMResources
* \brief A simple wrapper around a drmModeResPtr that ensures proper cleanup.
*/
MythDRMResources::MythDRMResources(int FD)
: m_resources(drmModeGetResources(FD))
{
}

MythDRMResources::~MythDRMResources()
{
if (m_resources)
drmModeFreeResources(m_resources);
}

drmModeResPtr MythDRMResources::operator->() const
{
return m_resources;
}

drmModeResPtr MythDRMResources::operator*() const
{
return m_resources;
}


35 changes: 35 additions & 0 deletions mythtv/libs/libmythui/platforms/drm/mythdrmresources.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#ifndef MYTHDRMRESOURCES_H
#define MYTHDRMRESOURCES_H

// Qt
#include <QString>

// MythTV
#include "mythuiexp.h"

// libdrm
extern "C" {
#include <xf86drm.h>
#include <xf86drmMode.h>
}

// Std
#include <map>
#include <vector>
#include <memory>

using DRMArray = std::array<uint32_t,4>;

class MUI_PUBLIC MythDRMResources
{
public:
MythDRMResources(int FD);
~MythDRMResources();
drmModeResPtr operator->() const;
drmModeResPtr operator*() const;

private:
drmModeResPtr m_resources { nullptr };
};

#endif