xref: /aosp_15_r20/external/drm_hwcomposer/hwc2_device/HwcDisplayConfigs.cpp (revision 0a9764fe0a15e71ebbeb85e87e10990c23aab47f)
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