• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <cstring>
23 
24 #include "drm/DrmConnector.h"
25 #include "utils/log.h"
26 
27 constexpr uint32_t kHeadlessModeDisplayWidthMm = 163;
28 constexpr uint32_t kHeadlessModeDisplayHeightMm = 122;
29 constexpr uint32_t kHeadlessModeDisplayWidthPx = 1024;
30 constexpr uint32_t kHeadlessModeDisplayHeightPx = 768;
31 constexpr uint32_t kHeadlessModeDisplayVRefresh = 60;
32 constexpr uint32_t kSyncLen = 10;
33 constexpr uint32_t kBackPorch = 10;
34 constexpr uint32_t kFrontPorch = 10;
35 constexpr uint32_t kHzInKHz = 1000;
36 
37 namespace android {
38 
39 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
40 uint32_t HwcDisplayConfigs::last_config_id = 1;
41 
GenFakeMode(uint16_t width,uint16_t height)42 void HwcDisplayConfigs::GenFakeMode(uint16_t width, uint16_t height) {
43   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){
48       .hdisplay = width,
49       .vdisplay = height,
50       .vrefresh = kHeadlessModeDisplayVRefresh,
51       .name = "VIRTUAL-MODE",
52   };
53 
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 
79   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;
87 }
88 
89 // NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme
Update(DrmConnector & connector)90 HWC2::Error HwcDisplayConfigs::Update(DrmConnector &connector) {
91   /* In case UpdateModes will fail we will still have one mode for headless
92    * mode
93    */
94   GenFakeMode(0, 0);
95   /* Read real configs */
96   auto ret = connector.UpdateModes();
97   if (ret != 0) {
98     ALOGE("Failed to update display modes %d", ret);
99     return HWC2::Error::BadDisplay;
100   }
101 
102   if (connector.GetModes().empty()) {
103     ALOGE("No modes reported by KMS");
104     return HWC2::Error::BadDisplay;
105   }
106 
107   hwc_configs.clear();
108   mm_width = connector.GetMmWidth();
109   mm_height = connector.GetMmHeight();
110 
111   preferred_config_id = 0;
112   uint32_t preferred_config_group_id = 0;
113 
114   auto first_config_id = last_config_id;
115   uint32_t last_group_id = 1;
116 
117   /* Group modes */
118   for (const auto &mode : connector.GetModes()) {
119     /* Find group for the new mode or create new group */
120     uint32_t group_found = 0;
121     for (auto &hwc_config : hwc_configs) {
122       if (mode.GetRawMode().hdisplay ==
123               hwc_config.second.mode.GetRawMode().hdisplay &&
124           mode.GetRawMode().vdisplay ==
125               hwc_config.second.mode.GetRawMode().vdisplay) {
126         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;
134     if ((mode.GetRawMode().flags & DRM_MODE_FLAG_3D_MASK) != 0) {
135       ALOGI("Disabling display mode %s (Modes with 3D flag aren't supported)",
136             mode.GetName().c_str());
137       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 */
149     if ((mode.GetRawMode().type & DRM_MODE_TYPE_PREFERRED) != 0 &&
150         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) {
161     preferred_config_id = first_config_id;
162     preferred_config_group_id = 1;
163   }
164 
165   for (uint32_t group = 1; group < last_group_id; group++) {
166     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 
180     auto has_both = has_interlaced && has_progressive;
181     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 
196       auto disable = group_contains_preferred_interlaced
197                          ? !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)",
204             group, hwc_config.second.mode.GetName().c_str(),
205             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
216   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++) {
218       if (m1 != m2 && hwc_configs[m1].group_id == hwc_configs[m2].group_id &&
219           !hwc_configs[m1].disabled && !hwc_configs[m2].disabled &&
220           fabsf(hwc_configs[m1].mode.GetVRefresh() -
221                 hwc_configs[m2].mode.GetVRefresh()) < kMinFpsDelta) {
222         ALOGI(
223             "Group %i: Disabling display mode %s (Refresh rate value is "
224             "too close to existing mode %s)",
225             hwc_configs[m2].group_id, hwc_configs[m2].mode.GetName().c_str(),
226             hwc_configs[m1].mode.GetName().c_str());
227 
228         hwc_configs[m2].disabled = true;
229       }
230     }
231   }
232 
233   return HWC2::Error::None;
234 }
235 
236 }  // namespace android
237