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 {
29     hwc_dpp_range.min = dpp_range.min;
30     hwc_dpp_range.max = dpp_range.max;
31     hwc_dpp_range.align = dpp_range.align;
32 }
33 
set_dpp_ch_restriction(struct hwc_dpp_ch_restriction & hwc_dpp_restriction,struct dpp_ch_restriction & drm_restriction)34 static void set_dpp_ch_restriction(struct hwc_dpp_ch_restriction &hwc_dpp_restriction,
35         struct dpp_ch_restriction &drm_restriction)
36 {
37     hwc_dpp_restriction.id = drm_restriction.id;
38     hwc_dpp_restriction.attr = drm_restriction.attr;
39     set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.src_f_w, drm_restriction.restriction.src_f_w);
40     set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.src_f_h, drm_restriction.restriction.src_f_h);
41     set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.src_w, drm_restriction.restriction.src_w);
42     set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.src_h, drm_restriction.restriction.src_h);
43     hwc_dpp_restriction.restriction.src_x_align = drm_restriction.restriction.src_x_align;
44     hwc_dpp_restriction.restriction.src_y_align = drm_restriction.restriction.src_y_align;
45     set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.dst_f_w, drm_restriction.restriction.dst_f_w);
46     set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.dst_f_h, drm_restriction.restriction.dst_f_h);
47     set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.dst_w, drm_restriction.restriction.dst_w);
48     set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.dst_h, drm_restriction.restriction.dst_h);
49     hwc_dpp_restriction.restriction.dst_x_align = drm_restriction.restriction.dst_x_align;
50     hwc_dpp_restriction.restriction.dst_y_align = drm_restriction.restriction.dst_y_align;
51     set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.blk_w, drm_restriction.restriction.blk_w);
52     set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.blk_h, drm_restriction.restriction.blk_h);
53     hwc_dpp_restriction.restriction.blk_x_align = drm_restriction.restriction.blk_x_align;
54     hwc_dpp_restriction.restriction.blk_y_align = drm_restriction.restriction.blk_y_align;
55     hwc_dpp_restriction.restriction.src_h_rot_max = drm_restriction.restriction.src_h_rot_max;
56     hwc_dpp_restriction.restriction.scale_down = drm_restriction.restriction.scale_down;
57     hwc_dpp_restriction.restriction.scale_up = drm_restriction.restriction.scale_up;
58 
59     /* scale ratio can't be 0 */
60     if (hwc_dpp_restriction.restriction.scale_down == 0)
61         hwc_dpp_restriction.restriction.scale_down = 1;
62     if (hwc_dpp_restriction.restriction.scale_up == 0)
63         hwc_dpp_restriction.restriction.scale_up = 1;
64 }
65 
66 using namespace SOC_VERSION;
67 
ExynosDeviceDrmInterface(ExynosDevice * exynosDevice)68 ExynosDeviceDrmInterface::ExynosDeviceDrmInterface(ExynosDevice *exynosDevice)
69 {
70     mType = INTERFACE_TYPE_DRM;
71 }
72 
~ExynosDeviceDrmInterface()73 ExynosDeviceDrmInterface::~ExynosDeviceDrmInterface()
74 {
75     mDrmDevice->event_listener()->UnRegisterHotplugHandler(static_cast<DrmEventHandler *>(&mExynosDrmEventHandler));
76     mDrmDevice->event_listener()->UnRegisterTUIHandler(static_cast<DrmTUIEventHandler *>(&mExynosDrmEventHandler));
77 }
78 
init(ExynosDevice * exynosDevice)79 void ExynosDeviceDrmInterface::init(ExynosDevice *exynosDevice)
80 {
81     mUseQuery = false;
82     mExynosDevice = exynosDevice;
83     mDrmResourceManager.Init();
84     mDrmDevice = mDrmResourceManager.GetDrmDevice(HWC_DISPLAY_PRIMARY);
85     assert(mDrmDevice != NULL);
86 
87     updateRestrictions();
88 
89     mExynosDrmEventHandler.init(mExynosDevice, mDrmDevice);
90     mDrmDevice->event_listener()->RegisterHotplugHandler(static_cast<DrmEventHandler *>(&mExynosDrmEventHandler));
91     mDrmDevice->event_listener()->RegisterTUIHandler(static_cast<DrmTUIEventHandler *>(&mExynosDrmEventHandler));
92 
93     if (mDrmDevice->event_listener()->IsDrmInTUI()) {
94         mExynosDevice->enterToTUI();
95         ALOGD("%s:: device is already in TUI", __func__);
96     }
97 }
98 
initDisplayInterface(std::unique_ptr<ExynosDisplayInterface> & dispInterface)99 int32_t ExynosDeviceDrmInterface::initDisplayInterface(
100          std::unique_ptr<ExynosDisplayInterface> &dispInterface)
101 {
102     ExynosDisplayDrmInterface *displayInterface =
103         static_cast<ExynosDisplayDrmInterface*>(dispInterface.get());
104     return displayInterface->initDrmDevice(mDrmDevice);
105 }
106 
updateRestrictions()107 void ExynosDeviceDrmInterface::updateRestrictions()
108 {
109     int32_t ret = 0;
110 
111     mDPUInfo.dpuInfo.dpp_chs.resize(mDrmDevice->planes().size());
112     uint32_t channelId = 0;
113 
114     for (auto &plane : mDrmDevice->planes()) {
115         /* Set size restriction information */
116         if (plane->hw_restrictions_property().id()) {
117             uint64_t blobId;
118             std::tie(ret, blobId) = plane->hw_restrictions_property().value();
119             if (ret)
120                 break;
121             struct dpp_ch_restriction *res;
122             drmModePropertyBlobPtr blob = drmModeGetPropertyBlob(mDrmDevice->fd(), blobId);
123             if (!blob) {
124                 ALOGE("Fail to get blob for hw_restrictions(%" PRId64 ")", blobId);
125                 ret = HWC2_ERROR_UNSUPPORTED;
126                 break;
127             }
128             res = (struct dpp_ch_restriction *)blob->data;
129             set_dpp_ch_restriction(mDPUInfo.dpuInfo.dpp_chs[channelId], *res);
130             drmModeFreePropertyBlob(blob);
131         } else {
132             ALOGI("plane[%d] There is no hw restriction information", channelId);
133             ret = HWC2_ERROR_UNSUPPORTED;
134             break;
135         }
136         /* Set supported format information */
137         for (auto format : plane->formats()) {
138             std::vector<uint32_t> halFormats;
139             if (drmFormatToHalFormats(format, &halFormats) != NO_ERROR) {
140                 ALOGE("Fail to convert drm format(%d)", format);
141                 continue;
142             }
143             for (auto halFormat : halFormats) {
144                 mDPUInfo.dpuInfo.dpp_chs[channelId].restriction.formats.push_back(halFormat);
145             }
146         }
147         if (hwcCheckDebugMessages(eDebugAttrSetting))
148             printDppRestriction(mDPUInfo.dpuInfo.dpp_chs[channelId]);
149 
150         channelId++;
151     }
152 
153     DrmCrtc *drmCrtc = mDrmDevice->GetCrtcForDisplay(0);
154     if (drmCrtc != nullptr) {
155         /*
156          * Run makeDPURestrictions() even if there is error
157          * in getting the value
158          */
159         if (drmCrtc->ppc_property().id()) {
160             auto [ret_ppc, value] = drmCrtc->ppc_property().value();
161             if (ret_ppc < 0) {
162                 ALOGE("Failed to get ppc property");
163             } else {
164                 mDPUInfo.dpuInfo.ppc = static_cast<uint32_t>(value);
165             }
166         }
167         if (drmCrtc->max_disp_freq_property().id()) {
168             auto [ret_max_freq, value] = drmCrtc->max_disp_freq_property().value();
169             if (ret_max_freq < 0) {
170                 ALOGE("Failed to get max_disp_freq property");
171             } else {
172                 mDPUInfo.dpuInfo.max_disp_freq = static_cast<uint32_t>(value);
173             }
174         }
175     } else {
176         ALOGE("%s:: Fail to get DrmCrtc", __func__);
177     }
178 
179     if (ret != NO_ERROR) {
180         ALOGI("Fail to get restriction (ret: %d)", ret);
181         mUseQuery = false;
182         return;
183     }
184 
185     if ((ret = makeDPURestrictions()) != NO_ERROR) {
186         ALOGE("makeDPURestrictions fail");
187     } else if ((ret = updateFeatureTable()) != NO_ERROR) {
188         ALOGE("updateFeatureTable fail");
189     }
190 
191     if (ret == NO_ERROR)
192         mUseQuery = true;
193     else {
194         ALOGI("There is no hw restriction information, use default values");
195         mUseQuery = false;
196     }
197 }
198 
init(ExynosDevice * exynosDevice,DrmDevice * drmDevice)199 void ExynosDeviceDrmInterface::ExynosDrmEventHandler::init(ExynosDevice *exynosDevice, DrmDevice *drmDevice)
200 {
201     mExynosDevice = exynosDevice;
202     mDrmDevice = drmDevice;
203 }
204 
HandleEvent(uint64_t timestamp_us)205 void ExynosDeviceDrmInterface::ExynosDrmEventHandler::HandleEvent(uint64_t timestamp_us)
206 {
207     hwc2_callback_data_t callbackData =
208         mExynosDevice->mCallbackInfos[HWC2_CALLBACK_HOTPLUG].callbackData;
209     HWC2_PFN_HOTPLUG callbackFunc =
210         (HWC2_PFN_HOTPLUG)mExynosDevice->mCallbackInfos[HWC2_CALLBACK_HOTPLUG].funcPointer;
211 
212     if (callbackData == NULL || callbackFunc == NULL)
213     {
214         ALOGE("%s:: callback info is NULL", __func__);
215         return;
216     }
217 
218     for (auto it : mExynosDevice->mDisplays) {
219         /* Call UpdateModes to get plug status */
220         uint32_t numConfigs;
221         it->getDisplayConfigs(&numConfigs, NULL);
222 
223         callbackFunc(callbackData, getDisplayId(it->mType, it->mIndex),
224                 it->mPlugState ? HWC2_CONNECTION_CONNECTED : HWC2_CONNECTION_DISCONNECTED);
225     }
226 
227     /* TODO: Check plug status hear or ExynosExternalDisplay::handleHotplugEvent() */
228     ExynosExternalDisplayModule *display =
229         static_cast<ExynosExternalDisplayModule*>(mExynosDevice->getDisplay(getDisplayId(HWC_DISPLAY_EXTERNAL, 0)));
230     if (display != NULL)
231         display->handleHotplugEvent();
232 }
233 
HandleTUIEvent()234 void ExynosDeviceDrmInterface::ExynosDrmEventHandler::HandleTUIEvent()
235 {
236     if (mDrmDevice->event_listener()->IsDrmInTUI()) {
237         /* Received TUI Enter event */
238         if (!mExynosDevice->isInTUI()) {
239             mExynosDevice->enterToTUI();
240             ALOGV("%s:: DRM device in TUI", __func__);
241         }
242     } else {
243         /* Received TUI Exit event */
244         if (mExynosDevice->isInTUI()) {
245             mExynosDevice->invalidate();
246             mExynosDevice->exitFromTUI();
247             ALOGV("%s:: DRM device out TUI", __func__);
248         }
249     }
250 }
251