6 #include <QGuiApplication>
8 #ifdef USING_QTPRIVATEHEADERS
9 #include <qpa/qplatformnativeinterface.h>
25 #include <drm_fourcc.h>
28 #define LOC (QString("%1: ").arg(m_deviceName))
115 #ifdef USING_QTPRIVATEHEADERS
116 MythDRMPtr MythDRMDevice::FindDevice(
bool NeedPlanes)
126 if (!s_mythDRMDevice.isEmpty())
128 LOG(VB_GENERAL, LOG_INFO, QString(
"Forcing '%1' as DRM device").arg(s_mythDRMDevice));
131 devices.append(s_mythDRMDevice);
134 for (
const auto & dev : qAsConst(devices))
135 if (
auto device =
MythDRMDevice::Create(
nullptr, root + dev, NeedPlanes); device && device->Authenticated())
144 if (CmdLine.
toBool(
"vrr"))
150 auto platform = CmdLine.
toString(
"platform");
151 if (platform.isEmpty())
152 platform = qEnvironmentVariable(
"QT_QPA_PLATFORM");
153 if (!platform.contains(
"eglfs", Qt::CaseInsensitive))
156 LOG(VB_GENERAL, LOG_INFO,
"'eglfs' not explicitly requested. Not configuring DRM.");
161 static const char * s_kmsPlaneIndex =
"QT_QPA_EGLFS_KMS_PLANE_INDEX";
162 static const char * s_kmsPlaneCRTCS =
"QT_QPA_EGLFS_KMS_PLANES_FOR_CRTCS";
163 static const char * s_kmsPlaneZpos =
"QT_QPA_EGLFS_KMS_ZPOS";
164 static const char * s_kmsConfigFile =
"QT_QPA_EGLFS_KMS_CONFIG";
165 static const char * s_kmsAtomic =
"QT_QPA_EGLFS_KMS_ATOMIC";
166 static const char * s_kmsSetMode =
"QT_QPA_EGLFS_ALWAYS_SET_MODE";
173 LOG(VB_GENERAL, LOG_INFO, QString(
"Exporting '%1=1'").arg(s_kmsAtomic));
174 setenv(s_kmsAtomic,
"1", 0);
177 LOG(VB_GENERAL, LOG_INFO, QString(
"Exporting '%1=1'").arg(s_kmsSetMode));
178 setenv(s_kmsSetMode,
"1", 0);
180 bool plane = qEnvironmentVariableIsSet(s_kmsPlaneIndex) ||
181 qEnvironmentVariableIsSet(s_kmsPlaneCRTCS);
182 bool config = qEnvironmentVariableIsSet(s_kmsConfigFile);
183 bool zpos = qEnvironmentVariableIsSet(s_kmsPlaneZpos);
184 bool custom = plane || config || zpos;
189 LOG(VB_GENERAL, LOG_INFO,
"QT_QPA_EGLFS_KMS user overrides detected");
195 LOG(VB_GENERAL, LOG_WARNING,
"Qt eglfs_kms custom plane settings detected"
196 " but planar support not requested.");
201 s_planarRequested =
true;
206 LOG(VB_GENERAL, LOG_WARNING, QString(
"%1 not detected - assuming not required")
207 .arg(s_kmsPlaneZpos));
211 if (!(plane && config))
213 LOG(VB_GENERAL, LOG_WARNING,
"Warning: DRM planar support requested but "
214 "it looks like not all environment variables have been set.");
215 LOG(VB_GENERAL, LOG_INFO,
216 QString(
"Minimum required: %1 and/or %2 for plane index and %3 for alpha blending")
217 .arg(s_kmsPlaneIndex, s_kmsPlaneCRTCS, s_kmsConfigFile));
221 LOG(VB_GENERAL, LOG_INFO,
"DRM planar support enabled for custom user settings");
229 LOG(VB_GENERAL, LOG_INFO,
"Qt eglfs_kms planar video not requested");
236 LOG(VB_GENERAL, LOG_WARNING,
"Failed to open any suitable DRM devices with privileges");
240 if (!(device->m_guiPlane.get() && device->m_guiPlane->m_id &&
241 device->m_videoPlane.get() && device->m_videoPlane->m_id))
243 LOG(VB_GENERAL, LOG_WARNING, QString(
"Failed to deduce correct planes for device '%1'")
244 .arg(drmGetDeviceNameFromFd2(device->GetFD())));
249 auto guiplane = device->m_guiPlane;
253 LOG(VB_GENERAL, LOG_WARNING,
"Failed to find alpha format for GUI. Quitting DRM setup.");
258 QString
confdir = qEnvironmentVariable(
"MYTHCONFDIR");
260 confdir = QDir::homePath() +
"/.mythtv";
264 if (!
file.open(QIODevice::WriteOnly))
266 LOG(VB_GENERAL, LOG_WARNING, QString(
"Failed to open '%1' for writing. Quitting DRM setup.")
271 static const QString s_json =
273 " \"device\": \"%1\",\n"
274 " \"outputs\": [ { \"name\": \"%2\", \"format\": \"%3\", \"mode\": \"%4\" } ]\n"
278 QString wrote = s_json.arg(drmGetDeviceNameFromFd2(device->GetFD()),
280 s_mythDRMVideoMode.isEmpty() ?
"current" : s_mythDRMVideoMode);
282 if (
file.write(qPrintable(wrote)))
284 LOG(VB_GENERAL, LOG_INFO, QString(
"Wrote %1:\r\n%2").arg(
filename, wrote));
285 LOG(VB_GENERAL, LOG_INFO, QString(
"Exporting '%1=%2'").arg(s_kmsConfigFile,
filename));
290 auto planeindex = QString::number(guiplane->m_index);
291 auto crtcplane = QString(
"%1,%2").arg(device->m_crtc->m_id).arg(guiplane->m_id);
292 LOG(VB_GENERAL, LOG_INFO, QString(
"Exporting '%1=%2'").arg(s_kmsPlaneIndex, planeindex));
293 LOG(VB_GENERAL, LOG_INFO, QString(
"Exporting '%1=%2'").arg(s_kmsPlaneCRTCS, crtcplane));
294 setenv(s_kmsPlaneIndex, qPrintable(planeindex), 1);
295 setenv(s_kmsPlaneCRTCS, qPrintable(crtcplane), 1);
302 auto val = QString::number(std::min(range->m_min + 1, range->m_max));
303 LOG(VB_GENERAL, LOG_INFO, QString(
"Exporting '%1=%2'").arg(s_kmsPlaneZpos, val));
304 setenv(s_kmsPlaneZpos, qPrintable(val), 1);
309 s_planarRequested =
true;
317 [[maybe_unused]]
bool NeedPlanes)
319 #ifdef USING_QTPRIVATEHEADERS
320 auto * app =
dynamic_cast<QGuiApplication *
>(QCoreApplication::instance());
321 if (qScreen && app && QGuiApplication::platformName().contains(
"eglfs", Qt::CaseInsensitive))
325 uint32_t connector = 0;
326 bool useatomic =
false;
327 auto * pni = QGuiApplication::platformNativeInterface();
328 if (
auto * drifd = pni->nativeResourceForIntegration(
"dri_fd"); drifd)
329 fd =
static_cast<int>(
reinterpret_cast<qintptr
>(drifd));
330 if (
auto * crtcid = pni->nativeResourceForScreen(
"dri_crtcid", qScreen); crtcid)
331 crtc =
static_cast<uint32_t
>(
reinterpret_cast<qintptr
>(crtcid));
332 if (
auto * connid = pni->nativeResourceForScreen(
"dri_connectorid", qScreen); connid)
333 connector =
static_cast<uint32_t
>(
reinterpret_cast<qintptr
>(connid));
334 if (
auto * atomic = pni->nativeResourceForIntegration(
"dri_atomic_request"); atomic)
335 if (
auto * request =
reinterpret_cast<drmModeAtomicReq*
>(atomic); request !=
nullptr)
338 LOG(VB_GENERAL, LOG_INFO, QString(
"%1 Qt EGLFS/KMS Fd:%2 Crtc id:%3 Connector id:%4 Atomic: %5")
339 .arg(drmGetDeviceNameFromFd2(fd)).arg(fd).arg(crtc).arg(connector).arg(useatomic));
342 if (fd && crtc && connector)
344 if (
auto result = std::shared_ptr<MythDRMDevice>(
new MythDRMDevice(fd, crtc, connector, useatomic));
345 result.get() && result->m_valid)
356 result.get() && result->m_valid)
364 #ifdef USING_QTPRIVATEHEADERS
365 if (
auto result = std::shared_ptr<MythDRMDevice>(
new MythDRMDevice(
Device, NeedPlanes)); result && result->m_valid)
374 const QString root(QString(DRM_DIR_NAME) +
"/");
376 QStringList namefilters;
378 namefilters.append(
"drm*");
380 namefilters.append(
"card*");
382 return { root, dir.entryList(namefilters, QDir::Files | QDir::System) };
394 m_verbose(
Device.isEmpty() ? LOG_INFO : LOG_DEBUG)
418 #if defined (USING_QTPRIVATEHEADERS)
428 : m_openedDevice(
false),
469 if (s_planarRequested)
472 if (m_videoPlane.get() && m_guiPlane.get() && m_videoPlane->m_id && m_guiPlane->m_id)
473 s_planarSetup =
true;
475 LOG(VB_GENERAL, LOG_INFO,
LOC +
"DRM device retrieved from Qt");
479 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Device setup failed");
482 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Multi-plane setup: Requested: %1 Setup: %2")
483 .arg(s_planarRequested).arg(s_planarSetup));
501 m_valid = drmSetClientCap(
m_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) == 0;
504 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to request universal planes");
513 if (!s_mythDRMConnector.isEmpty())
521 if (connector->m_state == DRM_MODE_CONNECTED)
531 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to find connector");
546 m_valid = m_videoPlane.get() && m_guiPlane.get();
655 auto index =
static_cast<size_t>(ModeIndex);
657 if (ModeIndex < 0 || index >=
m_connector->m_modes.size())
661 #ifdef USING_QTPRIVATEHEADERS
664 if (crtcid.get() && modeid.get())
669 if (drmModeCreatePropertyBlob(
m_fd, &
m_connector->m_modes[index],
sizeof(drmModeModeInfo), &blobid) == 0)
672 {
m_crtc->m_id, modeid->m_id, blobid }} );
678 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create mode blob");
696 int ret = drmSetMaster(
m_fd);
701 drm_magic_t magic = 0;
707 const auto * extra =
m_atomic ?
"" :
" but atomic operations required for mode switching";
712 LOG(VB_GENERAL,
m_verbose,
LOC +
"Not authenticated - mode switching not available");
736 LOG(VB_GENERAL,
m_verbose,
LOC +
"Will use first suitable connected device");
745 if (connector->m_state == DRM_MODE_CONNECTED)
747 if (serial.isEmpty())
762 LOG(VB_GENERAL, LOG_DEBUG,
LOC + QString(
"Matched connector with serial '%1'")
766 static_cast<int>(connector->m_mmHeight));
783 LOG(VB_GENERAL, LOG_DEBUG,
LOC + QString(
"Ignoring disconnected connector %1")
784 .arg(connector->m_name));
789 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"No connected connectors");
807 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Failed to find crtc for encoder");
815 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Initialised");
825 if (devices.isEmpty())
829 if (devices.size() == 1)
830 return root + devices.first();
833 auto serial =
m_screen->serialNumber();
834 if (serial.isEmpty())
840 for (
const auto& dev : qAsConst(devices))
842 QString device = root + dev;
858 int fd = open(
Device.toLocal8Bit().constData(), O_RDWR);
861 drmVersionPtr
version = drmGetVersion(fd);
881 #if defined (USING_QTPRIVATEHEADERS)
882 void MythDRMDevice::MainWindowReady()
914 bool MythDRMDevice::QueueAtomics(
const MythAtomics& Atomics)
const
916 auto * app =
dynamic_cast<QGuiApplication *
>(QCoreApplication::instance());
920 auto * pni = QGuiApplication::platformNativeInterface();
921 if (
auto * dri = pni->nativeResourceForIntegration(
"dri_atomic_request"); dri)
923 if (
auto * request =
reinterpret_cast<drmModeAtomicReq*
>(dri); request !=
nullptr)
925 for (
const auto & a : Atomics)
926 drmModeAtomicAddProperty(request, std::get<0>(a), std::get<1>(a), std::get<2>(a));
933 void MythDRMDevice::DisableVideoPlane()
935 if (m_videoPlane.get())
937 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Disabling video plane");
938 QueueAtomics( {{ m_videoPlane->m_id, m_videoPlane->m_fbIdProp->m_id, 0 },
939 { m_videoPlane->m_id, m_videoPlane->m_crtcIdProp->m_id, 0 }} );
943 DRMPlane MythDRMDevice::GetVideoPlane()
const
948 DRMPlane MythDRMDevice::GetGUIPlane()
const
972 void MythDRMDevice::AnalysePlanes()
981 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Found %1 planes; %2 for this CRTC")
982 .arg(allplanes.size()).arg(
m_planes.size()));
991 if (plane->m_type == DRM_PLANE_TYPE_PRIMARY)
993 if (!plane->m_videoFormats.empty())
994 primaryVideo.emplace_back(plane);
996 primaryGUI.emplace_back(plane);
998 else if (plane->m_type == DRM_PLANE_TYPE_OVERLAY)
1000 if (!plane->m_videoFormats.empty())
1001 overlayVideo.emplace_back(plane);
1003 overlayGUI.emplace_back(plane);
1007 LOG(VB_PLAYBACK, LOG_INFO,
LOC + plane->Description());
1011 if (primaryGUI.empty() && overlayGUI.empty())
1015 if (primaryVideo.empty() && overlayVideo.empty())
1017 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Found no planes with video support");
1022 auto nodupe = [](
const auto & Planes,
const auto & Plane)
1024 for (
const auto & plane : Planes)
1025 if (plane->m_id != Plane->m_id)
1034 if (primaryVideo.empty())
1036 m_videoPlane = overlayVideo.front();
1037 if (overlayGUI.empty())
1038 m_guiPlane = primaryGUI.front();
1040 m_guiPlane = nodupe(overlayGUI, m_videoPlane);
1044 m_videoPlane = primaryVideo.front();
1045 if (overlayGUI.empty())
1046 m_guiPlane = nodupe(primaryGUI, m_videoPlane);
1048 m_guiPlane = overlayGUI.front();
1051 if (!m_videoPlane.get())
1053 LOG(VB_GENERAL, LOG_ERR,
LOC +
"No video plane");
1057 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Selected Plane #%1 %2 for video")
1059 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Supported DRM video formats: %1")
1063 if (!m_guiPlane.get())
1065 LOG(VB_GENERAL, LOG_ERR,
LOC +
"No GUI plane");
1069 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Selected Plane #%1 %2 for GUI")