| /* |
| * 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 "DrmDisplay.h" |
| |
| #include "DrmAtomicRequest.h" |
| |
| namespace aidl::android::hardware::graphics::composer3::impl { |
| namespace { |
| |
| template <typename T> |
| uint64_t addressAsUint(T* pointer) { |
| return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(pointer)); |
| } |
| |
| } // namespace |
| |
| std::unique_ptr<DrmDisplay> DrmDisplay::create(uint32_t id, std::unique_ptr<DrmConnector> connector, |
| std::unique_ptr<DrmCrtc> crtc, |
| std::unique_ptr<DrmPlane> plane, |
| ::android::base::borrowed_fd drmFd) { |
| if (!crtc) { |
| ALOGE("%s: invalid crtc.", __FUNCTION__); |
| return nullptr; |
| } |
| if (!connector) { |
| ALOGE("%s: invalid connector.", __FUNCTION__); |
| return nullptr; |
| } |
| if (!plane) { |
| ALOGE("%s: invalid plane.", __FUNCTION__); |
| return nullptr; |
| } |
| |
| if (connector->isConnected()) { |
| auto request = DrmAtomicRequest::create(); |
| if (!request) { |
| ALOGE("%s: failed to create atomic request.", __FUNCTION__); |
| return nullptr; |
| } |
| |
| bool okay = true; |
| okay &= request->Set(connector->getId(), connector->getCrtcProperty(), crtc->getId()); |
| okay &= request->Set(crtc->getId(), crtc->getActiveProperty(), 1); |
| okay &= request->Set(crtc->getId(), crtc->getModeProperty(), |
| connector->getDefaultMode()->getBlobId()); |
| okay &= request->Commit(drmFd); |
| if (!okay) { |
| ALOGE("%s: failed to set display mode.", __FUNCTION__); |
| return nullptr; |
| } |
| } |
| |
| return std::unique_ptr<DrmDisplay>( |
| new DrmDisplay(id, std::move(connector), std::move(crtc), std::move(plane))); |
| } |
| |
| std::optional<std::vector<uint8_t>> DrmDisplay::getEdid(::android::base::borrowed_fd drmFd) const { |
| DEBUG_LOG("%s: display:%" PRIu32, __FUNCTION__, mId); |
| |
| if (!mConnector) { |
| ALOGE("%s: display:%" PRIu32 " is missing connector.", __FUNCTION__, mId); |
| return std::nullopt; |
| } |
| |
| const DrmProperty& edidProp = mConnector->getEdidProperty(); |
| if (edidProp.getId() == -1) { |
| ALOGW("%s: display:%" PRIu32 " does not have EDID.", __FUNCTION__, mId); |
| return std::nullopt; |
| } |
| |
| const uint64_t edidBlobId = edidProp.getValue(); |
| |
| auto blob = drmModeGetPropertyBlob(drmFd.get(), edidBlobId); |
| if (!blob) { |
| ALOGE("%s: display:%" PRIu32 " failed to read EDID blob (%" PRIu64 "): %s", __FUNCTION__, |
| mId, edidBlobId, strerror(errno)); |
| return std::nullopt; |
| } |
| |
| std::vector<uint8_t> edid; |
| uint8_t* start = static_cast<uint8_t*>(blob->data); |
| edid.insert(edid.begin(), start, start + blob->length); |
| |
| drmModeFreePropertyBlob(blob); |
| |
| return edid; |
| } |
| |
| std::tuple<HWC3::Error, ::android::base::unique_fd> DrmDisplay::flush( |
| ::android::base::borrowed_fd drmFd, ::android::base::borrowed_fd inSyncFd, |
| const std::shared_ptr<DrmBuffer>& buffer) { |
| std::unique_ptr<DrmAtomicRequest> request = DrmAtomicRequest::create(); |
| if (!request) { |
| ALOGE("%s: failed to create atomic request.", __FUNCTION__); |
| return std::make_tuple(HWC3::Error::NoResources, ::android::base::unique_fd()); |
| } |
| |
| int flushFenceFd = -1; |
| |
| bool okay = true; |
| okay &= |
| request->Set(mCrtc->getId(), mCrtc->getOutFenceProperty(), addressAsUint(&flushFenceFd)); |
| okay &= request->Set(mPlane->getId(), mPlane->getCrtcProperty(), mCrtc->getId()); |
| okay &= request->Set(mPlane->getId(), mPlane->getInFenceProperty(), inSyncFd.get()); |
| okay &= request->Set(mPlane->getId(), mPlane->getFbProperty(), *buffer->mDrmFramebuffer); |
| okay &= request->Set(mPlane->getId(), mPlane->getCrtcXProperty(), 0); |
| okay &= request->Set(mPlane->getId(), mPlane->getCrtcYProperty(), 0); |
| okay &= request->Set(mPlane->getId(), mPlane->getCrtcWProperty(), buffer->mWidth); |
| okay &= request->Set(mPlane->getId(), mPlane->getCrtcHProperty(), buffer->mHeight); |
| okay &= request->Set(mPlane->getId(), mPlane->getSrcXProperty(), 0); |
| okay &= request->Set(mPlane->getId(), mPlane->getSrcYProperty(), 0); |
| okay &= request->Set(mPlane->getId(), mPlane->getSrcWProperty(), buffer->mWidth << 16); |
| okay &= request->Set(mPlane->getId(), mPlane->getSrcHProperty(), buffer->mHeight << 16); |
| |
| okay &= request->Commit(drmFd); |
| if (!okay) { |
| ALOGE("%s: failed to flush to display.", __FUNCTION__); |
| return std::make_tuple(HWC3::Error::NoResources, ::android::base::unique_fd()); |
| } |
| |
| mPreviousBuffer = buffer; |
| |
| DEBUG_LOG("%s: submitted atomic update, flush fence:%d\n", __FUNCTION__, flushFenceFd); |
| return std::make_tuple(HWC3::Error::None, ::android::base::unique_fd(flushFenceFd)); |
| } |
| |
| bool DrmDisplay::onConnect(::android::base::borrowed_fd drmFd) { |
| DEBUG_LOG("%s: display:%" PRIu32, __FUNCTION__, mId); |
| |
| auto request = DrmAtomicRequest::create(); |
| if (!request) { |
| ALOGE("%s: display:%" PRIu32 " failed to create atomic request.", __FUNCTION__, mId); |
| return false; |
| } |
| |
| bool okay = true; |
| okay &= request->Set(mConnector->getId(), mConnector->getCrtcProperty(), mCrtc->getId()); |
| okay &= request->Set(mCrtc->getId(), mCrtc->getActiveProperty(), 1); |
| okay &= request->Set(mCrtc->getId(), mCrtc->getModeProperty(), |
| mConnector->getDefaultMode()->getBlobId()); |
| |
| okay &= request->Commit(drmFd); |
| if (!okay) { |
| ALOGE("%s: display:%" PRIu32 " failed to set mode.", __FUNCTION__, mId); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool DrmDisplay::onDisconnect(::android::base::borrowed_fd drmFd) { |
| DEBUG_LOG("%s: display:%" PRIu32, __FUNCTION__, mId); |
| |
| auto request = DrmAtomicRequest::create(); |
| if (!request) { |
| ALOGE("%s: display:%" PRIu32 " failed to create atomic request.", __FUNCTION__, mId); |
| return false; |
| } |
| |
| bool okay = true; |
| okay &= request->Set(mPlane->getId(), mPlane->getCrtcProperty(), 0); |
| okay &= request->Set(mPlane->getId(), mPlane->getFbProperty(), 0); |
| |
| okay &= request->Commit(drmFd); |
| if (!okay) { |
| ALOGE("%s: display:%" PRIu32 " failed to set mode", __FUNCTION__, mId); |
| } |
| |
| mPreviousBuffer.reset(); |
| |
| return okay; |
| } |
| |
| DrmHotplugChange DrmDisplay::checkAndHandleHotplug(::android::base::borrowed_fd drmFd) { |
| DEBUG_LOG("%s: display:%" PRIu32, __FUNCTION__, mId); |
| |
| const bool oldConnected = mConnector->isConnected(); |
| mConnector->update(drmFd); |
| const bool newConnected = mConnector->isConnected(); |
| |
| if (oldConnected == newConnected) { |
| return DrmHotplugChange::kNoChange; |
| } |
| |
| if (newConnected) { |
| ALOGI("%s: display:%" PRIu32 " was connected.", __FUNCTION__, mId); |
| if (!onConnect(drmFd)) { |
| ALOGE("%s: display:%" PRIu32 " failed to connect.", __FUNCTION__, mId); |
| } |
| return DrmHotplugChange::kConnected; |
| } else { |
| ALOGI("%s: display:%" PRIu32 " was disconnected.", __FUNCTION__, mId); |
| if (!onDisconnect(drmFd)) { |
| ALOGE("%s: display:%" PRIu32 " failed to disconnect.", __FUNCTION__, mId); |
| } |
| return DrmHotplugChange::kDisconnected; |
| } |
| } |
| |
| } // namespace aidl::android::hardware::graphics::composer3::impl |