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