blob: 16f1ed0b621378be445756e53e3fd7baa0003972 [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
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
Roman Stratiienkof0c507f2022-01-17 18:29:24 +020026constexpr uint32_t kHeadlessModeDisplayWidthMm = 163;
27constexpr uint32_t kHeadlessModeDisplayHeightMm = 122;
28constexpr uint32_t kHeadlessModeDisplayWidthPx = 1024;
29constexpr uint32_t kHeadlessModeDisplayHeightPx = 768;
30constexpr uint32_t kHeadlessModeDisplayVRefresh = 60;
31
Roman Stratiienko0137f862022-01-04 18:27:40 +020032namespace android {
33
34// NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme
35HWC2::Error HwcDisplayConfigs::Update(DrmConnector &connector) {
Roman Stratiienkof0c507f2022-01-17 18:29:24 +020036 /* In case UpdateModes will fail we will still have one mode for headless
37 * mode*/
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 /* Read real configs */
Roman Stratiienko0137f862022-01-04 18:27:40 +020058 int ret = connector.UpdateModes();
59 if (ret != 0) {
60 ALOGE("Failed to update display modes %d", ret);
61 return HWC2::Error::BadDisplay;
62 }
63
Roman Stratiienko0137f862022-01-04 18:27:40 +020064 if (connector.modes().empty()) {
65 ALOGE("No modes reported by KMS");
66 return HWC2::Error::BadDisplay;
67 }
68
Roman Stratiienkoaa8ec522022-01-17 13:17:56 +020069 hwc_configs.clear();
Roman Stratiienkof0c507f2022-01-17 18:29:24 +020070 mm_width = connector.mm_width();
71 mm_height = connector.mm_height();
72
Roman Stratiienkoaa8ec522022-01-17 13:17:56 +020073 preferred_config_id = 0;
74 int preferred_config_group_id = 0;
75
76 int first_config_id = last_config_id;
Roman Stratiienko0137f862022-01-04 18:27:40 +020077 int last_group_id = 1;
78
79 /* Group modes */
80 for (const auto &mode : connector.modes()) {
81 /* Find group for the new mode or create new group */
82 int group_found = 0;
83 for (auto &hwc_config : hwc_configs) {
84 if (mode.h_display() == hwc_config.second.mode.h_display() &&
85 mode.v_display() == hwc_config.second.mode.v_display()) {
86 group_found = hwc_config.second.group_id;
87 }
88 }
89 if (group_found == 0) {
90 group_found = last_group_id++;
91 }
92
93 bool disabled = false;
94 if ((mode.flags() & DRM_MODE_FLAG_3D_MASK) != 0) {
95 ALOGI("Disabling display mode %s (Modes with 3D flag aren't supported)",
96 mode.name().c_str());
97 disabled = true;
98 }
99
100 /* Add config */
101 hwc_configs[last_config_id] = {
102 .id = last_config_id,
103 .group_id = group_found,
104 .mode = mode,
105 .disabled = disabled,
106 };
107
108 /* Chwck if the mode is preferred */
109 if ((mode.type() & DRM_MODE_TYPE_PREFERRED) != 0 &&
110 preferred_config_id == 0) {
111 preferred_config_id = last_config_id;
112 preferred_config_group_id = group_found;
113 }
114
115 last_config_id++;
116 }
117
118 /* We must have preferred mode. Set first mode as preferred
119 * in case KMS haven't reported anything. */
120 if (preferred_config_id == 0) {
Roman Stratiienkoaa8ec522022-01-17 13:17:56 +0200121 preferred_config_id = first_config_id;
Roman Stratiienko0137f862022-01-04 18:27:40 +0200122 preferred_config_group_id = 1;
123 }
124
125 for (int group = 1; group < last_group_id; group++) {
126 bool has_interlaced = false;
127 bool has_progressive = false;
128 for (auto &hwc_config : hwc_configs) {
129 if (hwc_config.second.group_id != group || hwc_config.second.disabled) {
130 continue;
131 }
132
133 if (hwc_config.second.IsInterlaced()) {
134 has_interlaced = true;
135 } else {
136 has_progressive = true;
137 }
138 }
139
140 bool has_both = has_interlaced && has_progressive;
141 if (!has_both) {
142 continue;
143 }
144
145 bool group_contains_preferred_interlaced = false;
146 if (group == preferred_config_group_id &&
147 hwc_configs[preferred_config_id].IsInterlaced()) {
148 group_contains_preferred_interlaced = true;
149 }
150
151 for (auto &hwc_config : hwc_configs) {
152 if (hwc_config.second.group_id != group || hwc_config.second.disabled) {
153 continue;
154 }
155
156 bool disable = group_contains_preferred_interlaced
157 ? !hwc_config.second.IsInterlaced()
158 : hwc_config.second.IsInterlaced();
159
160 if (disable) {
161 ALOGI(
162 "Group %i: Disabling display mode %s (This group should consist "
163 "of %s modes)",
164 group, hwc_config.second.mode.name().c_str(),
165 group_contains_preferred_interlaced ? "interlaced" : "progressive");
166
167 hwc_config.second.disabled = true;
168 }
169 }
170 }
171
172 /* Group should not contain 2 modes with FPS delta less than ~1HZ
173 * otherwise android.graphics.cts.SetFrameRateTest CTS will fail
174 */
175 constexpr float kMinFpsDelta = 1.0; // FPS
Roman Stratiienkoaa8ec522022-01-17 13:17:56 +0200176 for (int m1 = first_config_id; m1 < last_config_id; m1++) {
177 for (int m2 = first_config_id; m2 < last_config_id; m2++) {
Roman Stratiienko0137f862022-01-04 18:27:40 +0200178 if (m1 != m2 && hwc_configs[m1].group_id == hwc_configs[m2].group_id &&
179 !hwc_configs[m1].disabled && !hwc_configs[m2].disabled &&
180 fabsf(hwc_configs[m1].mode.v_refresh() -
181 hwc_configs[m2].mode.v_refresh()) < kMinFpsDelta) {
182 ALOGI(
183 "Group %i: Disabling display mode %s (Refresh rate value is "
184 "too close to existing mode %s)",
185 hwc_configs[m2].group_id, hwc_configs[m2].mode.name().c_str(),
186 hwc_configs[m1].mode.name().c_str());
187
188 hwc_configs[m2].disabled = true;
189 }
190 }
191 }
192
Roman Stratiienkof0c507f2022-01-17 18:29:24 +0200193 /* Set active mode to be valid mode */
194 active_config_id = preferred_config_id;
Roman Stratiienko0137f862022-01-04 18:27:40 +0200195 return HWC2::Error::None;
196}
197
198} // namespace android