1 /*
2  * Copyright 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "DrmConnector.h"
18 
19 #include <span>
20 
21 #include "EdidInfo.h"
22 
23 namespace aidl::android::hardware::graphics::composer3::impl {
24 namespace {
25 
26 static constexpr const float kMillimetersPerInch = 25.4f;
27 
28 }  // namespace
29 
create(::android::base::borrowed_fd drmFd,uint32_t connectorId)30 std::unique_ptr<DrmConnector> DrmConnector::create(::android::base::borrowed_fd drmFd,
31                                                    uint32_t connectorId) {
32     std::unique_ptr<DrmConnector> connector(new DrmConnector(connectorId));
33 
34     if (!connector->update(drmFd)) {
35         return nullptr;
36     }
37 
38     return connector;
39 }
40 
update(::android::base::borrowed_fd drmFd)41 bool DrmConnector::update(::android::base::borrowed_fd drmFd) {
42     DEBUG_LOG("%s: Loading properties for connector:%" PRIu32, __FUNCTION__, mId);
43 
44     if (!LoadDrmProperties(drmFd, mId, DRM_MODE_OBJECT_CONNECTOR, GetPropertiesMap(),
45                            this)) {
46         ALOGE("%s: Failed to load connector properties.", __FUNCTION__);
47         return false;
48     }
49 
50 
51     drmModeConnector* drmConnector = drmModeGetConnector(drmFd.get(), mId);
52     if (!drmConnector) {
53         ALOGE("%s: Failed to load connector.", __FUNCTION__);
54         return false;
55     }
56 
57     mStatus = drmConnector->connection;
58 
59     mModes.clear();
60     for (uint32_t i = 0; i < drmConnector->count_modes; i++) {
61         auto mode = DrmMode::create(drmFd, drmConnector->modes[i]);
62         if (!mode) {
63             ALOGE("%s: Failed to create mode for connector.", __FUNCTION__);
64             return false;
65         }
66 
67         mModes.push_back(std::move(mode));
68     }
69 
70     drmModeFreeConnector(drmConnector);
71 
72     if (mStatus == DRM_MODE_CONNECTED) {
73         std::optional<EdidInfo> maybeEdidInfo = loadEdid(drmFd);
74         if (maybeEdidInfo) {
75             const EdidInfo& edidInfo = maybeEdidInfo.value();
76             mWidthMillimeters = edidInfo.mWidthMillimeters;
77             mHeightMillimeters = edidInfo.mHeightMillimeters;
78         } else {
79             ALOGW("%s: Use fallback size from drmModeConnector. This can result inaccurate DPIs.",
80                   __FUNCTION__);
81             mWidthMillimeters = drmConnector->mmWidth;
82             mHeightMillimeters = drmConnector->mmHeight;
83         }
84     }
85 
86     DEBUG_LOG("%s: connector:%" PRIu32 " widthMillimeters:%" PRIu32 " heightMillimeters:%" PRIu32,
87               __FUNCTION__, mId, (mWidthMillimeters ? *mWidthMillimeters : 0),
88               (mHeightMillimeters ? *mHeightMillimeters : 0));
89 
90     return true;
91 }
92 
loadEdid(::android::base::borrowed_fd drmFd)93 std::optional<EdidInfo> DrmConnector::loadEdid(::android::base::borrowed_fd drmFd) {
94     DEBUG_LOG("%s: display:%" PRIu32, __FUNCTION__, mId);
95 
96     const uint64_t edidBlobId = mEdidProp.getValue();
97     if (edidBlobId == -1) {
98         ALOGW("%s: display:%" PRIu32 " does not have EDID.", __FUNCTION__, mId);
99         return std::nullopt;
100     }
101 
102     auto blob = drmModeGetPropertyBlob(drmFd.get(), static_cast<uint32_t>(edidBlobId));
103     if (!blob) {
104         ALOGE("%s: display:%" PRIu32 " failed to read EDID blob (%" PRIu64 "): %s", __FUNCTION__,
105               mId, edidBlobId, strerror(errno));
106         return std::nullopt;
107     }
108 
109     const uint8_t* blobStart = static_cast<uint8_t*>(blob->data);
110     mEdid = std::vector<uint8_t>(blobStart, blobStart + blob->length);
111 
112     drmModeFreePropertyBlob(blob);
113 
114     using byte_view = std::span<const uint8_t>;
115     byte_view edid(*mEdid);
116 
117     return EdidInfo::parse(edid);
118 }
119 
getWidth() const120 uint32_t DrmConnector::getWidth() const {
121     DEBUG_LOG("%s: connector:%" PRIu32, __FUNCTION__, mId);
122 
123     if (mModes.empty()) {
124         return 0;
125     }
126     return mModes[0]->hdisplay;
127 }
128 
getHeight() const129 uint32_t DrmConnector::getHeight() const {
130     DEBUG_LOG("%s: connector:%" PRIu32, __FUNCTION__, mId);
131 
132     if (mModes.empty()) {
133         return 0;
134     }
135     return mModes[0]->vdisplay;
136 }
137 
getDpiX() const138 uint32_t DrmConnector::getDpiX() const {
139     DEBUG_LOG("%s: connector:%" PRIu32, __FUNCTION__, mId);
140 
141     if (mModes.empty()) {
142         return 0;
143     }
144 
145     const auto& mode = mModes[0];
146     if (mWidthMillimeters) {
147         const uint32_t dpi = static_cast<uint32_t>(
148             (static_cast<float>(mode->hdisplay) / static_cast<float>(*mWidthMillimeters)) *
149             kMillimetersPerInch);
150         DEBUG_LOG("%s: connector:%" PRIu32 " has dpi-x:%" PRIu32, __FUNCTION__, mId, dpi);
151         return dpi;
152     }
153 
154     return 0;
155 }
156 
getDpiY() const157 uint32_t DrmConnector::getDpiY() const {
158     DEBUG_LOG("%s: connector:%" PRIu32, __FUNCTION__, mId);
159 
160     if (mModes.empty()) {
161         return 0;
162     }
163 
164     const auto& mode = mModes[0];
165     if (mHeightMillimeters) {
166         const uint32_t dpi = static_cast<uint32_t>(
167             (static_cast<float>(mode->vdisplay) / static_cast<float>(*mHeightMillimeters)) *
168             kMillimetersPerInch);
169         DEBUG_LOG("%s: connector:%" PRIu32 " has dpi-x:%" PRIu32, __FUNCTION__, mId, dpi);
170         return dpi;
171     }
172 
173     return 0;
174 }
175 
getRefreshRate() const176 float DrmConnector::getRefreshRate() const {
177     DEBUG_LOG("%s: connector:%" PRIu32, __FUNCTION__, mId);
178 
179     if (!mModes.empty()) {
180         const auto& mode = mModes[0];
181         return 1000.0f * mode->clock / ((float)mode->vtotal * (float)mode->htotal);
182     }
183 
184     return -1.0f;
185 }
186 
187 }  // namespace aidl::android::hardware::graphics::composer3::impl
188