• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 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 #define LOG_TAG "hwc-display-configs"
18 
19 #include "HwcDisplayConfigs.h"
20 
21 #include <cmath>
22 
23 #include "drm/DrmConnector.h"
24 #include "utils/log.h"
25 
26 constexpr uint32_t kHeadlessModeDisplayWidthMm = 163;
27 constexpr uint32_t kHeadlessModeDisplayHeightMm = 122;
28 constexpr uint32_t kHeadlessModeDisplayWidthPx = 1024;
29 constexpr uint32_t kHeadlessModeDisplayHeightPx = 768;
30 constexpr uint32_t kHeadlessModeDisplayVRefresh = 60;
31 
32 namespace android {
33 
34 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
35 uint32_t HwcDisplayConfigs::last_config_id = 1;
36 
FillHeadless()37 void HwcDisplayConfigs::FillHeadless() {
38   hwc_configs.clear();
39 
40   last_config_id++;
41   preferred_config_id = active_config_id = last_config_id;
42   auto headless_drm_mode_info = (drmModeModeInfo){
43       .hdisplay = kHeadlessModeDisplayWidthPx,
44       .vdisplay = kHeadlessModeDisplayHeightPx,
45       .vrefresh = kHeadlessModeDisplayVRefresh,
46       .name = "HEADLESS-MODE",
47   };
48   hwc_configs[active_config_id] = (HwcDisplayConfig){
49       .id = active_config_id,
50       .group_id = 1,
51       .mode = DrmMode(&headless_drm_mode_info),
52   };
53 
54   mm_width = kHeadlessModeDisplayWidthMm;
55   mm_height = kHeadlessModeDisplayHeightMm;
56 }
57 
58 // NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme
Update(DrmConnector & connector)59 HWC2::Error HwcDisplayConfigs::Update(DrmConnector &connector) {
60   /* In case UpdateModes will fail we will still have one mode for headless
61    * mode*/
62   FillHeadless();
63   /* Read real configs */
64   int ret = connector.UpdateModes();
65   if (ret != 0) {
66     ALOGE("Failed to update display modes %d", ret);
67     return HWC2::Error::BadDisplay;
68   }
69 
70   if (connector.GetModes().empty()) {
71     ALOGE("No modes reported by KMS");
72     return HWC2::Error::BadDisplay;
73   }
74 
75   hwc_configs.clear();
76   mm_width = connector.GetMmWidth();
77   mm_height = connector.GetMmHeight();
78 
79   preferred_config_id = 0;
80   uint32_t preferred_config_group_id = 0;
81 
82   uint32_t first_config_id = last_config_id;
83   uint32_t last_group_id = 1;
84 
85   /* Group modes */
86   for (const auto &mode : connector.GetModes()) {
87     /* Find group for the new mode or create new group */
88     uint32_t group_found = 0;
89     for (auto &hwc_config : hwc_configs) {
90       if (mode.h_display() == hwc_config.second.mode.h_display() &&
91           mode.v_display() == hwc_config.second.mode.v_display()) {
92         group_found = hwc_config.second.group_id;
93       }
94     }
95     if (group_found == 0) {
96       group_found = last_group_id++;
97     }
98 
99     bool disabled = false;
100     if ((mode.flags() & DRM_MODE_FLAG_3D_MASK) != 0) {
101       ALOGI("Disabling display mode %s (Modes with 3D flag aren't supported)",
102             mode.name().c_str());
103       disabled = true;
104     }
105 
106     /* Add config */
107     hwc_configs[last_config_id] = {
108         .id = last_config_id,
109         .group_id = group_found,
110         .mode = mode,
111         .disabled = disabled,
112     };
113 
114     /* Chwck if the mode is preferred */
115     if ((mode.type() & DRM_MODE_TYPE_PREFERRED) != 0 &&
116         preferred_config_id == 0) {
117       preferred_config_id = last_config_id;
118       preferred_config_group_id = group_found;
119     }
120 
121     last_config_id++;
122   }
123 
124   /* We must have preferred mode. Set first mode as preferred
125    * in case KMS haven't reported anything. */
126   if (preferred_config_id == 0) {
127     preferred_config_id = first_config_id;
128     preferred_config_group_id = 1;
129   }
130 
131   for (uint32_t group = 1; group < last_group_id; group++) {
132     bool has_interlaced = false;
133     bool has_progressive = false;
134     for (auto &hwc_config : hwc_configs) {
135       if (hwc_config.second.group_id != group || hwc_config.second.disabled) {
136         continue;
137       }
138 
139       if (hwc_config.second.IsInterlaced()) {
140         has_interlaced = true;
141       } else {
142         has_progressive = true;
143       }
144     }
145 
146     bool has_both = has_interlaced && has_progressive;
147     if (!has_both) {
148       continue;
149     }
150 
151     bool group_contains_preferred_interlaced = false;
152     if (group == preferred_config_group_id &&
153         hwc_configs[preferred_config_id].IsInterlaced()) {
154       group_contains_preferred_interlaced = true;
155     }
156 
157     for (auto &hwc_config : hwc_configs) {
158       if (hwc_config.second.group_id != group || hwc_config.second.disabled) {
159         continue;
160       }
161 
162       bool disable = group_contains_preferred_interlaced
163                          ? !hwc_config.second.IsInterlaced()
164                          : hwc_config.second.IsInterlaced();
165 
166       if (disable) {
167         ALOGI(
168             "Group %i: Disabling display mode %s (This group should consist "
169             "of %s modes)",
170             group, hwc_config.second.mode.name().c_str(),
171             group_contains_preferred_interlaced ? "interlaced" : "progressive");
172 
173         hwc_config.second.disabled = true;
174       }
175     }
176   }
177 
178   /* Group should not contain 2 modes with FPS delta less than ~1HZ
179    * otherwise android.graphics.cts.SetFrameRateTest CTS will fail
180    */
181   constexpr float kMinFpsDelta = 1.0;  // FPS
182   for (uint32_t m1 = first_config_id; m1 < last_config_id; m1++) {
183     for (uint32_t m2 = first_config_id; m2 < last_config_id; m2++) {
184       if (m1 != m2 && hwc_configs[m1].group_id == hwc_configs[m2].group_id &&
185           !hwc_configs[m1].disabled && !hwc_configs[m2].disabled &&
186           fabsf(hwc_configs[m1].mode.v_refresh() -
187                 hwc_configs[m2].mode.v_refresh()) < kMinFpsDelta) {
188         ALOGI(
189             "Group %i: Disabling display mode %s (Refresh rate value is "
190             "too close to existing mode %s)",
191             hwc_configs[m2].group_id, hwc_configs[m2].mode.name().c_str(),
192             hwc_configs[m1].mode.name().c_str());
193 
194         hwc_configs[m2].disabled = true;
195       }
196     }
197   }
198 
199   return HWC2::Error::None;
200 }
201 
202 }  // namespace android
203