340 lines
11 KiB
C++
340 lines
11 KiB
C++
|
|
/*
|
||
|
|
* Copyright 2022 The Android Open Source Project
|
||
|
|
*
|
||
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
|
* you may not use this file except in compliance with the License.
|
||
|
|
* You may obtain a copy of the License at
|
||
|
|
*
|
||
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
*
|
||
|
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
|
* See the License for the specific language governing permissions and
|
||
|
|
* limitations under the License.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include "DrmClient.h"
|
||
|
|
|
||
|
|
#include <cros_gralloc_handle.h>
|
||
|
|
|
||
|
|
using ::android::base::guest::AutoReadLock;
|
||
|
|
using ::android::base::guest::AutoWriteLock;
|
||
|
|
using ::android::base::guest::ReadWriteLock;
|
||
|
|
|
||
|
|
namespace aidl::android::hardware::graphics::composer3::impl {
|
||
|
|
|
||
|
|
DrmClient::~DrmClient() {
|
||
|
|
if (mFd > 0) {
|
||
|
|
drmDropMaster(mFd.get());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
HWC3::Error DrmClient::init() {
|
||
|
|
DEBUG_LOG("%s", __FUNCTION__);
|
||
|
|
|
||
|
|
mFd = ::android::base::unique_fd(open("/dev/dri/card0", O_RDWR | O_CLOEXEC));
|
||
|
|
if (mFd < 0) {
|
||
|
|
ALOGE("%s: failed to open drm device: %s", __FUNCTION__, strerror(errno));
|
||
|
|
return HWC3::Error::NoResources;
|
||
|
|
}
|
||
|
|
|
||
|
|
int ret = drmSetClientCap(mFd.get(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
|
||
|
|
if (ret) {
|
||
|
|
ALOGE("%s: failed to set cap universal plane %s\n", __FUNCTION__, strerror(errno));
|
||
|
|
return HWC3::Error::NoResources;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = drmSetClientCap(mFd.get(), DRM_CLIENT_CAP_ATOMIC, 1);
|
||
|
|
if (ret) {
|
||
|
|
ALOGE("%s: failed to set cap atomic %s\n", __FUNCTION__, strerror(errno));
|
||
|
|
return HWC3::Error::NoResources;
|
||
|
|
}
|
||
|
|
|
||
|
|
drmSetMaster(mFd.get());
|
||
|
|
|
||
|
|
if (!drmIsMaster(mFd.get())) {
|
||
|
|
ALOGE("%s: failed to get master drm device", __FUNCTION__);
|
||
|
|
return HWC3::Error::NoResources;
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
AutoWriteLock lock(mDisplaysMutex);
|
||
|
|
bool success = loadDrmDisplays();
|
||
|
|
if (success) {
|
||
|
|
DEBUG_LOG("%s: Successfully initialized DRM backend", __FUNCTION__);
|
||
|
|
} else {
|
||
|
|
ALOGE("%s: Failed to initialize DRM backend", __FUNCTION__);
|
||
|
|
return HWC3::Error::NoResources;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
mDrmEventListener = DrmEventListener::create(mFd, [this]() { handleHotplug(); });
|
||
|
|
if (!mDrmEventListener) {
|
||
|
|
ALOGE("%s: Failed to initialize DRM event listener", __FUNCTION__);
|
||
|
|
} else {
|
||
|
|
DEBUG_LOG("%s: Successfully initialized DRM event listener", __FUNCTION__);
|
||
|
|
}
|
||
|
|
|
||
|
|
DEBUG_LOG("%s: Successfully initialized.", __FUNCTION__);
|
||
|
|
return HWC3::Error::None;
|
||
|
|
}
|
||
|
|
|
||
|
|
HWC3::Error DrmClient::getDisplayConfigs(std::vector<DisplayConfig>* configs) const {
|
||
|
|
DEBUG_LOG("%s", __FUNCTION__);
|
||
|
|
|
||
|
|
AutoReadLock lock(mDisplaysMutex);
|
||
|
|
|
||
|
|
configs->clear();
|
||
|
|
|
||
|
|
for (const auto& display : mDisplays) {
|
||
|
|
if (!display->isConnected()) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
configs->emplace_back(DisplayConfig{
|
||
|
|
.id = display->getId(),
|
||
|
|
.width = display->getWidth(),
|
||
|
|
.height = display->getHeight(),
|
||
|
|
.dpiX = display->getDpiX(),
|
||
|
|
.dpiY = display->getDpiY(),
|
||
|
|
.refreshRateHz = display->getRefreshRateUint(),
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
return HWC3::Error::None;
|
||
|
|
}
|
||
|
|
|
||
|
|
HWC3::Error DrmClient::registerOnHotplugCallback(const HotplugCallback& cb) {
|
||
|
|
mHotplugCallback = cb;
|
||
|
|
return HWC3::Error::None;
|
||
|
|
}
|
||
|
|
|
||
|
|
HWC3::Error DrmClient::unregisterOnHotplugCallback() {
|
||
|
|
mHotplugCallback.reset();
|
||
|
|
return HWC3::Error::None;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool DrmClient::loadDrmDisplays() {
|
||
|
|
DEBUG_LOG("%s", __FUNCTION__);
|
||
|
|
|
||
|
|
std::vector<std::unique_ptr<DrmCrtc>> crtcs;
|
||
|
|
std::vector<std::unique_ptr<DrmConnector>> connectors;
|
||
|
|
std::vector<std::unique_ptr<DrmPlane>> planes;
|
||
|
|
|
||
|
|
drmModePlaneResPtr drmPlaneResources = drmModeGetPlaneResources(mFd.get());
|
||
|
|
for (uint32_t i = 0; i < drmPlaneResources->count_planes; ++i) {
|
||
|
|
const uint32_t planeId = drmPlaneResources->planes[i];
|
||
|
|
|
||
|
|
auto crtc = DrmPlane::create(mFd, planeId);
|
||
|
|
if (!crtc) {
|
||
|
|
ALOGE("%s: Failed to create DRM CRTC.", __FUNCTION__);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
planes.emplace_back(std::move(crtc));
|
||
|
|
}
|
||
|
|
drmModeFreePlaneResources(drmPlaneResources);
|
||
|
|
|
||
|
|
drmModeRes* drmResources = drmModeGetResources(mFd.get());
|
||
|
|
for (uint32_t crtcIndex = 0; crtcIndex < drmResources->count_crtcs; crtcIndex++) {
|
||
|
|
const uint32_t crtcId = drmResources->crtcs[crtcIndex];
|
||
|
|
|
||
|
|
auto crtc = DrmCrtc::create(mFd, crtcId, crtcIndex);
|
||
|
|
if (!crtc) {
|
||
|
|
ALOGE("%s: Failed to create DRM CRTC.", __FUNCTION__);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
crtcs.emplace_back(std::move(crtc));
|
||
|
|
}
|
||
|
|
|
||
|
|
for (uint32_t i = 0; i < drmResources->count_connectors; ++i) {
|
||
|
|
const uint32_t connectorId = drmResources->connectors[i];
|
||
|
|
|
||
|
|
auto connector = DrmConnector::create(mFd, connectorId);
|
||
|
|
if (!connector) {
|
||
|
|
ALOGE("%s: Failed to create DRM CRTC.", __FUNCTION__);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
connectors.emplace_back(std::move(connector));
|
||
|
|
}
|
||
|
|
|
||
|
|
drmModeFreeResources(drmResources);
|
||
|
|
|
||
|
|
if (crtcs.size() != connectors.size()) {
|
||
|
|
ALOGE("%s: Failed assumption mCrtcs.size():%zu equals mConnectors.size():%zu", __FUNCTION__,
|
||
|
|
crtcs.size(), connectors.size());
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (uint32_t i = 0; i < crtcs.size(); i++) {
|
||
|
|
std::unique_ptr<DrmCrtc> crtc = std::move(crtcs[i]);
|
||
|
|
std::unique_ptr<DrmConnector> connector = std::move(connectors[i]);
|
||
|
|
|
||
|
|
auto planeIt =
|
||
|
|
std::find_if(planes.begin(), planes.end(), [&](const std::unique_ptr<DrmPlane>& plane) {
|
||
|
|
if (!plane->isOverlay() && !plane->isPrimary()) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return plane->isCompatibleWith(*crtc);
|
||
|
|
});
|
||
|
|
if (planeIt == planes.end()) {
|
||
|
|
ALOGE("%s: Failed to find plane for display:%" PRIu32, __FUNCTION__, i);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::unique_ptr<DrmPlane> plane = std::move(*planeIt);
|
||
|
|
planes.erase(planeIt);
|
||
|
|
|
||
|
|
auto display =
|
||
|
|
DrmDisplay::create(i, std::move(connector), std::move(crtc), std::move(plane), mFd);
|
||
|
|
if (!display) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
mDisplays.push_back(std::move(display));
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::tuple<HWC3::Error, std::shared_ptr<DrmBuffer>> DrmClient::create(
|
||
|
|
const native_handle_t* handle) {
|
||
|
|
cros_gralloc_handle* crosHandle = (cros_gralloc_handle*)handle;
|
||
|
|
if (crosHandle == nullptr) {
|
||
|
|
ALOGE("%s: invalid cros_gralloc_handle", __FUNCTION__);
|
||
|
|
return std::make_tuple(HWC3::Error::NoResources, nullptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
DrmPrimeBufferHandle primeHandle = 0;
|
||
|
|
int ret = drmPrimeFDToHandle(mFd.get(), crosHandle->fds[0], &primeHandle);
|
||
|
|
if (ret) {
|
||
|
|
ALOGE("%s: drmPrimeFDToHandle failed: %s (errno %d)", __FUNCTION__, strerror(errno), errno);
|
||
|
|
return std::make_tuple(HWC3::Error::NoResources, nullptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
auto buffer = std::shared_ptr<DrmBuffer>(new DrmBuffer(*this));
|
||
|
|
buffer->mWidth = crosHandle->width;
|
||
|
|
buffer->mHeight = crosHandle->height;
|
||
|
|
buffer->mDrmFormat = crosHandle->format;
|
||
|
|
buffer->mPlaneFds[0] = crosHandle->fds[0];
|
||
|
|
buffer->mPlaneHandles[0] = primeHandle;
|
||
|
|
buffer->mPlanePitches[0] = crosHandle->strides[0];
|
||
|
|
buffer->mPlaneOffsets[0] = crosHandle->offsets[0];
|
||
|
|
|
||
|
|
uint32_t framebuffer = 0;
|
||
|
|
ret = drmModeAddFB2(mFd.get(), buffer->mWidth, buffer->mHeight, buffer->mDrmFormat,
|
||
|
|
buffer->mPlaneHandles, buffer->mPlanePitches, buffer->mPlaneOffsets,
|
||
|
|
&framebuffer, 0);
|
||
|
|
if (ret) {
|
||
|
|
ALOGE("%s: drmModeAddFB2 failed: %s (errno %d)", __FUNCTION__, strerror(errno), errno);
|
||
|
|
return std::make_tuple(HWC3::Error::NoResources, nullptr);
|
||
|
|
}
|
||
|
|
DEBUG_LOG("%s: created framebuffer:%" PRIu32, __FUNCTION__, framebuffer);
|
||
|
|
buffer->mDrmFramebuffer = framebuffer;
|
||
|
|
|
||
|
|
return std::make_tuple(HWC3::Error::None, std::shared_ptr<DrmBuffer>(buffer));
|
||
|
|
}
|
||
|
|
|
||
|
|
HWC3::Error DrmClient::destroyDrmFramebuffer(DrmBuffer* buffer) {
|
||
|
|
if (buffer->mDrmFramebuffer) {
|
||
|
|
uint32_t framebuffer = *buffer->mDrmFramebuffer;
|
||
|
|
if (drmModeRmFB(mFd.get(), framebuffer)) {
|
||
|
|
ALOGE("%s: drmModeRmFB failed: %s (errno %d)", __FUNCTION__, strerror(errno), errno);
|
||
|
|
return HWC3::Error::NoResources;
|
||
|
|
}
|
||
|
|
DEBUG_LOG("%s: destroyed framebuffer:%" PRIu32, __FUNCTION__, framebuffer);
|
||
|
|
buffer->mDrmFramebuffer.reset();
|
||
|
|
}
|
||
|
|
if (buffer->mPlaneHandles[0]) {
|
||
|
|
struct drm_gem_close gem_close = {};
|
||
|
|
gem_close.handle = buffer->mPlaneHandles[0];
|
||
|
|
if (drmIoctl(mFd.get(), DRM_IOCTL_GEM_CLOSE, &gem_close)) {
|
||
|
|
ALOGE("%s: DRM_IOCTL_GEM_CLOSE failed: %s (errno %d)", __FUNCTION__, strerror(errno),
|
||
|
|
errno);
|
||
|
|
return HWC3::Error::NoResources;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return HWC3::Error::None;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool DrmClient::handleHotplug() {
|
||
|
|
DEBUG_LOG("%s", __FUNCTION__);
|
||
|
|
|
||
|
|
struct HotplugToReport {
|
||
|
|
uint32_t id;
|
||
|
|
uint32_t width;
|
||
|
|
uint32_t height;
|
||
|
|
uint32_t dpiX;
|
||
|
|
uint32_t dpiY;
|
||
|
|
uint32_t rr;
|
||
|
|
bool connected;
|
||
|
|
};
|
||
|
|
|
||
|
|
std::vector<HotplugToReport> hotplugs;
|
||
|
|
|
||
|
|
{
|
||
|
|
AutoWriteLock lock(mDisplaysMutex);
|
||
|
|
|
||
|
|
for (auto& display : mDisplays) {
|
||
|
|
auto change = display->checkAndHandleHotplug(mFd);
|
||
|
|
if (change == DrmHotplugChange::kNoChange) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
hotplugs.push_back(HotplugToReport{
|
||
|
|
.id = display->getId(),
|
||
|
|
.width = display->getWidth(),
|
||
|
|
.height = display->getHeight(),
|
||
|
|
.dpiX = display->getDpiX(),
|
||
|
|
.dpiY = display->getDpiY(),
|
||
|
|
.rr = display->getRefreshRateUint(),
|
||
|
|
.connected = change == DrmHotplugChange::kConnected,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const auto& hotplug : hotplugs) {
|
||
|
|
if (mHotplugCallback) {
|
||
|
|
(*mHotplugCallback)(hotplug.connected, //
|
||
|
|
hotplug.id, //
|
||
|
|
hotplug.width, //
|
||
|
|
hotplug.height, //
|
||
|
|
hotplug.dpiX, //
|
||
|
|
hotplug.dpiY, //
|
||
|
|
hotplug.rr);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::tuple<HWC3::Error, ::android::base::unique_fd> DrmClient::flushToDisplay(
|
||
|
|
int displayId, const std::shared_ptr<DrmBuffer>& buffer,
|
||
|
|
::android::base::borrowed_fd inSyncFd) {
|
||
|
|
ATRACE_CALL();
|
||
|
|
|
||
|
|
if (!buffer->mDrmFramebuffer) {
|
||
|
|
ALOGE("%s: failed, no framebuffer created.", __FUNCTION__);
|
||
|
|
return std::make_tuple(HWC3::Error::NoResources, ::android::base::unique_fd());
|
||
|
|
}
|
||
|
|
|
||
|
|
AutoReadLock lock(mDisplaysMutex);
|
||
|
|
return mDisplays[displayId]->flush(mFd, inSyncFd, buffer);
|
||
|
|
}
|
||
|
|
|
||
|
|
std::optional<std::vector<uint8_t>> DrmClient::getEdid(uint32_t displayId) {
|
||
|
|
AutoReadLock lock(mDisplaysMutex);
|
||
|
|
|
||
|
|
if (displayId >= mDisplays.size()) {
|
||
|
|
DEBUG_LOG("%s: invalid display:%" PRIu32, __FUNCTION__, displayId);
|
||
|
|
return std::nullopt;
|
||
|
|
}
|
||
|
|
|
||
|
|
return mDisplays[displayId]->getEdid();
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace aidl::android::hardware::graphics::composer3::impl
|