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
findGoldfishSecondaryDisplays(std::vector<DisplayMultiConfigs> * outDisplays)126 HWC3::Error findGoldfishSecondaryDisplays(
127 std::vector<DisplayMultiConfigs>* outDisplays) {
128 DEBUG_LOG("%s", __FUNCTION__);
129
130 static constexpr const char kExternalDisplayProp[] =
131 "hwservicemanager.external.displays";
132
133 const auto propString =
134 ::android::base::GetProperty(kExternalDisplayProp, "");
135 DEBUG_LOG("%s: prop value is: %s", __FUNCTION__, propString.c_str());
136
137 if (propString.empty()) {
138 return HWC3::Error::None;
139 }
140
141 const std::vector<std::string> propStringParts =
142 ::android::base::Split(propString, ",");
143 if (propStringParts.size() % 5 != 0) {
144 ALOGE("%s: Invalid syntax for system prop %s which is %s", __FUNCTION__,
145 kExternalDisplayProp, propString.c_str());
146 return HWC3::Error::BadParameter;
147 }
148
149 std::vector<int> propIntParts;
150 for (const std::string& propStringPart : propStringParts) {
151 int propIntPart;
152 if (!::android::base::ParseInt(propStringPart, &propIntPart)) {
153 ALOGE("%s: Invalid syntax for system prop %s which is %s", __FUNCTION__,
154 kExternalDisplayProp, propString.c_str());
155 return HWC3::Error::BadParameter;
156 }
157 propIntParts.push_back(propIntPart);
158 }
159
160 int64_t secondaryDisplayId = 1;
161 while (!propIntParts.empty()) {
162 DisplayMultiConfigs display;
163 display.displayId = secondaryDisplayId;
164 display.activeConfigId = 0;
165 display.configs.push_back(DisplayConfig(
166 0, //
167 /*width=*/propIntParts[1], //
168 /*heighth=*/propIntParts[2], //
169 /*dpiXh=*/propIntParts[3], //
170 /*dpiYh=*/propIntParts[3], //
171 /*vsyncPeriod=*/HertzToPeriodNanos(160) //
172 ));
173 outDisplays->push_back(display);
174
175 ++secondaryDisplayId;
176
177 propIntParts.erase(propIntParts.begin(), propIntParts.begin() + 5);
178 }
179
180 return HWC3::Error::None;
181 }
182
findGoldfishDisplays(std::vector<DisplayMultiConfigs> * outDisplays)183 HWC3::Error findGoldfishDisplays(std::vector<DisplayMultiConfigs>* outDisplays) {
184 HWC3::Error error = findGoldfishPrimaryDisplay(outDisplays);
185 if (error != HWC3::Error::None) {
186 ALOGE("%s failed to find Goldfish primary display", __FUNCTION__);
187 return error;
188 }
189
190 error = findGoldfishSecondaryDisplays(outDisplays);
191 if (error != HWC3::Error::None) {
192 ALOGE("%s failed to find Goldfish secondary displays", __FUNCTION__);
193 }
194
195 return error;
196 }
197
198 // This is currently only used for Gem5 bring-up where virtio-gpu and drm
199 // are not currently available. For now, just return a placeholder display.
findNoOpDisplays(std::vector<DisplayMultiConfigs> * outDisplays)200 HWC3::Error findNoOpDisplays(std::vector<DisplayMultiConfigs>* outDisplays) {
201 outDisplays->push_back(DisplayMultiConfigs{
202 .displayId = 0,
203 .activeConfigId = 0,
204 .configs = {DisplayConfig(0,
205 /*width=*/720, //
206 /*heighth=*/1280, //
207 /*dpiXh=*/320, //
208 /*dpiYh=*/320, //
209 /*vsyncPeriod=*/HertzToPeriodNanos(30) //
210 )},
211 });
212
213 return HWC3::Error::None;
214 }
215
findDrmDisplays(const DrmPresenter & drm,std::vector<DisplayMultiConfigs> * outDisplays)216 HWC3::Error findDrmDisplays(const DrmPresenter& drm,
217 std::vector<DisplayMultiConfigs>* outDisplays) {
218 outDisplays->clear();
219
220 std::vector<DrmPresenter::DisplayConfig> drmDisplayConfigs;
221
222 HWC3::Error error = drm.getDisplayConfigs(&drmDisplayConfigs);
223 if (error != HWC3::Error::None) {
224 ALOGE("%s failed to find displays from DRM.", __FUNCTION__);
225 return error;
226 }
227
228 for (const DrmPresenter::DisplayConfig drmDisplayConfig : drmDisplayConfigs) {
229 outDisplays->push_back(DisplayMultiConfigs{
230 .displayId = drmDisplayConfig.id,
231 .activeConfigId = static_cast<int32_t>(drmDisplayConfig.id),
232 .configs = {
233 DisplayConfig(static_cast<int32_t>(drmDisplayConfig.id),
234 drmDisplayConfig.width,
235 drmDisplayConfig.height,
236 drmDisplayConfig.dpiX,
237 drmDisplayConfig.dpiY,
238 HertzToPeriodNanos(drmDisplayConfig.refreshRateHz)),
239 },
240 });
241 }
242
243 return HWC3::Error::None;
244 }
245
246 } // namespace
247
findDisplays(const DrmPresenter * drm,std::vector<DisplayMultiConfigs> * outDisplays)248 HWC3::Error findDisplays(const DrmPresenter* drm,
249 std::vector<DisplayMultiConfigs>* outDisplays) {
250 HWC3::Error error = HWC3::Error::None;
251 if (IsInNoOpCompositionMode()) {
252 error = findNoOpDisplays(outDisplays);
253 } else if (IsInDrmDisplayFinderMode()) {
254 if (drm == nullptr) {
255 ALOGE("%s asked to find displays from DRM, but DRM not available.",
256 __FUNCTION__);
257 return HWC3::Error::NoResources;
258 }
259 error = findDrmDisplays(*drm, outDisplays);
260 } else if (IsCuttlefish()) {
261 error = findCuttlefishDisplays(outDisplays);
262 } else {
263 error = findGoldfishDisplays(outDisplays);
264 }
265
266 if (error != HWC3::Error::None) {
267 ALOGE("%s failed to find displays", __FUNCTION__);
268 return error;
269 }
270
271 for (auto& display : *outDisplays) {
272 DisplayConfig::addConfigGroups(&display.configs);
273 }
274
275 return HWC3::Error::None;
276 }
277
278 } // namespace aidl::android::hardware::graphics::composer3::impl
279