/* * 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 "DrmConnector.h" #include namespace aidl::android::hardware::graphics::composer3::impl { namespace { static constexpr const float kMillimetersPerInch = 25.4f; } // namespace std::unique_ptr DrmConnector::create(::android::base::borrowed_fd drmFd, uint32_t connectorId) { std::unique_ptr connector(new DrmConnector(connectorId)); if (!LoadDrmProperties(drmFd, connectorId, DRM_MODE_OBJECT_CONNECTOR, GetPropertiesMap(), connector.get())) { ALOGE("%s: Failed to load connector properties.", __FUNCTION__); return nullptr; } if (!connector->update(drmFd)) { return nullptr; } return connector; } bool DrmConnector::update(::android::base::borrowed_fd drmFd) { DEBUG_LOG("%s: Loading properties for connector:%" PRIu32, __FUNCTION__, mId); drmModeConnector* drmConnector = drmModeGetConnector(drmFd.get(), mId); if (!drmConnector) { ALOGE("%s: Failed to load connector.", __FUNCTION__); return false; } mStatus = drmConnector->connection; mModes.clear(); for (uint32_t i = 0; i < drmConnector->count_modes; i++) { auto mode = DrmMode::create(drmFd, drmConnector->modes[i]); if (!mode) { ALOGE("%s: Failed to create mode for connector.", __FUNCTION__); return false; } mModes.push_back(std::move(mode)); } drmModeFreeConnector(drmConnector); if (mStatus == DRM_MODE_CONNECTED) { if (!loadEdid(drmFd)) { return false; } } DEBUG_LOG("%s: connector:%" PRIu32 " widthMillimeters:%" PRIu32 " heightMillimeters:%" PRIu32, __FUNCTION__, mId, (mWidthMillimeters ? *mWidthMillimeters : 0), (mHeightMillimeters ? *mHeightMillimeters : 0)); return true; } bool DrmConnector::loadEdid(::android::base::borrowed_fd drmFd) { DEBUG_LOG("%s: display:%" PRIu32, __FUNCTION__, mId); const uint64_t edidBlobId = mEdidProp.getValue(); if (edidBlobId == -1) { ALOGW("%s: display:%" PRIu32 " does not have EDID.", __FUNCTION__, mId); return true; } auto blob = drmModeGetPropertyBlob(drmFd.get(), static_cast(edidBlobId)); if (!blob) { ALOGE("%s: display:%" PRIu32 " failed to read EDID blob (%" PRIu64 "): %s", __FUNCTION__, mId, edidBlobId, strerror(errno)); return false; } const uint8_t* blobStart = static_cast(blob->data); mEdid = std::vector(blobStart, blobStart + blob->length); drmModeFreePropertyBlob(blob); using byte_view = std::span; constexpr size_t kEdidDescriptorOffset = 54; constexpr size_t kEdidDescriptorLength = 18; byte_view edid(*mEdid); edid = edid.subspan(kEdidDescriptorOffset); byte_view descriptor(edid.data(), kEdidDescriptorLength); if (descriptor[0] == 0 && descriptor[1] == 0) { ALOGE("%s: display:%" PRIu32 " is missing preferred detailed timing descriptor.", __FUNCTION__, mId); return -1; } const uint8_t w_mm_lsb = descriptor[12]; const uint8_t h_mm_lsb = descriptor[13]; const uint8_t w_and_h_mm_msb = descriptor[14]; mWidthMillimeters = w_mm_lsb | (w_and_h_mm_msb & 0xf0) << 4; mHeightMillimeters = h_mm_lsb | (w_and_h_mm_msb & 0xf) << 8; return true; } uint32_t DrmConnector::getWidth() const { DEBUG_LOG("%s: connector:%" PRIu32, __FUNCTION__, mId); if (mModes.empty()) { return 0; } return mModes[0]->hdisplay; } uint32_t DrmConnector::getHeight() const { DEBUG_LOG("%s: connector:%" PRIu32, __FUNCTION__, mId); if (mModes.empty()) { return 0; } return mModes[0]->vdisplay; } uint32_t DrmConnector::getDpiX() const { DEBUG_LOG("%s: connector:%" PRIu32, __FUNCTION__, mId); if (mModes.empty()) { return 0; } const auto& mode = mModes[0]; if (mWidthMillimeters) { const uint32_t dpi = static_cast( (static_cast(mode->hdisplay) / static_cast(*mWidthMillimeters)) * kMillimetersPerInch); DEBUG_LOG("%s: connector:%" PRIu32 " has dpi-x:%" PRIu32, __FUNCTION__, mId, dpi); return dpi; } return 0; } uint32_t DrmConnector::getDpiY() const { DEBUG_LOG("%s: connector:%" PRIu32, __FUNCTION__, mId); if (mModes.empty()) { return 0; } const auto& mode = mModes[0]; if (mHeightMillimeters) { const uint32_t dpi = static_cast( (static_cast(mode->vdisplay) / static_cast(*mHeightMillimeters)) * kMillimetersPerInch); DEBUG_LOG("%s: connector:%" PRIu32 " has dpi-x:%" PRIu32, __FUNCTION__, mId, dpi); return dpi; } return 0; } float DrmConnector::getRefreshRate() const { DEBUG_LOG("%s: connector:%" PRIu32, __FUNCTION__, mId); if (!mModes.empty()) { const auto& mode = mModes[0]; return 1000.0f * mode->clock / ((float)mode->vtotal * (float)mode->htotal); } return -1.0f; } } // namespace aidl::android::hardware::graphics::composer3::impl