/* * Copyright (C) 2015 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. */ #define LOG_TAG "hwc-drm-device" #include "DrmDevice.h" #include #include #include #include #include #include #include "drm/DrmAtomicStateManager.h" #include "drm/DrmPlane.h" #include "utils/log.h" #include "utils/properties.h" namespace android { DrmDevice::DrmDevice() { drm_fb_importer_ = std::make_unique(*this); } auto DrmDevice::Init(const char *path) -> int { /* TODO: Use drmOpenControl here instead */ fd_ = UniqueFd(open(path, O_RDWR | O_CLOEXEC)); if (!fd_) { // NOLINTNEXTLINE(concurrency-mt-unsafe): Fixme ALOGE("Failed to open dri %s: %s", path, strerror(errno)); return -ENODEV; } int ret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); if (ret != 0) { ALOGE("Failed to set universal plane cap %d", ret); return ret; } ret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_ATOMIC, 1); if (ret != 0) { ALOGE("Failed to set atomic cap %d", ret); return ret; } #ifdef DRM_CLIENT_CAP_WRITEBACK_CONNECTORS ret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1); if (ret != 0) { ALOGI("Failed to set writeback cap %d", ret); } #endif uint64_t cap_value = 0; if (drmGetCap(GetFd(), DRM_CAP_ADDFB2_MODIFIERS, &cap_value) != 0) { ALOGW("drmGetCap failed. Fallback to no modifier support."); cap_value = 0; } HasAddFb2ModifiersSupport_ = cap_value != 0; drmSetMaster(GetFd()); if (drmIsMaster(GetFd()) == 0) { ALOGE("DRM/KMS master access required"); return -EACCES; } auto res = MakeDrmModeResUnique(GetFd()); if (!res) { ALOGE("Failed to get DrmDevice resources"); return -ENODEV; } min_resolution_ = std::pair(res->min_width, res->min_height); max_resolution_ = std::pair(res->max_width, res->max_height); for (int i = 0; i < res->count_crtcs; ++i) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) auto crtc = DrmCrtc::CreateInstance(*this, res->crtcs[i], i); if (crtc) { crtcs_.emplace_back(std::move(crtc)); } } for (int i = 0; i < res->count_encoders; ++i) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) auto enc = DrmEncoder::CreateInstance(*this, res->encoders[i], i); if (enc) { encoders_.emplace_back(std::move(enc)); } } for (int i = 0; i < res->count_connectors; ++i) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) auto conn = DrmConnector::CreateInstance(*this, res->connectors[i], i); if (!conn) { continue; } if (conn->IsWriteback()) { writeback_connectors_.emplace_back(std::move(conn)); } else { connectors_.emplace_back(std::move(conn)); } } auto plane_res = MakeDrmModePlaneResUnique(GetFd()); if (!plane_res) { ALOGE("Failed to get plane resources"); return -ENOENT; } for (uint32_t i = 0; i < plane_res->count_planes; ++i) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) auto plane = DrmPlane::CreateInstance(*this, plane_res->planes[i]); if (plane) { planes_.emplace_back(std::move(plane)); } } return 0; } auto DrmDevice::RegisterUserPropertyBlob(void *data, size_t length) const -> DrmModeUserPropertyBlobUnique { struct drm_mode_create_blob create_blob {}; create_blob.length = length; // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) create_blob.data = (__u64)data; int ret = drmIoctl(GetFd(), DRM_IOCTL_MODE_CREATEPROPBLOB, &create_blob); if (ret != 0) { ALOGE("Failed to create mode property blob %d", ret); return {}; } return DrmModeUserPropertyBlobUnique( new uint32_t(create_blob.blob_id), [this](const uint32_t *it) { struct drm_mode_destroy_blob destroy_blob {}; destroy_blob.blob_id = (__u32)*it; int err = drmIoctl(GetFd(), DRM_IOCTL_MODE_DESTROYPROPBLOB, &destroy_blob); if (err != 0) { ALOGE("Failed to destroy mode property blob %" PRIu32 "/%d", *it, err); } // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) delete it; }); } int DrmDevice::GetProperty(uint32_t obj_id, uint32_t obj_type, const char *prop_name, DrmProperty *property) const { drmModeObjectPropertiesPtr props = nullptr; props = drmModeObjectGetProperties(GetFd(), obj_id, obj_type); if (props == nullptr) { ALOGE("Failed to get properties for %d/%x", obj_id, obj_type); return -ENODEV; } bool found = false; for (int i = 0; !found && (size_t)i < props->count_props; ++i) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) drmModePropertyPtr p = drmModeGetProperty(GetFd(), props->props[i]); if (strcmp(p->name, prop_name) == 0) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) property->Init(obj_id, p, props->prop_values[i]); found = true; } drmModeFreeProperty(p); } drmModeFreeObjectProperties(props); return found ? 0 : -ENOENT; } std::string DrmDevice::GetName() const { auto *ver = drmGetVersion(GetFd()); if (ver == nullptr) { ALOGW("Failed to get drm version for fd=%d", GetFd()); return "generic"; } std::string name(ver->name); drmFreeVersion(ver); return name; } auto DrmDevice::IsKMSDev(const char *path) -> bool { auto fd = UniqueFd(open(path, O_RDWR | O_CLOEXEC)); if (!fd) { return false; } auto res = MakeDrmModeResUnique(fd.Get()); if (!res) { return false; } bool is_kms = res->count_crtcs > 0 && res->count_connectors > 0 && res->count_encoders > 0; return is_kms; } auto DrmDevice::GetConnectors() -> const std::vector> & { return connectors_; } auto DrmDevice::GetPlanes() -> const std::vector> & { return planes_; } auto DrmDevice::GetCrtcs() -> const std::vector> & { return crtcs_; } auto DrmDevice::GetEncoders() -> const std::vector> & { return encoders_; } } // namespace android