1 /*
2 * Copyright 2021 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 android {
28 namespace {
29
HertzToPeriodNanos(uint32_t hertz)30 constexpr int32_t HertzToPeriodNanos(uint32_t hertz) {
31 return 1000 * 1000 * 1000 / hertz;
32 }
33
getVsyncHzFromProperty()34 static int getVsyncHzFromProperty() {
35 static constexpr const auto kVsyncProp = "ro.boot.qemu.vsync";
36
37 const auto vsyncProp = android::base::GetProperty(kVsyncProp, "");
38 DEBUG_LOG("%s: prop value is: %s", __FUNCTION__, vsyncProp.c_str());
39
40 uint64_t vsyncPeriod;
41 if (!android::base::ParseUint(vsyncProp, &vsyncPeriod)) {
42 ALOGE("%s: failed to parse vsync period '%s', returning default 60",
43 __FUNCTION__, vsyncProp.c_str());
44 return 60;
45 }
46
47 return static_cast<int>(vsyncPeriod);
48 }
49
getVsyncForDisplay(DrmPresenter * drmPresenter,uint32_t displayId)50 int32_t getVsyncForDisplay(DrmPresenter* drmPresenter, uint32_t displayId) {
51 const int32_t vsyncPeriodForDisplay = drmPresenter->refreshRate(displayId);
52 return vsyncPeriodForDisplay < 0
53 ? HertzToPeriodNanos(getVsyncHzFromProperty())
54 : HertzToPeriodNanos(vsyncPeriodForDisplay);
55 }
56
findCuttlefishDisplays(std::vector<DisplayMultiConfigs> & displays)57 HWC2::Error findCuttlefishDisplays(std::vector<DisplayMultiConfigs>& displays) {
58 DEBUG_LOG("%s", __FUNCTION__);
59
60 // TODO: replace with initializing directly from DRM info.
61 const auto deviceConfig = cuttlefish::GetDeviceConfig();
62
63 hwc2_display_t displayId = 0;
64 for (const auto& deviceDisplayConfig : deviceConfig.display_config()) {
65 // crosvm does not fully support EDID yet, so we solely rely on the
66 // device config for our vsync.
67 const auto vsyncPeriodNanos =
68 HertzToPeriodNanos(deviceDisplayConfig.refresh_rate_hz());
69
70 DisplayMultiConfigs display = {
71 .displayId = displayId,
72 .activeConfigId = 0,
73 .configs =
74 {
75 DisplayConfig(0, //
76 deviceDisplayConfig.width(), //
77 deviceDisplayConfig.height(), //
78 deviceDisplayConfig.dpi(), //
79 deviceDisplayConfig.dpi(), //
80 vsyncPeriodNanos),
81 },
82 };
83 displays.push_back(display);
84 ++displayId;
85 }
86
87 return HWC2::Error::None;
88 }
89
findGoldfishPrimaryDisplay(DrmPresenter * drmPresenter,std::vector<DisplayMultiConfigs> & displays)90 HWC2::Error findGoldfishPrimaryDisplay(
91 DrmPresenter* drmPresenter, std::vector<DisplayMultiConfigs>& displays) {
92 DEBUG_LOG("%s", __FUNCTION__);
93
94 DEFINE_AND_VALIDATE_HOST_CONNECTION
95 hostCon->lock();
96 DisplayMultiConfigs display;
97 display.displayId = 0;
98 if (rcEnc->hasHWCMultiConfigs()) {
99 int count = rcEnc->rcGetFBDisplayConfigsCount(rcEnc);
100 if (count <= 0) {
101 ALOGE("%s failed to allocate primary display, config count %d", __func__,
102 count);
103 return HWC2::Error::NoResources;
104 }
105 display.activeConfigId = rcEnc->rcGetFBDisplayActiveConfig(rcEnc);
106 for (int configId = 0; configId < count; configId++) {
107 display.configs.push_back(DisplayConfig(
108 configId, //
109 rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_WIDTH), //
110 rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_HEIGHT), //
111 rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_XDPI), //
112 rcEnc->rcGetFBDisplayConfigsParam(rcEnc, configId, FB_YDPI), //
113 getVsyncForDisplay(drmPresenter, display.displayId) //
114 ));
115 }
116 } else {
117 display.activeConfigId = 0;
118 display.configs.push_back(DisplayConfig(
119 0, //
120 rcEnc->rcGetFBParam(rcEnc, FB_WIDTH), //
121 rcEnc->rcGetFBParam(rcEnc, FB_HEIGHT), //
122 rcEnc->rcGetFBParam(rcEnc, FB_XDPI), //
123 rcEnc->rcGetFBParam(rcEnc, FB_YDPI), //
124 getVsyncForDisplay(drmPresenter, 0) //
125 ));
126 }
127 hostCon->unlock();
128
129 displays.push_back(display);
130
131 return HWC2::Error::None;
132 }
133
findGoldfishSecondaryDisplays(std::vector<DisplayMultiConfigs> & displays)134 HWC2::Error findGoldfishSecondaryDisplays(
135 std::vector<DisplayMultiConfigs>& displays) {
136 DEBUG_LOG("%s", __FUNCTION__);
137
138 static constexpr const char kExternalDisplayProp[] =
139 "hwservicemanager.external.displays";
140
141 const auto propString = android::base::GetProperty(kExternalDisplayProp, "");
142 DEBUG_LOG("%s: prop value is: %s", __FUNCTION__, propString.c_str());
143
144 if (propString.empty()) {
145 return HWC2::Error::None;
146 }
147
148 const std::vector<std::string> propStringParts =
149 android::base::Split(propString, ",");
150 if (propStringParts.size() % 5 != 0) {
151 ALOGE("%s: Invalid syntax for system prop %s which is %s", __FUNCTION__,
152 kExternalDisplayProp, propString.c_str());
153 return HWC2::Error::BadParameter;
154 }
155
156 std::vector<int> propIntParts;
157 for (const std::string& propStringPart : propStringParts) {
158 int propIntPart;
159 if (!android::base::ParseInt(propStringPart, &propIntPart)) {
160 ALOGE("%s: Invalid syntax for system prop %s which is %s", __FUNCTION__,
161 kExternalDisplayProp, propString.c_str());
162 return HWC2::Error::BadParameter;
163 }
164 propIntParts.push_back(propIntPart);
165 }
166
167 hwc2_display_t secondaryDisplayId = 1;
168 while (!propIntParts.empty()) {
169 DisplayMultiConfigs display;
170 display.displayId = secondaryDisplayId;
171 display.activeConfigId = 0;
172 display.configs.push_back(DisplayConfig(
173 0, //
174 /*width=*/propIntParts[1], //
175 /*heighth=*/propIntParts[2], //
176 /*dpiXh=*/propIntParts[3], //
177 /*dpiYh=*/propIntParts[3], //
178 /*vsyncPeriod=*/HertzToPeriodNanos(160) //
179 ));
180 displays.push_back(display);
181
182 ++secondaryDisplayId;
183
184 propIntParts.erase(propIntParts.begin(), propIntParts.begin() + 5);
185 }
186
187 return HWC2::Error::None;
188 }
189
findGoldfishDisplays(DrmPresenter * drmPresenter,std::vector<DisplayMultiConfigs> & displays)190 HWC2::Error findGoldfishDisplays(DrmPresenter* drmPresenter,
191 std::vector<DisplayMultiConfigs>& displays) {
192 HWC2::Error error = findGoldfishPrimaryDisplay(drmPresenter, displays);
193 if (error != HWC2::Error::None) {
194 ALOGE("%s failed to find Goldfish primary display", __FUNCTION__);
195 return error;
196 }
197
198 error = findGoldfishSecondaryDisplays(displays);
199 if (error != HWC2::Error::None) {
200 ALOGE("%s failed to find Goldfish secondary displays", __FUNCTION__);
201 }
202
203 return error;
204 }
205
206 // This is currently only used for Gem5 bring-up where virtio-gpu and drm
207 // are not currently available. For now, just return a placeholder display.
findNoOpDisplays(std::vector<DisplayMultiConfigs> & displays)208 HWC2::Error findNoOpDisplays(std::vector<DisplayMultiConfigs>& displays) {
209 displays.push_back(DisplayMultiConfigs{
210 .displayId = 0,
211 .activeConfigId = 0,
212 .configs = {DisplayConfig(0,
213 /*width=*/720, //
214 /*heighth=*/1280, //
215 /*dpiXh=*/320, //
216 /*dpiYh=*/320, //
217 /*vsyncPeriod=*/HertzToPeriodNanos(30) //
218 )},
219 });
220
221 return HWC2::Error::None;
222 }
223
224 } // namespace
225
findDisplays(DrmPresenter * drmPresenter,std::vector<DisplayMultiConfigs> & displays)226 HWC2::Error findDisplays(DrmPresenter* drmPresenter,
227 std::vector<DisplayMultiConfigs>& displays) {
228 HWC2::Error error = HWC2::Error::None;
229 if (IsNoOpMode()) {
230 error = findNoOpDisplays(displays);
231 } else if (IsCuttlefish()) {
232 error = findCuttlefishDisplays(displays);
233 } else {
234 error = findGoldfishDisplays(drmPresenter, displays);
235 }
236
237 if (error != HWC2::Error::None) {
238 ALOGE("%s failed to find displays", __FUNCTION__);
239 return error;
240 }
241
242 for (auto& display : displays) {
243 DisplayConfig::addConfigGroups(&display.configs);
244 }
245
246 return HWC2::Error::None;
247 }
248
249 } // namespace android
250