1*0a9764feSAndroid Build Coastguard Worker /*
2*0a9764feSAndroid Build Coastguard Worker * Copyright (C) 2022 The Android Open Source Project
3*0a9764feSAndroid Build Coastguard Worker *
4*0a9764feSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*0a9764feSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*0a9764feSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*0a9764feSAndroid Build Coastguard Worker *
8*0a9764feSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*0a9764feSAndroid Build Coastguard Worker *
10*0a9764feSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*0a9764feSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*0a9764feSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*0a9764feSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*0a9764feSAndroid Build Coastguard Worker * limitations under the License.
15*0a9764feSAndroid Build Coastguard Worker */
16*0a9764feSAndroid Build Coastguard Worker
17*0a9764feSAndroid Build Coastguard Worker #define LOG_TAG "drmhwc"
18*0a9764feSAndroid Build Coastguard Worker
19*0a9764feSAndroid Build Coastguard Worker #include "HwcDisplayConfigs.h"
20*0a9764feSAndroid Build Coastguard Worker
21*0a9764feSAndroid Build Coastguard Worker #include <cmath>
22*0a9764feSAndroid Build Coastguard Worker #include <cstring>
23*0a9764feSAndroid Build Coastguard Worker
24*0a9764feSAndroid Build Coastguard Worker #include "drm/DrmConnector.h"
25*0a9764feSAndroid Build Coastguard Worker #include "utils/log.h"
26*0a9764feSAndroid Build Coastguard Worker #include "utils/properties.h"
27*0a9764feSAndroid Build Coastguard Worker
28*0a9764feSAndroid Build Coastguard Worker constexpr uint32_t kHeadlessModeDisplayWidthMm = 163;
29*0a9764feSAndroid Build Coastguard Worker constexpr uint32_t kHeadlessModeDisplayHeightMm = 122;
30*0a9764feSAndroid Build Coastguard Worker constexpr uint32_t kHeadlessModeDisplayWidthPx = 1024;
31*0a9764feSAndroid Build Coastguard Worker constexpr uint32_t kHeadlessModeDisplayHeightPx = 768;
32*0a9764feSAndroid Build Coastguard Worker constexpr uint32_t kHeadlessModeDisplayVRefresh = 60;
33*0a9764feSAndroid Build Coastguard Worker constexpr uint32_t kSyncLen = 10;
34*0a9764feSAndroid Build Coastguard Worker constexpr uint32_t kBackPorch = 10;
35*0a9764feSAndroid Build Coastguard Worker constexpr uint32_t kFrontPorch = 10;
36*0a9764feSAndroid Build Coastguard Worker constexpr uint32_t kHzInKHz = 1000;
37*0a9764feSAndroid Build Coastguard Worker
38*0a9764feSAndroid Build Coastguard Worker namespace android {
39*0a9764feSAndroid Build Coastguard Worker
40*0a9764feSAndroid Build Coastguard Worker // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
41*0a9764feSAndroid Build Coastguard Worker uint32_t HwcDisplayConfigs::last_config_id = 1;
42*0a9764feSAndroid Build Coastguard Worker
GenFakeMode(uint16_t width,uint16_t height)43*0a9764feSAndroid Build Coastguard Worker void HwcDisplayConfigs::GenFakeMode(uint16_t width, uint16_t height) {
44*0a9764feSAndroid Build Coastguard Worker hwc_configs.clear();
45*0a9764feSAndroid Build Coastguard Worker
46*0a9764feSAndroid Build Coastguard Worker last_config_id++;
47*0a9764feSAndroid Build Coastguard Worker preferred_config_id = active_config_id = last_config_id;
48*0a9764feSAndroid Build Coastguard Worker auto headless_drm_mode_info = (drmModeModeInfo){
49*0a9764feSAndroid Build Coastguard Worker .hdisplay = width,
50*0a9764feSAndroid Build Coastguard Worker .vdisplay = height,
51*0a9764feSAndroid Build Coastguard Worker .vrefresh = kHeadlessModeDisplayVRefresh,
52*0a9764feSAndroid Build Coastguard Worker .name = "VIRTUAL-MODE",
53*0a9764feSAndroid Build Coastguard Worker };
54*0a9764feSAndroid Build Coastguard Worker
55*0a9764feSAndroid Build Coastguard Worker if (width == 0 || height == 0) {
56*0a9764feSAndroid Build Coastguard Worker strcpy(headless_drm_mode_info.name, "HEADLESS-MODE");
57*0a9764feSAndroid Build Coastguard Worker headless_drm_mode_info.hdisplay = kHeadlessModeDisplayWidthPx;
58*0a9764feSAndroid Build Coastguard Worker headless_drm_mode_info.vdisplay = kHeadlessModeDisplayHeightPx;
59*0a9764feSAndroid Build Coastguard Worker }
60*0a9764feSAndroid Build Coastguard Worker
61*0a9764feSAndroid Build Coastguard Worker /* We need a valid mode to pass the kernel validation */
62*0a9764feSAndroid Build Coastguard Worker
63*0a9764feSAndroid Build Coastguard Worker headless_drm_mode_info.hsync_start = headless_drm_mode_info.hdisplay +
64*0a9764feSAndroid Build Coastguard Worker kFrontPorch;
65*0a9764feSAndroid Build Coastguard Worker headless_drm_mode_info.hsync_end = headless_drm_mode_info.hsync_start +
66*0a9764feSAndroid Build Coastguard Worker kSyncLen;
67*0a9764feSAndroid Build Coastguard Worker headless_drm_mode_info.htotal = headless_drm_mode_info.hsync_end + kBackPorch;
68*0a9764feSAndroid Build Coastguard Worker
69*0a9764feSAndroid Build Coastguard Worker headless_drm_mode_info.vsync_start = headless_drm_mode_info.vdisplay +
70*0a9764feSAndroid Build Coastguard Worker kFrontPorch;
71*0a9764feSAndroid Build Coastguard Worker headless_drm_mode_info.vsync_end = headless_drm_mode_info.vsync_start +
72*0a9764feSAndroid Build Coastguard Worker kSyncLen;
73*0a9764feSAndroid Build Coastguard Worker headless_drm_mode_info.vtotal = headless_drm_mode_info.vsync_end + kBackPorch;
74*0a9764feSAndroid Build Coastguard Worker
75*0a9764feSAndroid Build Coastguard Worker headless_drm_mode_info.clock = (headless_drm_mode_info.htotal *
76*0a9764feSAndroid Build Coastguard Worker headless_drm_mode_info.vtotal *
77*0a9764feSAndroid Build Coastguard Worker headless_drm_mode_info.vrefresh) /
78*0a9764feSAndroid Build Coastguard Worker kHzInKHz;
79*0a9764feSAndroid Build Coastguard Worker
80*0a9764feSAndroid Build Coastguard Worker hwc_configs[active_config_id] = (HwcDisplayConfig){
81*0a9764feSAndroid Build Coastguard Worker .id = active_config_id,
82*0a9764feSAndroid Build Coastguard Worker .group_id = 1,
83*0a9764feSAndroid Build Coastguard Worker .mode = DrmMode(&headless_drm_mode_info),
84*0a9764feSAndroid Build Coastguard Worker };
85*0a9764feSAndroid Build Coastguard Worker
86*0a9764feSAndroid Build Coastguard Worker mm_width = kHeadlessModeDisplayWidthMm;
87*0a9764feSAndroid Build Coastguard Worker mm_height = kHeadlessModeDisplayHeightMm;
88*0a9764feSAndroid Build Coastguard Worker }
89*0a9764feSAndroid Build Coastguard Worker
90*0a9764feSAndroid Build Coastguard Worker // NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme
Update(DrmConnector & connector)91*0a9764feSAndroid Build Coastguard Worker HWC2::Error HwcDisplayConfigs::Update(DrmConnector &connector) {
92*0a9764feSAndroid Build Coastguard Worker /* In case UpdateModes will fail we will still have one mode for headless
93*0a9764feSAndroid Build Coastguard Worker * mode
94*0a9764feSAndroid Build Coastguard Worker */
95*0a9764feSAndroid Build Coastguard Worker GenFakeMode(0, 0);
96*0a9764feSAndroid Build Coastguard Worker /* Read real configs */
97*0a9764feSAndroid Build Coastguard Worker auto ret = connector.UpdateModes();
98*0a9764feSAndroid Build Coastguard Worker if (ret != 0) {
99*0a9764feSAndroid Build Coastguard Worker ALOGE("Failed to update display modes %d", ret);
100*0a9764feSAndroid Build Coastguard Worker return HWC2::Error::BadDisplay;
101*0a9764feSAndroid Build Coastguard Worker }
102*0a9764feSAndroid Build Coastguard Worker
103*0a9764feSAndroid Build Coastguard Worker if (connector.GetModes().empty()) {
104*0a9764feSAndroid Build Coastguard Worker ALOGE("No modes reported by KMS");
105*0a9764feSAndroid Build Coastguard Worker return HWC2::Error::BadDisplay;
106*0a9764feSAndroid Build Coastguard Worker }
107*0a9764feSAndroid Build Coastguard Worker
108*0a9764feSAndroid Build Coastguard Worker hwc_configs.clear();
109*0a9764feSAndroid Build Coastguard Worker mm_width = connector.GetMmWidth();
110*0a9764feSAndroid Build Coastguard Worker mm_height = connector.GetMmHeight();
111*0a9764feSAndroid Build Coastguard Worker
112*0a9764feSAndroid Build Coastguard Worker preferred_config_id = 0;
113*0a9764feSAndroid Build Coastguard Worker uint32_t preferred_config_group_id = 0;
114*0a9764feSAndroid Build Coastguard Worker
115*0a9764feSAndroid Build Coastguard Worker auto first_config_id = last_config_id;
116*0a9764feSAndroid Build Coastguard Worker uint32_t last_group_id = 1;
117*0a9764feSAndroid Build Coastguard Worker const bool use_config_groups = Properties::UseConfigGroups();
118*0a9764feSAndroid Build Coastguard Worker
119*0a9764feSAndroid Build Coastguard Worker /* Group modes */
120*0a9764feSAndroid Build Coastguard Worker for (const auto &mode : connector.GetModes()) {
121*0a9764feSAndroid Build Coastguard Worker /* Find group for the new mode or create new group */
122*0a9764feSAndroid Build Coastguard Worker uint32_t group_found = 0;
123*0a9764feSAndroid Build Coastguard Worker if (use_config_groups) {
124*0a9764feSAndroid Build Coastguard Worker for (auto &hwc_config : hwc_configs) {
125*0a9764feSAndroid Build Coastguard Worker if (mode.GetRawMode().hdisplay ==
126*0a9764feSAndroid Build Coastguard Worker hwc_config.second.mode.GetRawMode().hdisplay &&
127*0a9764feSAndroid Build Coastguard Worker mode.GetRawMode().vdisplay ==
128*0a9764feSAndroid Build Coastguard Worker hwc_config.second.mode.GetRawMode().vdisplay) {
129*0a9764feSAndroid Build Coastguard Worker group_found = hwc_config.second.group_id;
130*0a9764feSAndroid Build Coastguard Worker }
131*0a9764feSAndroid Build Coastguard Worker }
132*0a9764feSAndroid Build Coastguard Worker }
133*0a9764feSAndroid Build Coastguard Worker if (group_found == 0) {
134*0a9764feSAndroid Build Coastguard Worker group_found = last_group_id++;
135*0a9764feSAndroid Build Coastguard Worker }
136*0a9764feSAndroid Build Coastguard Worker
137*0a9764feSAndroid Build Coastguard Worker bool disabled = false;
138*0a9764feSAndroid Build Coastguard Worker if ((mode.GetRawMode().flags & DRM_MODE_FLAG_3D_MASK) != 0) {
139*0a9764feSAndroid Build Coastguard Worker ALOGI("Disabling display mode %s (Modes with 3D flag aren't supported)",
140*0a9764feSAndroid Build Coastguard Worker mode.GetName().c_str());
141*0a9764feSAndroid Build Coastguard Worker disabled = true;
142*0a9764feSAndroid Build Coastguard Worker }
143*0a9764feSAndroid Build Coastguard Worker
144*0a9764feSAndroid Build Coastguard Worker /* Add config */
145*0a9764feSAndroid Build Coastguard Worker hwc_configs[last_config_id] = {
146*0a9764feSAndroid Build Coastguard Worker .id = last_config_id,
147*0a9764feSAndroid Build Coastguard Worker .group_id = group_found,
148*0a9764feSAndroid Build Coastguard Worker .mode = mode,
149*0a9764feSAndroid Build Coastguard Worker .disabled = disabled,
150*0a9764feSAndroid Build Coastguard Worker };
151*0a9764feSAndroid Build Coastguard Worker
152*0a9764feSAndroid Build Coastguard Worker /* Chwck if the mode is preferred */
153*0a9764feSAndroid Build Coastguard Worker if ((mode.GetRawMode().type & DRM_MODE_TYPE_PREFERRED) != 0 &&
154*0a9764feSAndroid Build Coastguard Worker preferred_config_id == 0) {
155*0a9764feSAndroid Build Coastguard Worker preferred_config_id = last_config_id;
156*0a9764feSAndroid Build Coastguard Worker preferred_config_group_id = group_found;
157*0a9764feSAndroid Build Coastguard Worker }
158*0a9764feSAndroid Build Coastguard Worker
159*0a9764feSAndroid Build Coastguard Worker last_config_id++;
160*0a9764feSAndroid Build Coastguard Worker }
161*0a9764feSAndroid Build Coastguard Worker
162*0a9764feSAndroid Build Coastguard Worker /* We must have preferred mode. Set first mode as preferred
163*0a9764feSAndroid Build Coastguard Worker * in case KMS haven't reported anything. */
164*0a9764feSAndroid Build Coastguard Worker if (preferred_config_id == 0) {
165*0a9764feSAndroid Build Coastguard Worker preferred_config_id = first_config_id;
166*0a9764feSAndroid Build Coastguard Worker preferred_config_group_id = 1;
167*0a9764feSAndroid Build Coastguard Worker }
168*0a9764feSAndroid Build Coastguard Worker
169*0a9764feSAndroid Build Coastguard Worker for (uint32_t group = 1; group < last_group_id; group++) {
170*0a9764feSAndroid Build Coastguard Worker bool has_interlaced = false;
171*0a9764feSAndroid Build Coastguard Worker bool has_progressive = false;
172*0a9764feSAndroid Build Coastguard Worker for (auto &hwc_config : hwc_configs) {
173*0a9764feSAndroid Build Coastguard Worker if (hwc_config.second.group_id != group || hwc_config.second.disabled) {
174*0a9764feSAndroid Build Coastguard Worker continue;
175*0a9764feSAndroid Build Coastguard Worker }
176*0a9764feSAndroid Build Coastguard Worker
177*0a9764feSAndroid Build Coastguard Worker if (hwc_config.second.IsInterlaced()) {
178*0a9764feSAndroid Build Coastguard Worker has_interlaced = true;
179*0a9764feSAndroid Build Coastguard Worker } else {
180*0a9764feSAndroid Build Coastguard Worker has_progressive = true;
181*0a9764feSAndroid Build Coastguard Worker }
182*0a9764feSAndroid Build Coastguard Worker }
183*0a9764feSAndroid Build Coastguard Worker
184*0a9764feSAndroid Build Coastguard Worker auto has_both = has_interlaced && has_progressive;
185*0a9764feSAndroid Build Coastguard Worker if (!has_both) {
186*0a9764feSAndroid Build Coastguard Worker continue;
187*0a9764feSAndroid Build Coastguard Worker }
188*0a9764feSAndroid Build Coastguard Worker
189*0a9764feSAndroid Build Coastguard Worker bool group_contains_preferred_interlaced = false;
190*0a9764feSAndroid Build Coastguard Worker if (group == preferred_config_group_id &&
191*0a9764feSAndroid Build Coastguard Worker hwc_configs[preferred_config_id].IsInterlaced()) {
192*0a9764feSAndroid Build Coastguard Worker group_contains_preferred_interlaced = true;
193*0a9764feSAndroid Build Coastguard Worker }
194*0a9764feSAndroid Build Coastguard Worker
195*0a9764feSAndroid Build Coastguard Worker for (auto &hwc_config : hwc_configs) {
196*0a9764feSAndroid Build Coastguard Worker if (hwc_config.second.group_id != group || hwc_config.second.disabled) {
197*0a9764feSAndroid Build Coastguard Worker continue;
198*0a9764feSAndroid Build Coastguard Worker }
199*0a9764feSAndroid Build Coastguard Worker
200*0a9764feSAndroid Build Coastguard Worker auto disable = group_contains_preferred_interlaced
201*0a9764feSAndroid Build Coastguard Worker ? !hwc_config.second.IsInterlaced()
202*0a9764feSAndroid Build Coastguard Worker : hwc_config.second.IsInterlaced();
203*0a9764feSAndroid Build Coastguard Worker
204*0a9764feSAndroid Build Coastguard Worker if (disable) {
205*0a9764feSAndroid Build Coastguard Worker ALOGI(
206*0a9764feSAndroid Build Coastguard Worker "Group %i: Disabling display mode %s (This group should consist "
207*0a9764feSAndroid Build Coastguard Worker "of %s modes)",
208*0a9764feSAndroid Build Coastguard Worker group, hwc_config.second.mode.GetName().c_str(),
209*0a9764feSAndroid Build Coastguard Worker group_contains_preferred_interlaced ? "interlaced" : "progressive");
210*0a9764feSAndroid Build Coastguard Worker
211*0a9764feSAndroid Build Coastguard Worker hwc_config.second.disabled = true;
212*0a9764feSAndroid Build Coastguard Worker }
213*0a9764feSAndroid Build Coastguard Worker }
214*0a9764feSAndroid Build Coastguard Worker }
215*0a9764feSAndroid Build Coastguard Worker
216*0a9764feSAndroid Build Coastguard Worker /* Group should not contain 2 modes with FPS delta less than ~1HZ
217*0a9764feSAndroid Build Coastguard Worker * otherwise android.graphics.cts.SetFrameRateTest CTS will fail
218*0a9764feSAndroid Build Coastguard Worker */
219*0a9764feSAndroid Build Coastguard Worker constexpr float kMinFpsDelta = 1.0; // FPS
220*0a9764feSAndroid Build Coastguard Worker for (uint32_t m1 = first_config_id; m1 < last_config_id; m1++) {
221*0a9764feSAndroid Build Coastguard Worker for (uint32_t m2 = first_config_id; m2 < last_config_id; m2++) {
222*0a9764feSAndroid Build Coastguard Worker if (m1 != m2 && hwc_configs[m1].group_id == hwc_configs[m2].group_id &&
223*0a9764feSAndroid Build Coastguard Worker !hwc_configs[m1].disabled && !hwc_configs[m2].disabled &&
224*0a9764feSAndroid Build Coastguard Worker fabsf(hwc_configs[m1].mode.GetVRefresh() -
225*0a9764feSAndroid Build Coastguard Worker hwc_configs[m2].mode.GetVRefresh()) < kMinFpsDelta) {
226*0a9764feSAndroid Build Coastguard Worker ALOGI(
227*0a9764feSAndroid Build Coastguard Worker "Group %i: Disabling display mode %s (Refresh rate value is "
228*0a9764feSAndroid Build Coastguard Worker "too close to existing mode %s)",
229*0a9764feSAndroid Build Coastguard Worker hwc_configs[m2].group_id, hwc_configs[m2].mode.GetName().c_str(),
230*0a9764feSAndroid Build Coastguard Worker hwc_configs[m1].mode.GetName().c_str());
231*0a9764feSAndroid Build Coastguard Worker
232*0a9764feSAndroid Build Coastguard Worker hwc_configs[m2].disabled = true;
233*0a9764feSAndroid Build Coastguard Worker }
234*0a9764feSAndroid Build Coastguard Worker }
235*0a9764feSAndroid Build Coastguard Worker }
236*0a9764feSAndroid Build Coastguard Worker
237*0a9764feSAndroid Build Coastguard Worker return HWC2::Error::None;
238*0a9764feSAndroid Build Coastguard Worker }
239*0a9764feSAndroid Build Coastguard Worker
240*0a9764feSAndroid Build Coastguard Worker } // namespace android
241