blob: 364535634ca7e1879d4819dec097c5972c5e5c43 [file] [log] [blame]
Roman Stratiienko0137f862022-01-04 18:27:40 +02001/*
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
Sean Paul468a7542024-07-16 19:50:58 +000017#define LOG_TAG "drmhwc"
Roman Stratiienko0137f862022-01-04 18:27:40 +020018
19#include "HwcDisplayConfigs.h"
20
21#include <cmath>
Roman Stratiienkof2c060f2023-09-18 22:46:08 +030022#include <cstring>
Roman Stratiienko0137f862022-01-04 18:27:40 +020023
24#include "drm/DrmConnector.h"
25#include "utils/log.h"
26
Roman Stratiienkof0c507f2022-01-17 18:29:24 +020027constexpr uint32_t kHeadlessModeDisplayWidthMm = 163;
28constexpr uint32_t kHeadlessModeDisplayHeightMm = 122;
29constexpr uint32_t kHeadlessModeDisplayWidthPx = 1024;
30constexpr uint32_t kHeadlessModeDisplayHeightPx = 768;
31constexpr uint32_t kHeadlessModeDisplayVRefresh = 60;
Roman Stratiienkof2c060f2023-09-18 22:46:08 +030032constexpr uint32_t kSyncLen = 10;
33constexpr uint32_t kBackPorch = 10;
34constexpr uint32_t kFrontPorch = 10;
35constexpr uint32_t kHzInKHz = 1000;
Roman Stratiienkof0c507f2022-01-17 18:29:24 +020036
Roman Stratiienko0137f862022-01-04 18:27:40 +020037namespace android {
38
Roman Stratiienko3dacd472022-01-11 19:18:34 +020039// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Roman Stratiienkod0c035b2022-01-21 15:12:56 +020040uint32_t HwcDisplayConfigs::last_config_id = 1;
Roman Stratiienko3dacd472022-01-11 19:18:34 +020041
Roman Stratiienkof2c060f2023-09-18 22:46:08 +030042void HwcDisplayConfigs::GenFakeMode(uint16_t width, uint16_t height) {
Roman Stratiienkof0c507f2022-01-17 18:29:24 +020043 hwc_configs.clear();
44
45 last_config_id++;
46 preferred_config_id = active_config_id = last_config_id;
47 auto headless_drm_mode_info = (drmModeModeInfo){
Roman Stratiienkof2c060f2023-09-18 22:46:08 +030048 .hdisplay = width,
49 .vdisplay = height,
Roman Stratiienkof0c507f2022-01-17 18:29:24 +020050 .vrefresh = kHeadlessModeDisplayVRefresh,
Roman Stratiienkof2c060f2023-09-18 22:46:08 +030051 .name = "VIRTUAL-MODE",
Roman Stratiienkof0c507f2022-01-17 18:29:24 +020052 };
Roman Stratiienkof2c060f2023-09-18 22:46:08 +030053
54 if (width == 0 || height == 0) {
55 strcpy(headless_drm_mode_info.name, "HEADLESS-MODE");
56 headless_drm_mode_info.hdisplay = kHeadlessModeDisplayWidthPx;
57 headless_drm_mode_info.vdisplay = kHeadlessModeDisplayHeightPx;
58 }
59
60 /* We need a valid mode to pass the kernel validation */
61
62 headless_drm_mode_info.hsync_start = headless_drm_mode_info.hdisplay +
63 kFrontPorch;
64 headless_drm_mode_info.hsync_end = headless_drm_mode_info.hsync_start +
65 kSyncLen;
66 headless_drm_mode_info.htotal = headless_drm_mode_info.hsync_end + kBackPorch;
67
68 headless_drm_mode_info.vsync_start = headless_drm_mode_info.vdisplay +
69 kFrontPorch;
70 headless_drm_mode_info.vsync_end = headless_drm_mode_info.vsync_start +
71 kSyncLen;
72 headless_drm_mode_info.vtotal = headless_drm_mode_info.vsync_end + kBackPorch;
73
74 headless_drm_mode_info.clock = (headless_drm_mode_info.htotal *
75 headless_drm_mode_info.vtotal *
76 headless_drm_mode_info.vrefresh) /
77 kHzInKHz;
78
Roman Stratiienkof0c507f2022-01-17 18:29:24 +020079 hwc_configs[active_config_id] = (HwcDisplayConfig){
80 .id = active_config_id,
81 .group_id = 1,
82 .mode = DrmMode(&headless_drm_mode_info),
83 };
84
85 mm_width = kHeadlessModeDisplayWidthMm;
86 mm_height = kHeadlessModeDisplayHeightMm;
Roman Stratiienko3dacd472022-01-11 19:18:34 +020087}
Roman Stratiienkof0c507f2022-01-17 18:29:24 +020088
Roman Stratiienko3dacd472022-01-11 19:18:34 +020089// NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme
90HWC2::Error HwcDisplayConfigs::Update(DrmConnector &connector) {
91 /* In case UpdateModes will fail we will still have one mode for headless
Roman Stratiienkof2c060f2023-09-18 22:46:08 +030092 * mode
93 */
94 GenFakeMode(0, 0);
Roman Stratiienkof0c507f2022-01-17 18:29:24 +020095 /* Read real configs */
Roman Stratiienkoa7913de2022-10-20 13:18:57 +030096 auto ret = connector.UpdateModes();
Roman Stratiienko0137f862022-01-04 18:27:40 +020097 if (ret != 0) {
98 ALOGE("Failed to update display modes %d", ret);
99 return HWC2::Error::BadDisplay;
100 }
101
Roman Stratiienko650299a2022-01-30 23:46:10 +0200102 if (connector.GetModes().empty()) {
Roman Stratiienko0137f862022-01-04 18:27:40 +0200103 ALOGE("No modes reported by KMS");
104 return HWC2::Error::BadDisplay;
105 }
106
Roman Stratiienkoaa8ec522022-01-17 13:17:56 +0200107 hwc_configs.clear();
Roman Stratiienko650299a2022-01-30 23:46:10 +0200108 mm_width = connector.GetMmWidth();
109 mm_height = connector.GetMmHeight();
Roman Stratiienkof0c507f2022-01-17 18:29:24 +0200110
Roman Stratiienkoaa8ec522022-01-17 13:17:56 +0200111 preferred_config_id = 0;
Roman Stratiienkod0c035b2022-01-21 15:12:56 +0200112 uint32_t preferred_config_group_id = 0;
Roman Stratiienkoaa8ec522022-01-17 13:17:56 +0200113
Roman Stratiienkoa7913de2022-10-20 13:18:57 +0300114 auto first_config_id = last_config_id;
Roman Stratiienkod0c035b2022-01-21 15:12:56 +0200115 uint32_t last_group_id = 1;
Roman Stratiienko0137f862022-01-04 18:27:40 +0200116
117 /* Group modes */
Roman Stratiienko650299a2022-01-30 23:46:10 +0200118 for (const auto &mode : connector.GetModes()) {
Roman Stratiienko0137f862022-01-04 18:27:40 +0200119 /* Find group for the new mode or create new group */
Roman Stratiienkod0c035b2022-01-21 15:12:56 +0200120 uint32_t group_found = 0;
Roman Stratiienko0137f862022-01-04 18:27:40 +0200121 for (auto &hwc_config : hwc_configs) {
Roman Stratiienkodf3120f2022-12-07 23:10:55 +0200122 if (mode.GetRawMode().hdisplay ==
123 hwc_config.second.mode.GetRawMode().hdisplay &&
124 mode.GetRawMode().vdisplay ==
125 hwc_config.second.mode.GetRawMode().vdisplay) {
Roman Stratiienko0137f862022-01-04 18:27:40 +0200126 group_found = hwc_config.second.group_id;
127 }
128 }
129 if (group_found == 0) {
130 group_found = last_group_id++;
131 }
132
133 bool disabled = false;
Roman Stratiienkodf3120f2022-12-07 23:10:55 +0200134 if ((mode.GetRawMode().flags & DRM_MODE_FLAG_3D_MASK) != 0) {
Roman Stratiienko0137f862022-01-04 18:27:40 +0200135 ALOGI("Disabling display mode %s (Modes with 3D flag aren't supported)",
Roman Stratiienkodf3120f2022-12-07 23:10:55 +0200136 mode.GetName().c_str());
Roman Stratiienko0137f862022-01-04 18:27:40 +0200137 disabled = true;
138 }
139
140 /* Add config */
141 hwc_configs[last_config_id] = {
142 .id = last_config_id,
143 .group_id = group_found,
144 .mode = mode,
145 .disabled = disabled,
146 };
147
148 /* Chwck if the mode is preferred */
Roman Stratiienkodf3120f2022-12-07 23:10:55 +0200149 if ((mode.GetRawMode().type & DRM_MODE_TYPE_PREFERRED) != 0 &&
Roman Stratiienko0137f862022-01-04 18:27:40 +0200150 preferred_config_id == 0) {
151 preferred_config_id = last_config_id;
152 preferred_config_group_id = group_found;
153 }
154
155 last_config_id++;
156 }
157
158 /* We must have preferred mode. Set first mode as preferred
159 * in case KMS haven't reported anything. */
160 if (preferred_config_id == 0) {
Roman Stratiienkoaa8ec522022-01-17 13:17:56 +0200161 preferred_config_id = first_config_id;
Roman Stratiienko0137f862022-01-04 18:27:40 +0200162 preferred_config_group_id = 1;
163 }
164
Roman Stratiienkod0c035b2022-01-21 15:12:56 +0200165 for (uint32_t group = 1; group < last_group_id; group++) {
Roman Stratiienko0137f862022-01-04 18:27:40 +0200166 bool has_interlaced = false;
167 bool has_progressive = false;
168 for (auto &hwc_config : hwc_configs) {
169 if (hwc_config.second.group_id != group || hwc_config.second.disabled) {
170 continue;
171 }
172
173 if (hwc_config.second.IsInterlaced()) {
174 has_interlaced = true;
175 } else {
176 has_progressive = true;
177 }
178 }
179
Roman Stratiienkoa7913de2022-10-20 13:18:57 +0300180 auto has_both = has_interlaced && has_progressive;
Roman Stratiienko0137f862022-01-04 18:27:40 +0200181 if (!has_both) {
182 continue;
183 }
184
185 bool group_contains_preferred_interlaced = false;
186 if (group == preferred_config_group_id &&
187 hwc_configs[preferred_config_id].IsInterlaced()) {
188 group_contains_preferred_interlaced = true;
189 }
190
191 for (auto &hwc_config : hwc_configs) {
192 if (hwc_config.second.group_id != group || hwc_config.second.disabled) {
193 continue;
194 }
195
Roman Stratiienkoa7913de2022-10-20 13:18:57 +0300196 auto disable = group_contains_preferred_interlaced
Roman Stratiienko0137f862022-01-04 18:27:40 +0200197 ? !hwc_config.second.IsInterlaced()
198 : hwc_config.second.IsInterlaced();
199
200 if (disable) {
201 ALOGI(
202 "Group %i: Disabling display mode %s (This group should consist "
203 "of %s modes)",
Roman Stratiienkodf3120f2022-12-07 23:10:55 +0200204 group, hwc_config.second.mode.GetName().c_str(),
Roman Stratiienko0137f862022-01-04 18:27:40 +0200205 group_contains_preferred_interlaced ? "interlaced" : "progressive");
206
207 hwc_config.second.disabled = true;
208 }
209 }
210 }
211
212 /* Group should not contain 2 modes with FPS delta less than ~1HZ
213 * otherwise android.graphics.cts.SetFrameRateTest CTS will fail
214 */
215 constexpr float kMinFpsDelta = 1.0; // FPS
Roman Stratiienkod0c035b2022-01-21 15:12:56 +0200216 for (uint32_t m1 = first_config_id; m1 < last_config_id; m1++) {
217 for (uint32_t m2 = first_config_id; m2 < last_config_id; m2++) {
Roman Stratiienko0137f862022-01-04 18:27:40 +0200218 if (m1 != m2 && hwc_configs[m1].group_id == hwc_configs[m2].group_id &&
219 !hwc_configs[m1].disabled && !hwc_configs[m2].disabled &&
Roman Stratiienkodf3120f2022-12-07 23:10:55 +0200220 fabsf(hwc_configs[m1].mode.GetVRefresh() -
221 hwc_configs[m2].mode.GetVRefresh()) < kMinFpsDelta) {
Roman Stratiienko0137f862022-01-04 18:27:40 +0200222 ALOGI(
223 "Group %i: Disabling display mode %s (Refresh rate value is "
224 "too close to existing mode %s)",
Roman Stratiienkodf3120f2022-12-07 23:10:55 +0200225 hwc_configs[m2].group_id, hwc_configs[m2].mode.GetName().c_str(),
226 hwc_configs[m1].mode.GetName().c_str());
Roman Stratiienko0137f862022-01-04 18:27:40 +0200227
228 hwc_configs[m2].disabled = true;
229 }
230 }
231 }
232
Roman Stratiienko0137f862022-01-04 18:27:40 +0200233 return HWC2::Error::None;
234}
235
236} // namespace android