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