• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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