2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
11 #include "settings/Settings.h"
12 #include "settings/SettingsComponent.h"
13 #include "utils/DRMHelpers.h"
14 #include "utils/StringUtils.h"
15 #include "utils/log.h"
16 #include "windowing/GraphicContext.h"
18 #include "PlatformDefs.h"
20 using namespace KODI::WINDOWING::GBM
;
24 const std::string SETTING_VIDEOSCREEN_LIMITGUISIZE
= "videoscreen.limitguisize";
26 void DrmFbDestroyCallback(gbm_bo
* bo
, void* data
)
28 drm_fb
* fb
= static_cast<drm_fb
*>(data
);
32 CLog::Log(LOGDEBUG
, "CDRMUtils::{} - removing framebuffer: {}", __FUNCTION__
, fb
->fb_id
);
33 int drm_fd
= gbm_device_get_fd(gbm_bo_get_device(bo
));
34 drmModeRmFB(drm_fd
, fb
->fb_id
);
41 CDRMUtils::~CDRMUtils()
46 bool CDRMUtils::SetMode(const RESOLUTION_INFO
& res
)
48 if (!m_connector
->CheckConnector())
51 m_mode
= m_connector
->GetModeForIndex(std::atoi(res
.strId
.c_str()));
53 m_height
= res
.iHeight
;
55 CLog::Log(LOGDEBUG
, "CDRMUtils::{} - found crtc mode: {}x{}{} @ {} Hz", __FUNCTION__
,
56 m_mode
->hdisplay
, m_mode
->vdisplay
, m_mode
->flags
& DRM_MODE_FLAG_INTERLACE
? "i" : "",
62 drm_fb
* CDRMUtils::DrmFbGetFromBo(struct gbm_bo
*bo
)
65 struct drm_fb
*fb
= static_cast<drm_fb
*>(gbm_bo_get_user_data(bo
));
68 if (m_gui_plane
->GetFormat() == fb
->format
)
71 DrmFbDestroyCallback(bo
, gbm_bo_get_user_data(bo
));
75 struct drm_fb
*fb
= new drm_fb
;
77 fb
->format
= m_gui_plane
->GetFormat();
85 uint64_t modifiers
[4] = {0};
87 width
= gbm_bo_get_width(bo
);
88 height
= gbm_bo_get_height(bo
);
90 #if defined(HAS_GBM_MODIFIERS)
91 for (int i
= 0; i
< gbm_bo_get_plane_count(bo
); i
++)
93 handles
[i
] = gbm_bo_get_handle_for_plane(bo
, i
).u32
;
94 strides
[i
] = gbm_bo_get_stride_for_plane(bo
, i
);
95 offsets
[i
] = gbm_bo_get_offset(bo
, i
);
96 modifiers
[i
] = gbm_bo_get_modifier(bo
);
99 handles
[0] = gbm_bo_get_handle(bo
).u32
;
100 strides
[0] = gbm_bo_get_stride(bo
);
101 memset(offsets
, 0, 16);
106 if (modifiers
[0] && modifiers
[0] != DRM_FORMAT_MOD_INVALID
)
108 flags
|= DRM_MODE_FB_MODIFIERS
;
109 CLog::Log(LOGDEBUG
, "CDRMUtils::{} - using modifier: {}", __FUNCTION__
,
110 DRMHELPERS::ModifierToString(modifiers
[0]));
113 int ret
= drmModeAddFB2WithModifiers(m_fd
,
126 ret
= drmModeAddFB2(m_fd
,
139 CLog::Log(LOGDEBUG
, "CDRMUtils::{} - failed to add framebuffer: {} ({})", __FUNCTION__
, strerror(errno
), errno
);
144 gbm_bo_set_user_data(bo
, fb
, DrmFbDestroyCallback
);
149 bool CDRMUtils::FindPreferredMode()
154 for (int i
= 0, area
= 0; i
< m_connector
->GetModesCount(); i
++)
156 drmModeModeInfo
* current_mode
= m_connector
->GetModeForIndex(i
);
158 if(current_mode
->type
& DRM_MODE_TYPE_PREFERRED
)
160 m_mode
= current_mode
;
161 CLog::Log(LOGDEBUG
, "CDRMUtils::{} - found preferred mode: {}x{}{} @ {} Hz", __FUNCTION__
,
162 m_mode
->hdisplay
, m_mode
->vdisplay
,
163 m_mode
->flags
& DRM_MODE_FLAG_INTERLACE
? "i" : "", m_mode
->vrefresh
);
167 auto current_area
= current_mode
->hdisplay
* current_mode
->vdisplay
;
168 if (current_area
> area
)
170 m_mode
= current_mode
;
177 CLog::Log(LOGDEBUG
, "CDRMUtils::{} - failed to find preferred mode", __FUNCTION__
);
184 bool CDRMUtils::FindPlanes()
186 for (size_t i
= 0; i
< m_crtcs
.size(); i
++)
188 if (!(m_encoder
->GetPossibleCrtcs() & (1 << i
)))
191 auto videoPlane
= std::find_if(m_planes
.begin(), m_planes
.end(), [&i
](auto& plane
) {
192 if (plane
->GetPossibleCrtcs() & (1 << i
))
194 return plane
->SupportsFormat(DRM_FORMAT_NV12
);
199 uint32_t videoPlaneId
{0};
201 if (videoPlane
!= m_planes
.end())
202 videoPlaneId
= videoPlane
->get()->GetPlaneId();
205 std::find_if(m_planes
.begin(), m_planes
.end(), [&i
, &videoPlaneId
](auto& plane
) {
206 if (plane
->GetPossibleCrtcs() & (1 << i
))
208 return (plane
->GetPlaneId() != videoPlaneId
&&
209 (videoPlaneId
== 0 || plane
->SupportsFormat(DRM_FORMAT_ARGB8888
)) &&
210 (plane
->SupportsFormat(DRM_FORMAT_XRGB2101010
) ||
211 plane
->SupportsFormat(DRM_FORMAT_XRGB8888
)));
216 if (videoPlane
!= m_planes
.end() && guiPlane
!= m_planes
.end())
218 m_crtc
= m_crtcs
[i
].get();
219 m_video_plane
= videoPlane
->get();
220 m_gui_plane
= guiPlane
->get();
224 if (guiPlane
!= m_planes
.end())
226 if (!m_crtc
&& m_encoder
->GetCrtcId() == m_crtcs
[i
]->GetCrtcId())
228 m_crtc
= m_crtcs
[i
].get();
229 m_gui_plane
= guiPlane
->get();
230 m_video_plane
= nullptr;
235 CLog::Log(LOGINFO
, "CDRMUtils::{} - using crtc: {}", __FUNCTION__
, m_crtc
->GetCrtcId());
237 // video plane may not be available
239 CLog::Log(LOGDEBUG
, "CDRMUtils::{} - using video plane {}", __FUNCTION__
,
240 m_video_plane
->GetPlaneId());
242 if (m_gui_plane
->SupportsFormat(DRM_FORMAT_XRGB2101010
))
244 m_gui_plane
->SetFormat(DRM_FORMAT_XRGB2101010
);
245 CLog::Log(LOGDEBUG
, "CDRMUtils::{} - using 10bit gui plane {}", __FUNCTION__
,
246 m_gui_plane
->GetPlaneId());
250 m_gui_plane
->SetFormat(DRM_FORMAT_XRGB8888
);
251 CLog::Log(LOGDEBUG
, "CDRMUtils::{} - using gui plane {}", __FUNCTION__
,
252 m_gui_plane
->GetPlaneId());
258 void CDRMUtils::PrintDrmDeviceInfo(drmDevicePtr device
)
263 message
.append(fmt::format("CDRMUtils::{} - DRM Device Info:", __FUNCTION__
));
264 message
.append(fmt::format("\n available_nodes: {:#04x}", device
->available_nodes
));
265 message
.append("\n nodes:");
267 for (int i
= 0; i
< DRM_NODE_MAX
; i
++)
269 if (device
->available_nodes
& 1 << i
)
270 message
.append(fmt::format("\n nodes[{}]: {}", i
, device
->nodes
[i
]));
273 message
.append(fmt::format("\n bustype: {:#04x}", device
->bustype
));
275 if (device
->bustype
== DRM_BUS_PCI
)
277 message
.append("\n pci:");
278 message
.append(fmt::format("\n domain: {:#04x}", device
->businfo
.pci
->domain
));
279 message
.append(fmt::format("\n bus: {:#02x}", device
->businfo
.pci
->bus
));
280 message
.append(fmt::format("\n dev: {:#02x}", device
->businfo
.pci
->dev
));
281 message
.append(fmt::format("\n func: {:#1}", device
->businfo
.pci
->func
));
283 message
.append("\n deviceinfo:");
284 message
.append("\n pci:");
285 message
.append(fmt::format("\n vendor_id: {:#04x}", device
->deviceinfo
.pci
->vendor_id
));
286 message
.append(fmt::format("\n device_id: {:#04x}", device
->deviceinfo
.pci
->device_id
));
287 message
.append(fmt::format("\n subvendor_id: {:#04x}", device
->deviceinfo
.pci
->subvendor_id
));
288 message
.append(fmt::format("\n subdevice_id: {:#04x}", device
->deviceinfo
.pci
->subdevice_id
));
290 else if (device
->bustype
== DRM_BUS_USB
)
292 message
.append("\n usb:");
293 message
.append(fmt::format("\n bus: {:#03x}", device
->businfo
.usb
->bus
));
294 message
.append(fmt::format("\n dev: {:#03x}", device
->businfo
.usb
->dev
));
296 message
.append("\n deviceinfo:");
297 message
.append("\n usb:");
298 message
.append(fmt::format("\n vendor: {:#04x}", device
->deviceinfo
.usb
->vendor
));
299 message
.append(fmt::format("\n product: {:#04x}", device
->deviceinfo
.usb
->product
));
301 else if (device
->bustype
== DRM_BUS_PLATFORM
)
303 message
.append("\n platform:");
304 message
.append(fmt::format("\n fullname: {}", device
->businfo
.platform
->fullname
));
306 else if (device
->bustype
== DRM_BUS_HOST1X
)
308 message
.append("\n host1x:");
309 message
.append(fmt::format("\n fullname: {}", device
->businfo
.host1x
->fullname
));
312 message
.append("\n unhandled bus type");
315 CLog::Log(LOGDEBUG
, "{}", message
);
318 bool CDRMUtils::OpenDrm(bool needConnector
)
320 int numDevices
= drmGetDevices2(0, nullptr, 0);
323 CLog::Log(LOGERROR
, "CDRMUtils::{} - no drm devices found: ({})", __FUNCTION__
,
328 CLog::Log(LOGDEBUG
, "CDRMUtils::{} - drm devices found: {}", __FUNCTION__
, numDevices
);
330 std::vector
<drmDevicePtr
> devices(numDevices
);
332 int ret
= drmGetDevices2(0, devices
.data(), devices
.size());
335 CLog::Log(LOGERROR
, "CDRMUtils::{} - drmGetDevices2 return an error: ({})", __FUNCTION__
,
340 for (const auto device
: devices
)
342 if (!(device
->available_nodes
& 1 << DRM_NODE_PRIMARY
))
346 m_fd
= open(device
->nodes
[DRM_NODE_PRIMARY
], O_RDWR
| O_CLOEXEC
);
352 auto resources
= drmModeGetResources(m_fd
);
356 m_connectors
.clear();
357 for (int i
= 0; i
< resources
->count_connectors
; i
++)
358 m_connectors
.emplace_back(std::make_unique
<CDRMConnector
>(m_fd
, resources
->connectors
[i
]));
360 drmModeFreeResources(resources
);
362 if (!FindConnector())
366 CLog::Log(LOGDEBUG
, "CDRMUtils::{} - opened device: {}", __FUNCTION__
,
367 device
->nodes
[DRM_NODE_PRIMARY
]);
369 PrintDrmDeviceInfo(device
);
371 const char* renderPath
= drmGetRenderDeviceNameFromFd(m_fd
);
374 renderPath
= drmGetDeviceNameFromFd2(m_fd
);
377 renderPath
= drmGetDeviceNameFromFd(m_fd
);
381 m_renderDevicePath
= renderPath
;
382 m_renderFd
= open(renderPath
, O_RDWR
| O_CLOEXEC
);
384 CLog::Log(LOGDEBUG
, "CDRMUtils::{} - opened render node: {}", __FUNCTION__
, renderPath
);
387 drmFreeDevices(devices
.data(), devices
.size());
391 drmFreeDevices(devices
.data(), devices
.size());
395 bool CDRMUtils::InitDrm()
400 /* caps need to be set before allocating connectors, encoders, crtcs, and planes */
401 int ret
= drmSetClientCap(m_fd
, DRM_CLIENT_CAP_UNIVERSAL_PLANES
, 1);
404 CLog::Log(LOGERROR
, "CDRMUtils::{} - failed to set universal planes capability: {}",
405 __FUNCTION__
, strerror(errno
));
409 ret
= drmSetClientCap(m_fd
, DRM_CLIENT_CAP_STEREO_3D
, 1);
412 CLog::Log(LOGERROR
, "CDRMUtils::{} - failed to set stereo 3d capability: {}", __FUNCTION__
,
417 #if defined(DRM_CLIENT_CAP_ASPECT_RATIO)
418 ret
= drmSetClientCap(m_fd
, DRM_CLIENT_CAP_ASPECT_RATIO
, 0);
420 CLog::Log(LOGERROR
, "CDRMUtils::{} - aspect ratio capability is not supported: {}",
421 __FUNCTION__
, strerror(errno
));
424 auto resources
= drmModeGetResources(m_fd
);
427 CLog::Log(LOGERROR
, "CDRMUtils::{} - failed to get drm resources: {}", __FUNCTION__
, strerror(errno
));
431 m_connectors
.clear();
432 for (int i
= 0; i
< resources
->count_connectors
; i
++)
433 m_connectors
.emplace_back(std::make_unique
<CDRMConnector
>(m_fd
, resources
->connectors
[i
]));
436 for (int i
= 0; i
< resources
->count_encoders
; i
++)
437 m_encoders
.emplace_back(std::make_unique
<CDRMEncoder
>(m_fd
, resources
->encoders
[i
]));
440 for (int i
= 0; i
< resources
->count_crtcs
; i
++)
441 m_crtcs
.emplace_back(std::make_unique
<CDRMCrtc
>(m_fd
, resources
->crtcs
[i
]));
443 drmModeFreeResources(resources
);
445 auto planeResources
= drmModeGetPlaneResources(m_fd
);
448 CLog::Log(LOGERROR
, "CDRMUtils::{} - failed to get drm plane resources: {}", __FUNCTION__
, strerror(errno
));
453 for (uint32_t i
= 0; i
< planeResources
->count_planes
; i
++)
455 m_planes
.emplace_back(std::make_unique
<CDRMPlane
>(m_fd
, planeResources
->planes
[i
]));
456 m_planes
[i
]->FindModifiers();
459 drmModeFreePlaneResources(planeResources
);
461 if (!FindConnector())
473 if (!FindPreferredMode())
476 ret
= drmSetMaster(m_fd
);
480 "CDRMUtils::{} - failed to set drm master, will try to authorize instead: {}",
481 __FUNCTION__
, strerror(errno
));
485 ret
= drmGetMagic(m_fd
, &magic
);
488 CLog::Log(LOGERROR
, "CDRMUtils::{} - failed to get drm magic: {}", __FUNCTION__
,
493 ret
= drmAuthMagic(m_fd
, magic
);
496 CLog::Log(LOGERROR
, "CDRMUtils::{} - failed to authorize drm magic: {}", __FUNCTION__
,
501 CLog::Log(LOGINFO
, "CDRMUtils::{} - successfully authorized drm magic", __FUNCTION__
);
507 bool CDRMUtils::FindConnector()
509 auto settingsComponent
= CServiceBroker::GetSettingsComponent();
510 if (!settingsComponent
)
513 auto settings
= settingsComponent
->GetSettings();
517 std::vector
<std::unique_ptr
<CDRMConnector
>>::iterator connector
;
519 std::string connectorName
= settings
->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR
);
520 if (connectorName
!= "Default")
522 connector
= std::find_if(m_connectors
.begin(), m_connectors
.end(),
523 [&connectorName
](auto& connector
)
525 return connector
->GetEncoderId() > 0 && connector
->IsConnected() &&
526 connector
->GetName() == connectorName
;
529 if (connector
== m_connectors
.end())
531 CLog::Log(LOGDEBUG
, "CDRMUtils::{} - failed to find specified connector: {}, trying default",
532 __FUNCTION__
, connectorName
);
533 connectorName
= "Default";
537 if (connectorName
== "Default")
539 connector
= std::find_if(m_connectors
.begin(), m_connectors
.end(),
541 { return connector
->GetEncoderId() > 0 && connector
->IsConnected(); });
544 if (connector
== m_connectors
.end())
546 CLog::Log(LOGDEBUG
, "CDRMUtils::{} - failed to find connected connector", __FUNCTION__
);
550 CLog::Log(LOGINFO
, "CDRMUtils::{} - using connector: {}", __FUNCTION__
,
551 connector
->get()->GetName());
553 m_connector
= connector
->get();
557 bool CDRMUtils::FindEncoder()
559 auto encoder
= std::find_if(m_encoders
.begin(), m_encoders
.end(), [this](auto& encoder
) {
560 return encoder
->GetEncoderId() == m_connector
->GetEncoderId();
563 if (encoder
== m_encoders
.end())
565 CLog::Log(LOGDEBUG
, "CDRMUtils::{} - failed to find encoder for connector id: {}", __FUNCTION__
,
566 *m_connector
->GetConnectorId());
570 CLog::Log(LOGINFO
, "CDRMUtils::{} - using encoder: {}", __FUNCTION__
,
571 encoder
->get()->GetEncoderId());
573 m_encoder
= encoder
->get();
577 bool CDRMUtils::FindCrtc()
579 for (size_t i
= 0; i
< m_crtcs
.size(); i
++)
581 if (m_encoder
->GetPossibleCrtcs() & (1 << i
))
583 if (m_crtcs
[i
]->GetCrtcId() == m_encoder
->GetCrtcId())
585 m_orig_crtc
= m_crtcs
[i
].get();
586 if (m_orig_crtc
->GetModeValid())
588 m_mode
= m_orig_crtc
->GetMode();
589 CLog::Log(LOGDEBUG
, "CDRMUtils::{} - original crtc mode: {}x{}{} @ {} Hz", __FUNCTION__
,
590 m_mode
->hdisplay
, m_mode
->vdisplay
,
591 m_mode
->flags
& DRM_MODE_FLAG_INTERLACE
? "i" : "", m_mode
->vrefresh
);
601 bool CDRMUtils::RestoreOriginalMode()
608 auto ret
= drmModeSetCrtc(m_fd
, m_orig_crtc
->GetCrtcId(), m_orig_crtc
->GetBufferId(),
609 m_orig_crtc
->GetX(), m_orig_crtc
->GetY(), m_connector
->GetConnectorId(),
610 1, m_orig_crtc
->GetMode());
614 CLog::Log(LOGERROR
, "CDRMUtils::{} - failed to set original crtc mode", __FUNCTION__
);
618 CLog::Log(LOGDEBUG
, "CDRMUtils::{} - set original crtc mode", __FUNCTION__
);
623 void CDRMUtils::DestroyDrm()
625 RestoreOriginalMode();
627 if (drmAuthMagic(m_fd
, 0) == EINVAL
)
633 m_connector
= nullptr;
636 m_orig_crtc
= nullptr;
637 m_video_plane
= nullptr;
638 m_gui_plane
= nullptr;
641 RESOLUTION_INFO
CDRMUtils::GetResolutionInfo(drmModeModeInfoPtr mode
)
644 res
.iScreenWidth
= mode
->hdisplay
;
645 res
.iScreenHeight
= mode
->vdisplay
;
646 res
.iWidth
= res
.iScreenWidth
;
647 res
.iHeight
= res
.iScreenHeight
;
649 int limit
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
650 SETTING_VIDEOSCREEN_LIMITGUISIZE
);
651 if (limit
> 0 && res
.iScreenWidth
> 1920 && res
.iScreenHeight
> 1080)
659 case 2: // 1080p / 720p (>30hz)
660 res
.iWidth
= mode
->vrefresh
> 30 ? 1280 : 1920;
661 res
.iHeight
= mode
->vrefresh
> 30 ? 720 : 1080;
667 case 4: // Unlimited / 1080p (>30hz)
668 res
.iWidth
= mode
->vrefresh
> 30 ? 1920 : res
.iScreenWidth
;
669 res
.iHeight
= mode
->vrefresh
> 30 ? 1080 : res
.iScreenHeight
;
674 if (mode
->clock
% 5 != 0)
675 res
.fRefreshRate
= static_cast<float>(mode
->vrefresh
) * (1000.0f
/1001.0f
);
677 res
.fRefreshRate
= mode
->vrefresh
;
678 res
.iSubtitles
= res
.iHeight
;
679 res
.fPixelRatio
= 1.0f
;
680 res
.bFullScreen
= true;
682 if (mode
->flags
& DRM_MODE_FLAG_3D_MASK
)
684 if (mode
->flags
& DRM_MODE_FLAG_3D_TOP_AND_BOTTOM
)
685 res
.dwFlags
= D3DPRESENTFLAG_MODE3DTB
;
686 else if (mode
->flags
& DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF
)
687 res
.dwFlags
= D3DPRESENTFLAG_MODE3DSBS
;
689 else if (mode
->flags
& DRM_MODE_FLAG_INTERLACE
)
690 res
.dwFlags
= D3DPRESENTFLAG_INTERLACED
;
692 res
.dwFlags
= D3DPRESENTFLAG_PROGRESSIVE
;
695 StringUtils::Format("{}x{}{} @ {:.6f} Hz", res
.iScreenWidth
, res
.iScreenHeight
,
696 res
.dwFlags
& D3DPRESENTFLAG_INTERLACED
? "i" : "", res
.fRefreshRate
);
700 RESOLUTION_INFO
CDRMUtils::GetCurrentMode()
702 return GetResolutionInfo(m_mode
);
705 std::vector
<RESOLUTION_INFO
> CDRMUtils::GetModes()
707 std::vector
<RESOLUTION_INFO
> resolutions
;
708 resolutions
.reserve(m_connector
->GetModesCount());
710 for (auto i
= 0; i
< m_connector
->GetModesCount(); i
++)
712 RESOLUTION_INFO res
= GetResolutionInfo(m_connector
->GetModeForIndex(i
));
713 res
.strId
= std::to_string(i
);
714 resolutions
.push_back(res
);
720 std::vector
<std::string
> CDRMUtils::GetConnectedConnectorNames()
722 std::vector
<std::string
> connectorNames
;
723 for (const auto& connector
: m_connectors
)
725 if (connector
->IsConnected())
726 connectorNames
.emplace_back(connector
->GetName());
729 return connectorNames
;
732 uint32_t CDRMUtils::FourCCWithAlpha(uint32_t fourcc
)
734 return (fourcc
& 0xFFFFFF00) | static_cast<uint32_t>('A');
737 uint32_t CDRMUtils::FourCCWithoutAlpha(uint32_t fourcc
)
739 return (fourcc
& 0xFFFFFF00) | static_cast<uint32_t>('X');