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