1#include "libmythbase/mythconfig.h"
8#include <QGuiApplication>
10#if CONFIG_QTPRIVATEHEADERS
11#include <qpa/qplatformnativeinterface.h>
28#include <drm_fourcc.h>
31#define LOC (QString("%1: ").arg(m_deviceName))
118#if CONFIG_QTPRIVATEHEADERS
119MythDRMPtr MythDRMDevice::FindDevice(
bool NeedPlanes)
129 if (!s_mythDRMDevice.isEmpty())
131 LOG(VB_GENERAL, LOG_INFO, QString(
"Forcing '%1' as DRM device").arg(s_mythDRMDevice));
134 devices.append(s_mythDRMDevice);
137 for (
const auto & dev : std::as_const(devices))
138 if (
auto device =
MythDRMDevice::Create(
nullptr, root + dev, NeedPlanes); device && device->Authenticated())
147 if (CmdLine.
toBool(
"vrr"))
153 auto platform = CmdLine.
toString(
"platform");
154 if (platform.isEmpty())
155 platform = qEnvironmentVariable(
"QT_QPA_PLATFORM");
156 if (!platform.contains(
"eglfs", Qt::CaseInsensitive))
159 LOG(VB_GENERAL, LOG_INFO,
"'eglfs' not explicitly requested. Not configuring DRM.");
164 static const char * s_kmsPlaneIndex =
"QT_QPA_EGLFS_KMS_PLANE_INDEX";
165 static const char * s_kmsPlaneCRTCS =
"QT_QPA_EGLFS_KMS_PLANES_FOR_CRTCS";
166 static const char * s_kmsPlaneZpos =
"QT_QPA_EGLFS_KMS_ZPOS";
167 static const char * s_kmsConfigFile =
"QT_QPA_EGLFS_KMS_CONFIG";
168 static const char * s_kmsAtomic =
"QT_QPA_EGLFS_KMS_ATOMIC";
169 static const char * s_kmsSetMode =
"QT_QPA_EGLFS_ALWAYS_SET_MODE";
176 LOG(VB_GENERAL, LOG_INFO, QString(
"Exporting '%1=1'").arg(s_kmsAtomic));
177 setenv(s_kmsAtomic,
"1", 0);
180 LOG(VB_GENERAL, LOG_INFO, QString(
"Exporting '%1=1'").arg(s_kmsSetMode));
181 setenv(s_kmsSetMode,
"1", 0);
183 bool plane = qEnvironmentVariableIsSet(s_kmsPlaneIndex) ||
184 qEnvironmentVariableIsSet(s_kmsPlaneCRTCS);
185 bool config = qEnvironmentVariableIsSet(s_kmsConfigFile);
186 bool zpos = qEnvironmentVariableIsSet(s_kmsPlaneZpos);
187 bool custom = plane || config || zpos;
192 LOG(VB_GENERAL, LOG_INFO,
"QT_QPA_EGLFS_KMS user overrides detected");
198 LOG(VB_GENERAL, LOG_WARNING,
"Qt eglfs_kms custom plane settings detected"
199 " but planar support not requested.");
204 s_planarRequested =
true;
209 LOG(VB_GENERAL, LOG_WARNING, QString(
"%1 not detected - assuming not required")
210 .arg(s_kmsPlaneZpos));
214 if (!(plane && config))
216 LOG(VB_GENERAL, LOG_WARNING,
"Warning: DRM planar support requested but "
217 "it looks like not all environment variables have been set.");
218 LOG(VB_GENERAL, LOG_INFO,
219 QString(
"Minimum required: %1 and/or %2 for plane index and %3 for alpha blending")
220 .arg(s_kmsPlaneIndex, s_kmsPlaneCRTCS, s_kmsConfigFile));
224 LOG(VB_GENERAL, LOG_INFO,
"DRM planar support enabled for custom user settings");
232 LOG(VB_GENERAL, LOG_INFO,
"Qt eglfs_kms planar video not requested");
239 LOG(VB_GENERAL, LOG_WARNING,
"Failed to open any suitable DRM devices with privileges");
243 if (!(device->m_guiPlane.get() && device->m_guiPlane->m_id &&
244 device->m_videoPlane.get() && device->m_videoPlane->m_id))
246 LOG(VB_GENERAL, LOG_WARNING, QString(
"Failed to deduce correct planes for device '%1'")
247 .arg(drmGetDeviceNameFromFd2(device->GetFD())));
252 auto guiplane = device->m_guiPlane;
256 LOG(VB_GENERAL, LOG_WARNING,
"Failed to find alpha format for GUI. Quitting DRM setup.");
261 QString
confdir = qEnvironmentVariable(
"MYTHCONFDIR");
263 confdir = QDir::homePath() +
"/.mythtv";
267 if (!
file.open(QIODevice::WriteOnly))
269 LOG(VB_GENERAL, LOG_WARNING, QString(
"Failed to open '%1' for writing. Quitting DRM setup.")
274 static const QString s_json =
276 " \"device\": \"%1\",\n"
277 " \"outputs\": [ { \"name\": \"%2\", \"format\": \"%3\", \"mode\": \"%4\" } ]\n"
281 QString wrote = s_json.arg(drmGetDeviceNameFromFd2(device->GetFD()),
283 s_mythDRMVideoMode.isEmpty() ?
"current" : s_mythDRMVideoMode);
285 if (
file.write(qPrintable(wrote)))
287 LOG(VB_GENERAL, LOG_INFO, QString(
"Wrote %1:\r\n%2").arg(
filename, wrote));
288 LOG(VB_GENERAL, LOG_INFO, QString(
"Exporting '%1=%2'").arg(s_kmsConfigFile,
filename));
293 auto planeindex = QString::number(guiplane->m_index);
294 auto crtcplane = QString(
"%1,%2").arg(device->m_crtc->m_id).arg(guiplane->m_id);
295 LOG(VB_GENERAL, LOG_INFO, QString(
"Exporting '%1=%2'").arg(s_kmsPlaneIndex, planeindex));
296 LOG(VB_GENERAL, LOG_INFO, QString(
"Exporting '%1=%2'").arg(s_kmsPlaneCRTCS, crtcplane));
297 setenv(s_kmsPlaneIndex, qPrintable(planeindex), 1);
298 setenv(s_kmsPlaneCRTCS, qPrintable(crtcplane), 1);
305 auto val = QString::number(std::min(range->m_min + 1, range->m_max));
306 LOG(VB_GENERAL, LOG_INFO, QString(
"Exporting '%1=%2'").arg(s_kmsPlaneZpos, val));
307 setenv(s_kmsPlaneZpos, qPrintable(val), 1);
312 s_planarRequested =
true;
320 [[maybe_unused]]
bool NeedPlanes)
322#if CONFIG_QTPRIVATEHEADERS
323 auto * app =
dynamic_cast<QGuiApplication *
>(QCoreApplication::instance());
324 if (qScreen && app && QGuiApplication::platformName().contains(
"eglfs", Qt::CaseInsensitive))
328 uint32_t connector = 0;
329 bool useatomic =
false;
330 auto * pni = QGuiApplication::platformNativeInterface();
331 if (
auto * drifd = pni->nativeResourceForIntegration(
"dri_fd"); drifd)
332 fd =
static_cast<int>(
reinterpret_cast<qintptr
>(drifd));
333 if (
auto * crtcid = pni->nativeResourceForScreen(
"dri_crtcid", qScreen); crtcid)
334 crtc =
static_cast<uint32_t
>(
reinterpret_cast<qintptr
>(crtcid));
335 if (
auto * connid = pni->nativeResourceForScreen(
"dri_connectorid", qScreen); connid)
336 connector =
static_cast<uint32_t
>(
reinterpret_cast<qintptr
>(connid));
337 if (
auto * atomic = pni->nativeResourceForIntegration(
"dri_atomic_request"); atomic)
338 if (
auto * request =
reinterpret_cast<drmModeAtomicReq*
>(atomic); request !=
nullptr)
341 LOG(VB_GENERAL, LOG_INFO, QString(
"%1 Qt EGLFS/KMS Fd:%2 Crtc id:%3 Connector id:%4 Atomic: %5")
342 .arg(drmGetDeviceNameFromFd2(fd)).arg(fd).arg(crtc).arg(connector).arg(useatomic));
345 if (fd && crtc && connector)
347 if (
auto result = std::shared_ptr<MythDRMDevice>(
new MythDRMDevice(fd, crtc, connector, useatomic));
348 result.get() && result->m_valid)
359 result.get() && result->m_valid)
367#if CONFIG_QTPRIVATEHEADERS
368 if (
auto result = std::shared_ptr<MythDRMDevice>(
new MythDRMDevice(
Device, NeedPlanes)); result && result->m_valid)
377 const QString root(QString(DRM_DIR_NAME) +
"/");
379 QStringList namefilters;
381 namefilters.append(
"drm*");
383 namefilters.append(
"card*");
385 return { root, dir.entryList(namefilters, QDir::Files | QDir::System) };
397 m_verbose(
Device.isEmpty() ? LOG_INFO : LOG_DEBUG)
421#if CONFIG_QTPRIVATEHEADERS
431 : m_openedDevice(
false),
472 if (s_planarRequested)
475 if (m_videoPlane.get() && m_guiPlane.get() && m_videoPlane->m_id && m_guiPlane->m_id)
476 s_planarSetup =
true;
478 LOG(VB_GENERAL, LOG_INFO,
LOC +
"DRM device retrieved from Qt");
482 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Device setup failed");
485 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Multi-plane setup: Requested: %1 Setup: %2")
486 .arg(s_planarRequested).arg(s_planarSetup));
504 m_valid = drmSetClientCap(
m_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) == 0;
507 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to request universal planes");
516 if (!s_mythDRMConnector.isEmpty())
524 if (connector->m_state == DRM_MODE_CONNECTED)
534 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to find connector");
549 m_valid = m_videoPlane.get() && m_guiPlane.get();
659 auto index =
static_cast<size_t>(ModeIndex);
661 if (ModeIndex < 0 || index >=
m_connector->m_modes.size())
665#if CONFIG_QTPRIVATEHEADERS
668 if (crtcid.get() && modeid.get())
673 if (drmModeCreatePropertyBlob(
m_fd, &
m_connector->m_modes[index],
sizeof(drmModeModeInfo), &blobid) == 0)
676 {
m_crtc->m_id, modeid->m_id, blobid }} );
682 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create mode blob");
700 int ret = drmSetMaster(
m_fd);
705 drm_magic_t magic = 0;
711 const auto * extra =
m_atomic ?
"" :
" but atomic operations required for mode switching";
716 LOG(VB_GENERAL,
m_verbose,
LOC +
"Not authenticated - mode switching not available");
740 LOG(VB_GENERAL,
m_verbose,
LOC +
"Will use first suitable connected device");
749 if (connector->m_state == DRM_MODE_CONNECTED)
751 if (serial.isEmpty())
766 LOG(VB_GENERAL, LOG_DEBUG,
LOC + QString(
"Matched connector with serial '%1'")
770 static_cast<int>(connector->m_mmHeight));
787 LOG(VB_GENERAL, LOG_DEBUG,
LOC + QString(
"Ignoring disconnected connector %1")
788 .arg(connector->m_name));
793 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"No connected connectors");
811 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Failed to find crtc for encoder");
819 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Initialised");
829 if (devices.isEmpty())
833 if (devices.size() == 1)
834 return root + devices.first();
837 auto serial =
m_screen->serialNumber();
838 if (serial.isEmpty())
844 for (
const auto& dev : std::as_const(devices))
846 QString device = root + dev;
862 int fd = open(
Device.toLocal8Bit().constData(), O_RDWR);
865 drmVersionPtr
version = drmGetVersion(fd);
885#if CONFIG_QTPRIVATEHEADERS
886void MythDRMDevice::MainWindowReady()
918bool MythDRMDevice::QueueAtomics(
const MythAtomics& Atomics)
const
920 auto * app =
dynamic_cast<QGuiApplication *
>(QCoreApplication::instance());
924 auto * pni = QGuiApplication::platformNativeInterface();
925 if (
auto * dri = pni->nativeResourceForIntegration(
"dri_atomic_request"); dri)
927 if (
auto * request =
reinterpret_cast<drmModeAtomicReq*
>(dri); request !=
nullptr)
929 for (
const auto & a : Atomics)
930 drmModeAtomicAddProperty(request, std::get<0>(a), std::get<1>(a), std::get<2>(a));
937void MythDRMDevice::DisableVideoPlane()
939 if (m_videoPlane.get())
941 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Disabling video plane");
942 QueueAtomics( {{ m_videoPlane->m_id, m_videoPlane->m_fbIdProp->m_id, 0 },
943 { m_videoPlane->m_id, m_videoPlane->m_crtcIdProp->m_id, 0 }} );
947DRMPlane MythDRMDevice::GetVideoPlane()
const
952DRMPlane MythDRMDevice::GetGUIPlane()
const
976void MythDRMDevice::AnalysePlanes()
985 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Found %1 planes; %2 for this CRTC")
986 .arg(allplanes.size()).arg(
m_planes.size()));
997 if (plane->m_type == DRM_PLANE_TYPE_PRIMARY)
999 if (!plane->m_videoFormats.empty())
1000 primaryVideo.emplace_back(plane);
1002 primaryGUI.emplace_back(plane);
1004 else if (plane->m_type == DRM_PLANE_TYPE_OVERLAY)
1006 if (!plane->m_videoFormats.empty())
1007 overlayVideo.emplace_back(plane);
1009 overlayGUI.emplace_back(plane);
1013 LOG(VB_PLAYBACK, LOG_INFO,
LOC + plane->Description());
1017 if (primaryGUI.empty() && overlayGUI.empty())
1021 if (primaryVideo.empty() && overlayVideo.empty())
1023 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Found no planes with video support");
1028 auto nodupe = [](
const auto & Planes,
const auto & Plane)
1030 for (
const auto & plane : Planes)
1031 if (plane->m_id != Plane->m_id)
1040 if (primaryVideo.empty())
1042 m_videoPlane = overlayVideo.front();
1043 if (overlayGUI.empty())
1044 m_guiPlane = primaryGUI.front();
1046 m_guiPlane = nodupe(overlayGUI, m_videoPlane);
1050 m_videoPlane = primaryVideo.front();
1051 if (overlayGUI.empty())
1052 m_guiPlane = nodupe(primaryGUI, m_videoPlane);
1054 m_guiPlane = overlayGUI.front();
1057 if (!m_videoPlane.get())
1059 LOG(VB_GENERAL, LOG_ERR,
LOC +
"No video plane");
1063 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Selected Plane #%1 %2 for video")
1065 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Supported DRM video formats: %1")
1069 if (!m_guiPlane.get())
1071 LOG(VB_GENERAL, LOG_ERR,
LOC +
"No GUI plane");
1075 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Selected Plane #%1 %2 for GUI")
A device containing images (ie. USB stick, CD, storage group etc)
Parent class for defining application command line parsers.
bool toBool(const QString &key) const
Returns stored QVariant as a boolean.
QString toString(const QString &key) const
Returns stored QVariant as a QString, falling to default if not provided.
uint toUInt(const QString &key) const
Returns stored QVariant as an unsigned integer, falling to default if not provided.
static DRMConns GetConnectors(int FD)
static DRMConn GetConnectorByName(const DRMConns &Connectors, const QString &Name)
static DRMConn GetConnector(const DRMConns &Connectors, uint32_t Id)
static DRMCrtcs GetCrtcs(int FD)
static DRMCrtc GetCrtc(const DRMCrtcs &Crtcs, uint32_t Id)
static MythDRMPtr Create(QScreen *qScreen, const QString &Device=QString(), bool NeedPlanes=true)
Create a MythDRMDevice instance.
QSize GetPhysicalSize() const
double m_adjustedRefreshRate
MythDRMDevice(QScreen *qScreen, const QString &Device=QString())
Constructor used when we have no DRM handles from Qt.
QScreen * GetScreen() const
static std::tuple< QString, QStringList > GetDeviceList()
static bool ConfirmDevice(const QString &Device)
bool Authenticated() const
QString GetSerialNumber() const
void Authenticate()
Attempt to acquire privileged DRM access.
bool CanSwitchModes() const
double GetRefreshRate() const
Return the refresh rate we think is in use.
bool SwitchMode(int ModeIndex)
Set the required video mode.
QSize GetResolution() const
DRMConn GetConnector() const
const DRMModes & GetModes() const
static DRMEncs GetEncoders(int FD)
static DRMEnc GetEncoder(const DRMEncs &Encoders, uint32_t Id)
static QString FormatsToString(const FOURCCVec &Formats)
static DRMPlanes GetPlanes(int FD, int CRTCFilter=-1)
static bool HasOverlayFormat(const FOURCCVec &Formats)
Enusure list of supplied formats contains a format that is suitable for OpenGL/Vulkan.
static QString FormatToString(uint32_t Format)
static QString PlaneTypeToString(uint64_t Type)
static uint32_t GetAlphaFormat(const FOURCCVec &Formats)
static DRMProp GetProperty(const QString &Name, const DRMProps &Properties)
static void ForceFreeSync(const MythDRMPtr &Device, bool Enable)
Force FreeSync on or off before the main app is started.
QStringList SerialNumbers() const
std::shared_ptr< class MythDRMConnector > DRMConn
std::shared_ptr< class MythDRMCrtc > DRMCrtc
static constexpr const char * DRM_QUIET
std::shared_ptr< class MythDRMDevice > MythDRMPtr
std::vector< MythAtomic > MythAtomics
std::vector< DRMMode > DRMModes
#define DRM_FORMAT_INVALID
std::vector< DRMPlane > DRMPlanes
std::shared_ptr< class MythDRMPlane > DRMPlane
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
VERBOSE_PREAMBLE Most true