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 "DisplayFinder.h"
18
19 #include <android-base/parseint.h>
20 #include <android-base/properties.h>
21 #include <android-base/strings.h>
22 #include <device_config_shared.h>
23
24 #include "Common.h"
25 #include "HostUtils.h"
26
27 namespace aidl::android::hardware::graphics::composer3::impl {
28 namespace {
29
HertzToPeriodNanos(uint32_t hertz)30 constexpr int32_t HertzToPeriodNanos(uint32_t hertz) {
31 return 1000 * 1000 * 1000 / hertz;
32 }
33
findCuttlefishDisplays(std::vector<DisplayMultiConfigs> * outDisplays)34 HWC3::Error findCuttlefishDisplays(std::vector<DisplayMultiConfigs>* outDisplays) {
35 DEBUG_LOG("%s", __FUNCTION__);
36
37 // TODO: replace with initializing directly from DRM info.
38 const auto deviceConfig = cuttlefish::GetDeviceConfig();
39
40 int64_t displayId = 0;
41 for (const auto& deviceDisplayConfig : deviceConfig.display_config()) {
42 const auto vsyncPeriodNanos =
43 HertzToPeriodNanos(deviceDisplayConfig.refresh_rate_hz());
44
45 DisplayMultiConfigs display = {
46 .displayId = displayId,
47 .activeConfigId = 0,
48 .configs =
49 {
50 DisplayConfig(0, //
51 deviceDisplayConfig.width(), //
52 deviceDisplayConfig.height(), //
53 deviceDisplayConfig.dpi(), //
54 deviceDisplayConfig.dpi(), //
55 vsyncPeriodNanos),
56 },
57 };
58 outDisplays->push_back(display);
59 ++displayId;
60 }
61
62 return HWC3::Error::None;
63 }
64
getVsyncHzFromProperty()65 static int getVsyncHzFromProperty() {
66 static constexpr const auto kVsyncProp = "ro.boot.qemu.vsync";
67
68 const auto vsyncProp = ::android::base::GetProperty(kVsyncProp, "");
69 DEBUG_LOG("%s: prop value is: %s", __FUNCTION__, vsyncProp.c_str());
70
71 uint64_t vsyncPeriod;
72 if (!::android::base::ParseUint(vsyncProp, &vsyncPeriod)) {
73 ALOGE("%s: failed to parse vsync period '%s', returning default 60",
74 __FUNCTION__, vsyncProp.c_str());
75 return 60;
76 }
77
78 return static_cast<int>(vsyncPeriod);
79 }
80
findGoldfishPrimaryDisplay(std::vector<DisplayMultiConfigs> * outDisplays)81 HWC3::Error findGoldfishPrimaryDisplay(
82 std::vector<DisplayMultiConfigs>* outDisplays) {
83 DEBUG_LOG("%s", __FUNCTION__);
84
85 DEFINE_AND_VALIDATE_HOST_CONNECTION
86 hostCon->lock();
87 const int32_t vsyncPeriodNanos = HertzToPeriodNanos(getVsyncHzFromProperty());
88 DisplayMultiConfigs display;
89 display.displayId = 0;
90 if (rcEnc->hasHWCMultiConfigs()) {
91 int count = rcEnc->rcGetFBDisplayConfigsCount(rcEnc);
92 if (count <= 0) {
93 ALOGE("%s failed to allocate primary display, config count %d", __func__,
94 count);
95 return HWC3::Error::NoResources;
96 }
97 display.activeConfigId = rcEnc->rcGetFBDisplayActiveConfig(rcEnc);
98 for (int configId = 0; configId < count; configId++) {
99 display.configs.push_back(DisplayConfig(
100 configId, //
101 rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_WIDTH), //
102 rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_HEIGHT), //
103 rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_XDPI), //
104 rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_YDPI), //
105 vsyncPeriodNanos //
106 ));
107 }
108 } else {
109 display.activeConfigId = 0;
110 display.configs.push_back(DisplayConfig(
111 0, //
112 rcEnc->rcGetFBParam(rcEnc, FB_WIDTH), //
113 rcEnc->rcGetFBParam(rcEnc, FB_HEIGHT), //
114 rcEnc->rcGetFBParam(rcEnc, FB_XDPI), //
115 rcEnc->rcGetFBParam(rcEnc, FB_YDPI), //
116 vsyncPeriodNanos //
117 ));
118 }
119 hostCon->unlock();
120
121 outDisplays->push_back(display);
122
123 return HWC3::Error::None;
124 }
125
parseExternalDisplaysFromProperties(std::vector<int> & outPropIntParts)126 void parseExternalDisplaysFromProperties(std::vector<int>& outPropIntParts) {
127
128 static constexpr const char* kExternalDisplayProp[] = {
129 "hwservicemanager.external.displays",
130 "ro.boot.qemu.external.displays",
131 };
132
133 for (auto propName: kExternalDisplayProp) {
134 const std::string propVal = ::android::base::GetProperty(propName, "");
135 if (propVal.empty()) {
136 DEBUG_LOG("%s: prop name is: %s, prop value is: empty", __FUNCTION__,
137 propName);
138 continue;
139 }
140 DEBUG_LOG("%s: prop name is: %s, prop value is: %s", __FUNCTION__,
141 propName, propVal.c_str());
142
143 const std::vector<std::string> propStringParts =
144 ::android::base::Split(propVal, ",");
145 if (propStringParts.size() % 5 != 0) {
146 ALOGE("%s: Invalid syntax for system prop %s which is %s", __FUNCTION__,
147 propName, propVal.c_str());
148 continue;
149 }
150 std::vector<int> propIntParts;
151 for (const std::string& propStringPart : propStringParts) {
152 int propIntPart;
153 if (!::android::base::ParseInt(propStringPart, &propIntPart)) {
154 ALOGE("%s: Invalid syntax for system prop %s which is %s", __FUNCTION__,
155 propName, propVal.c_str());
156 break;
157 }
158 propIntParts.push_back(propIntPart);
159 }
160 if (propIntParts.empty() || propIntParts.size() % 5 != 0) {
161 continue;
162 }
163 outPropIntParts.insert(outPropIntParts.end(), propIntParts.begin(), propIntParts.end());
164 }
165 }
166
findGoldfishSecondaryDisplays(std::vector<DisplayMultiConfigs> * outDisplays)167 HWC3::Error findGoldfishSecondaryDisplays(
168 std::vector<DisplayMultiConfigs>* outDisplays) {
169 DEBUG_LOG("%s", __FUNCTION__);
170
171
172 std::vector<int> propIntParts;
173 parseExternalDisplaysFromProperties(propIntParts);
174
175 int64_t secondaryDisplayId = 1;
176 while (!propIntParts.empty()) {
177 DisplayMultiConfigs display;
178 display.displayId = secondaryDisplayId;
179 display.activeConfigId = 0;
180 display.configs.push_back(DisplayConfig(
181 0, //
182 /*width=*/propIntParts[1], //
183 /*heighth=*/propIntParts[2], //
184 /*dpiXh=*/propIntParts[3], //
185 /*dpiYh=*/propIntParts[3], //
186 /*vsyncPeriod=*/HertzToPeriodNanos(160) //
187 ));
188 outDisplays->push_back(display);
189
190 ++secondaryDisplayId;
191
192 propIntParts.erase(propIntParts.begin(), propIntParts.begin() + 5);
193 }
194
195 return HWC3::Error::None;
196 }
197
findGoldfishDisplays(std::vector<DisplayMultiConfigs> * outDisplays)198 HWC3::Error findGoldfishDisplays(std::vector<DisplayMultiConfigs>* outDisplays) {
199 HWC3::Error error = findGoldfishPrimaryDisplay(outDisplays);
200 if (error != HWC3::Error::None) {
201 ALOGE("%s failed to find Goldfish primary display", __FUNCTION__);
202 return error;
203 }
204
205 error = findGoldfishSecondaryDisplays(outDisplays);
206 if (error != HWC3::Error::None) {
207 ALOGE("%s failed to find Goldfish secondary displays", __FUNCTION__);
208 }
209
210 return error;
211 }
212
213 // This is currently only used for Gem5 bring-up where virtio-gpu and drm
214 // are not currently available. For now, just return a placeholder display.
findNoOpDisplays(std::vector<DisplayMultiConfigs> * outDisplays)215 HWC3::Error findNoOpDisplays(std::vector<DisplayMultiConfigs>* outDisplays) {
216 outDisplays->push_back(DisplayMultiConfigs{
217 .displayId = 0,
218 .activeConfigId = 0,
219 .configs = {DisplayConfig(0,
220 /*width=*/720, //
221 /*heighth=*/1280, //
222 /*dpiXh=*/320, //
223 /*dpiYh=*/320, //
224 /*vsyncPeriod=*/HertzToPeriodNanos(30) //
225 )},
226 });
227
228 return HWC3::Error::None;
229 }
230
findDrmDisplays(const DrmClient & drm,std::vector<DisplayMultiConfigs> * outDisplays)231 HWC3::Error findDrmDisplays(const DrmClient& drm,
232 std::vector<DisplayMultiConfigs>* outDisplays) {
233 outDisplays->clear();
234
235 std::vector<DrmClient::DisplayConfig> drmDisplayConfigs;
236
237 HWC3::Error error = drm.getDisplayConfigs(&drmDisplayConfigs);
238 if (error != HWC3::Error::None) {
239 ALOGE("%s failed to find displays from DRM.", __FUNCTION__);
240 return error;
241 }
242
243 for (const DrmClient::DisplayConfig drmDisplayConfig : drmDisplayConfigs) {
244 outDisplays->push_back(DisplayMultiConfigs{
245 .displayId = drmDisplayConfig.id,
246 .activeConfigId = static_cast<int32_t>(drmDisplayConfig.id),
247 .configs = {
248 DisplayConfig(static_cast<int32_t>(drmDisplayConfig.id),
249 drmDisplayConfig.width,
250 drmDisplayConfig.height,
251 drmDisplayConfig.dpiX,
252 drmDisplayConfig.dpiY,
253 HertzToPeriodNanos(drmDisplayConfig.refreshRateHz)),
254 },
255 });
256 }
257
258 return HWC3::Error::None;
259 }
260
261 } // namespace
262
findDisplays(const DrmClient * drm,std::vector<DisplayMultiConfigs> * outDisplays)263 HWC3::Error findDisplays(const DrmClient* drm,
264 std::vector<DisplayMultiConfigs>* outDisplays) {
265 HWC3::Error error = HWC3::Error::None;
266 if (IsInGem5DisplayFinderMode() || IsInNoOpCompositionMode()) {
267 error = findNoOpDisplays(outDisplays);
268 } else if (IsInDrmDisplayFinderMode()) {
269 if (drm == nullptr) {
270 ALOGE("%s asked to find displays from DRM, but DRM not available.",
271 __FUNCTION__);
272 return HWC3::Error::NoResources;
273 }
274 error = findDrmDisplays(*drm, outDisplays);
275 } else if (IsCuttlefish()) {
276 error = findCuttlefishDisplays(outDisplays);
277 } else {
278 error = findGoldfishDisplays(outDisplays);
279 }
280
281 if (error != HWC3::Error::None) {
282 ALOGE("%s failed to find displays", __FUNCTION__);
283 return error;
284 }
285
286 for (auto& display : *outDisplays) {
287 DisplayConfig::addConfigGroups(&display.configs);
288 }
289
290 return HWC3::Error::None;
291 }
292
293 } // namespace aidl::android::hardware::graphics::composer3::impl
294