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