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