• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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