1 /*
2 * Copyright (C) 2019 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 #include <drm/drm_mode.h>
18 #include "ExynosDeviceDrmInterface.h"
19 #include "ExynosDisplayDrmInterface.h"
20 #include "ExynosHWCDebug.h"
21 #include "ExynosDevice.h"
22 #include "ExynosDisplay.h"
23 #include "ExynosExternalDisplayModule.h"
24 #include <hardware/hwcomposer_defs.h>
25 #include <drm/samsung_drm.h>
26
set_hwc_dpp_size_range(hwc_dpp_size_range & hwc_dpp_range,dpp_size_range & dpp_range)27 void set_hwc_dpp_size_range(hwc_dpp_size_range &hwc_dpp_range, dpp_size_range &dpp_range) {
28 hwc_dpp_range.min = dpp_range.min;
29 hwc_dpp_range.max = dpp_range.max;
30 hwc_dpp_range.align = dpp_range.align;
31 }
32
set_dpp_ch_restriction(struct hwc_dpp_ch_restriction & hwc_dpp_restriction,struct dpp_ch_restriction & drm_restriction)33 static void set_dpp_ch_restriction(struct hwc_dpp_ch_restriction &hwc_dpp_restriction,
34 struct dpp_ch_restriction &drm_restriction) {
35 hwc_dpp_restriction.id = drm_restriction.id;
36 hwc_dpp_restriction.attr = drm_restriction.attr;
37 set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.src_f_w, drm_restriction.restriction.src_f_w);
38 set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.src_f_h, drm_restriction.restriction.src_f_h);
39 set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.src_w, drm_restriction.restriction.src_w);
40 set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.src_h, drm_restriction.restriction.src_h);
41 hwc_dpp_restriction.restriction.src_x_align = drm_restriction.restriction.src_x_align;
42 hwc_dpp_restriction.restriction.src_y_align = drm_restriction.restriction.src_y_align;
43 set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.dst_f_w, drm_restriction.restriction.dst_f_w);
44 set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.dst_f_h, drm_restriction.restriction.dst_f_h);
45 set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.dst_w, drm_restriction.restriction.dst_w);
46 set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.dst_h, drm_restriction.restriction.dst_h);
47 hwc_dpp_restriction.restriction.dst_x_align = drm_restriction.restriction.dst_x_align;
48 hwc_dpp_restriction.restriction.dst_y_align = drm_restriction.restriction.dst_y_align;
49 set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.blk_w, drm_restriction.restriction.blk_w);
50 set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.blk_h, drm_restriction.restriction.blk_h);
51 hwc_dpp_restriction.restriction.blk_x_align = drm_restriction.restriction.blk_x_align;
52 hwc_dpp_restriction.restriction.blk_y_align = drm_restriction.restriction.blk_y_align;
53 hwc_dpp_restriction.restriction.src_h_rot_max = drm_restriction.restriction.src_h_rot_max;
54 hwc_dpp_restriction.restriction.scale_down = drm_restriction.restriction.scale_down;
55 hwc_dpp_restriction.restriction.scale_up = drm_restriction.restriction.scale_up;
56
57 /* scale ratio can't be 0 */
58 if (hwc_dpp_restriction.restriction.scale_down == 0)
59 hwc_dpp_restriction.restriction.scale_down = 1;
60 if (hwc_dpp_restriction.restriction.scale_up == 0)
61 hwc_dpp_restriction.restriction.scale_up = 1;
62 }
63
64 using namespace SOC_VERSION;
65
ExynosDeviceDrmInterface(ExynosDevice * exynosDevice)66 ExynosDeviceDrmInterface::ExynosDeviceDrmInterface(ExynosDevice *exynosDevice) {
67 mType = INTERFACE_TYPE_DRM;
68 }
69
~ExynosDeviceDrmInterface()70 ExynosDeviceDrmInterface::~ExynosDeviceDrmInterface() {
71 mDrmDevice->event_listener()->UnRegisterHotplugHandler(
72 static_cast<DrmEventHandler *>(&mExynosDrmEventHandler));
73 mDrmDevice->event_listener()->UnRegisterHistogramHandler(
74 static_cast<DrmHistogramEventHandler *>(&mExynosDrmEventHandler));
75 mDrmDevice->event_listener()->UnRegisterTUIHandler(
76 static_cast<DrmTUIEventHandler *>(&mExynosDrmEventHandler));
77 mDrmDevice->event_listener()->UnRegisterPanelIdleHandler(
78 static_cast<DrmPanelIdleEventHandler *>(&mExynosDrmEventHandler));
79 }
80
init(ExynosDevice * exynosDevice)81 void ExynosDeviceDrmInterface::init(ExynosDevice *exynosDevice) {
82 mUseQuery = false;
83 mExynosDevice = exynosDevice;
84 mDrmResourceManager.Init();
85 mDrmDevice = mDrmResourceManager.GetDrmDevice(HWC_DISPLAY_PRIMARY);
86 assert(mDrmDevice != NULL);
87
88 updateRestrictions();
89
90 mExynosDrmEventHandler.init(mExynosDevice, mDrmDevice);
91 mDrmDevice->event_listener()->RegisterHotplugHandler(
92 static_cast<DrmEventHandler *>(&mExynosDrmEventHandler));
93 mDrmDevice->event_listener()->RegisterHistogramHandler(
94 static_cast<DrmHistogramEventHandler *>(&mExynosDrmEventHandler));
95 mDrmDevice->event_listener()->RegisterTUIHandler(
96 static_cast<DrmTUIEventHandler *>(&mExynosDrmEventHandler));
97 mDrmDevice->event_listener()->RegisterPanelIdleHandler(
98 static_cast<DrmPanelIdleEventHandler *>(&mExynosDrmEventHandler));
99
100 if (mDrmDevice->event_listener()->IsDrmInTUI()) {
101 mExynosDevice->enterToTUI();
102 ALOGD("%s:: device is already in TUI", __func__);
103 }
104 }
105
initDisplayInterface(std::unique_ptr<ExynosDisplayInterface> & dispInterface)106 int32_t ExynosDeviceDrmInterface::initDisplayInterface(
107 std::unique_ptr<ExynosDisplayInterface> &dispInterface) {
108 ExynosDisplayDrmInterface *displayInterface =
109 static_cast<ExynosDisplayDrmInterface*>(dispInterface.get());
110 return displayInterface->initDrmDevice(mDrmDevice);
111 }
112
updateRestrictions()113 void ExynosDeviceDrmInterface::updateRestrictions() {
114 int32_t ret = 0;
115 uint32_t channelId = 0;
116
117 for (auto &plane : mDrmDevice->planes()) {
118 struct hwc_dpp_ch_restriction hwc_res;
119
120 /* Set size restriction information */
121 if (plane->hw_restrictions_property().id()) {
122 uint64_t blobId;
123
124 std::tie(ret, blobId) = plane->hw_restrictions_property().value();
125 if (ret)
126 break;
127
128 struct dpp_ch_restriction *res;
129 drmModePropertyBlobPtr blob = drmModeGetPropertyBlob(mDrmDevice->fd(), blobId);
130 if (!blob) {
131 ALOGE("Fail to get blob for hw_restrictions(%" PRId64 ")", blobId);
132 ret = HWC2_ERROR_UNSUPPORTED;
133 break;
134 }
135 res = (struct dpp_ch_restriction *)blob->data;
136 set_dpp_ch_restriction(hwc_res, *res);
137 drmModeFreePropertyBlob(blob);
138 } else {
139 ALOGI("plane[%d] There is no hw restriction information", channelId);
140 ret = HWC2_ERROR_UNSUPPORTED;
141 break;
142 }
143
144 /* Set supported format information */
145 for (auto format : plane->formats()) {
146 std::vector<uint32_t> halFormats;
147 if (drmFormatToHalFormats(format, &halFormats) != NO_ERROR) {
148 ALOGE("Fail to convert drm format(%d)", format);
149 continue;
150 }
151 for (auto halFormat : halFormats) {
152 hwc_res.restriction.formats.push_back(halFormat);
153 }
154 }
155
156 if (hwcCheckDebugMessages(eDebugDefault))
157 printDppRestriction(hwc_res);
158
159 if (plane->isFormatSupported(DRM_FORMAT_C8) && plane->getNumFormatSupported() == 1)
160 mDPUInfo.dpuInfo.spp_chs.push_back(hwc_res);
161 else
162 mDPUInfo.dpuInfo.dpp_chs.push_back(hwc_res);
163
164 channelId++;
165 }
166
167 DrmCrtc *drmCrtc = mDrmDevice->GetCrtcForDisplay(0);
168 if (drmCrtc != nullptr) {
169 /*
170 * Run makeDPURestrictions() even if there is error
171 * in getting the value
172 */
173 if (drmCrtc->ppc_property().id()) {
174 auto [ret_ppc, value] = drmCrtc->ppc_property().value();
175 if (ret_ppc < 0) {
176 ALOGE("Failed to get ppc property");
177 } else {
178 mDPUInfo.dpuInfo.ppc = static_cast<uint32_t>(value);
179 }
180 }
181 if (drmCrtc->max_disp_freq_property().id()) {
182 auto [ret_max_freq, value] = drmCrtc->max_disp_freq_property().value();
183 if (ret_max_freq < 0) {
184 ALOGE("Failed to get max_disp_freq property");
185 } else {
186 mDPUInfo.dpuInfo.max_disp_freq = static_cast<uint32_t>(value);
187 }
188 }
189 } else {
190 ALOGE("%s:: Fail to get DrmCrtc", __func__);
191 }
192
193 if (ret != NO_ERROR) {
194 ALOGI("Fail to get restriction (ret: %d)", ret);
195 mUseQuery = false;
196 return;
197 }
198
199 if ((ret = makeDPURestrictions()) != NO_ERROR) {
200 ALOGE("makeDPURestrictions fail");
201 } else if ((ret = updateFeatureTable()) != NO_ERROR) {
202 ALOGE("updateFeatureTable fail");
203 }
204
205 if (ret == NO_ERROR)
206 mUseQuery = true;
207 else {
208 ALOGI("There is no hw restriction information, use default values");
209 mUseQuery = false;
210 }
211 }
212
init(ExynosDevice * exynosDevice,DrmDevice * drmDevice)213 void ExynosDeviceDrmInterface::ExynosDrmEventHandler::init(ExynosDevice *exynosDevice,
214 DrmDevice *drmDevice) {
215 mExynosDevice = exynosDevice;
216 mDrmDevice = drmDevice;
217 }
218
handleEvent(uint64_t timestamp_us)219 void ExynosDeviceDrmInterface::ExynosDrmEventHandler::handleEvent(uint64_t timestamp_us) {
220 if (!mExynosDevice->isCallbackAvailable(HWC2_CALLBACK_HOTPLUG)) {
221 return;
222 }
223
224 for (auto it : mExynosDevice->mDisplays) {
225 /* Call UpdateModes to get plug status */
226 uint32_t numConfigs;
227
228 it->getDisplayConfigs(&numConfigs, NULL);
229 mExynosDevice->onHotPlug(getDisplayId(it->mType, it->mIndex), it->mPlugState);
230 }
231
232 /* TODO: Check plug status hear or ExynosExternalDisplay::handleHotplugEvent() */
233 ExynosExternalDisplayModule *display =
234 static_cast<ExynosExternalDisplayModule*>(mExynosDevice->getDisplay(getDisplayId(HWC_DISPLAY_EXTERNAL, 0)));
235 if (display != NULL)
236 display->handleHotplugEvent();
237 }
238
handleHistogramEvent(uint32_t crtc_id,void * bin)239 void ExynosDeviceDrmInterface::ExynosDrmEventHandler::handleHistogramEvent(uint32_t crtc_id,
240 void *bin) {
241 ExynosDisplayDrmInterface *displayInterface;
242 DrmProperty crtc;
243 uint32_t id;
244
245 for (auto display : mExynosDevice->mDisplays) {
246 displayInterface =
247 static_cast<ExynosDisplayDrmInterface *>(display->mDisplayInterface.get());
248 id = displayInterface->getCrtcId();
249 if (id == crtc_id) {
250 displayInterface->setHistogramData(bin);
251 }
252 }
253 }
254
handleTUIEvent()255 void ExynosDeviceDrmInterface::ExynosDrmEventHandler::handleTUIEvent() {
256 if (mDrmDevice->event_listener()->IsDrmInTUI()) {
257 /* Received TUI Enter event */
258 if (!mExynosDevice->isInTUI()) {
259 mExynosDevice->enterToTUI();
260 ALOGV("%s:: DRM device in TUI", __func__);
261 }
262 } else {
263 /* Received TUI Exit event */
264 if (mExynosDevice->isInTUI()) {
265 mExynosDevice->onRefreshDisplays();
266 mExynosDevice->exitFromTUI();
267 ALOGV("%s:: DRM device out TUI", __func__);
268 }
269 }
270 }
271
272 constexpr size_t IDLE_ENTER_EVENT_DATA_SIZE = 3;
handleIdleEnterEvent(char const * event)273 void ExynosDeviceDrmInterface::ExynosDrmEventHandler::handleIdleEnterEvent(char const *event) {
274 /* PANEL_IDLE_ENTER=<display index>,<vrefresh>,<idle te vrefresh> */
275 std::string_view idle_event_str(event);
276 auto prefix_shift_pos = idle_event_str.find("=");
277 if (prefix_shift_pos == std::string::npos) {
278 ALOGE("%s: idle enter event format is incorrect", __func__);
279 }
280
281 int count = 0;
282 int value[IDLE_ENTER_EVENT_DATA_SIZE] = {0};
283 const auto &[displayIndex, vrefresh, idleTeVrefresh] = value;
284
285 auto start_pos = prefix_shift_pos + 1;
286 auto end_pos = idle_event_str.find(",", start_pos);
287 while (end_pos != std::string::npos && count < IDLE_ENTER_EVENT_DATA_SIZE) {
288 auto info = idle_event_str.substr(start_pos, end_pos - start_pos);
289
290 value[count++] = atoi(info.data());
291 start_pos = end_pos + 1;
292 end_pos = idle_event_str.find(",", start_pos);
293 if (end_pos == std::string::npos) {
294 info = idle_event_str.substr(start_pos, idle_event_str.size() - start_pos);
295 value[count++] = atoi(info.data());
296 }
297 }
298
299 if (count != IDLE_ENTER_EVENT_DATA_SIZE) {
300 ALOGE("%s: idle enter event is incomplete", __func__);
301 return;
302 }
303
304 ExynosDisplay *primaryDisplay =
305 mExynosDevice->getDisplay(getDisplayId(HWC_DISPLAY_PRIMARY, displayIndex));
306 if (primaryDisplay) {
307 /* sending vsyncIdle callback */
308 if (vrefresh != idleTeVrefresh) {
309 mExynosDevice->onVsyncIdle(primaryDisplay->getId());
310 }
311
312 primaryDisplay->handleDisplayIdleEnter(idleTeVrefresh);
313 }
314 }
315