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