android13/hardware/google/graphics/common/libhwc2.1/libdisplayinterface/ExynosDeviceDrmInterface.cpp

315 lines
12 KiB
C++

/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <drm/drm_mode.h>
#include "ExynosDeviceDrmInterface.h"
#include "ExynosDisplayDrmInterface.h"
#include "ExynosHWCDebug.h"
#include "ExynosDevice.h"
#include "ExynosDisplay.h"
#include "ExynosExternalDisplayModule.h"
#include <hardware/hwcomposer_defs.h>
#include <drm/samsung_drm.h>
void set_hwc_dpp_size_range(hwc_dpp_size_range &hwc_dpp_range, dpp_size_range &dpp_range) {
hwc_dpp_range.min = dpp_range.min;
hwc_dpp_range.max = dpp_range.max;
hwc_dpp_range.align = dpp_range.align;
}
static void set_dpp_ch_restriction(struct hwc_dpp_ch_restriction &hwc_dpp_restriction,
struct dpp_ch_restriction &drm_restriction) {
hwc_dpp_restriction.id = drm_restriction.id;
hwc_dpp_restriction.attr = drm_restriction.attr;
set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.src_f_w, drm_restriction.restriction.src_f_w);
set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.src_f_h, drm_restriction.restriction.src_f_h);
set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.src_w, drm_restriction.restriction.src_w);
set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.src_h, drm_restriction.restriction.src_h);
hwc_dpp_restriction.restriction.src_x_align = drm_restriction.restriction.src_x_align;
hwc_dpp_restriction.restriction.src_y_align = drm_restriction.restriction.src_y_align;
set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.dst_f_w, drm_restriction.restriction.dst_f_w);
set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.dst_f_h, drm_restriction.restriction.dst_f_h);
set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.dst_w, drm_restriction.restriction.dst_w);
set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.dst_h, drm_restriction.restriction.dst_h);
hwc_dpp_restriction.restriction.dst_x_align = drm_restriction.restriction.dst_x_align;
hwc_dpp_restriction.restriction.dst_y_align = drm_restriction.restriction.dst_y_align;
set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.blk_w, drm_restriction.restriction.blk_w);
set_hwc_dpp_size_range(hwc_dpp_restriction.restriction.blk_h, drm_restriction.restriction.blk_h);
hwc_dpp_restriction.restriction.blk_x_align = drm_restriction.restriction.blk_x_align;
hwc_dpp_restriction.restriction.blk_y_align = drm_restriction.restriction.blk_y_align;
hwc_dpp_restriction.restriction.src_h_rot_max = drm_restriction.restriction.src_h_rot_max;
hwc_dpp_restriction.restriction.scale_down = drm_restriction.restriction.scale_down;
hwc_dpp_restriction.restriction.scale_up = drm_restriction.restriction.scale_up;
/* scale ratio can't be 0 */
if (hwc_dpp_restriction.restriction.scale_down == 0)
hwc_dpp_restriction.restriction.scale_down = 1;
if (hwc_dpp_restriction.restriction.scale_up == 0)
hwc_dpp_restriction.restriction.scale_up = 1;
}
using namespace SOC_VERSION;
ExynosDeviceDrmInterface::ExynosDeviceDrmInterface(ExynosDevice *exynosDevice) {
mType = INTERFACE_TYPE_DRM;
}
ExynosDeviceDrmInterface::~ExynosDeviceDrmInterface() {
mDrmDevice->event_listener()->UnRegisterHotplugHandler(
static_cast<DrmEventHandler *>(&mExynosDrmEventHandler));
mDrmDevice->event_listener()->UnRegisterHistogramHandler(
static_cast<DrmHistogramEventHandler *>(&mExynosDrmEventHandler));
mDrmDevice->event_listener()->UnRegisterTUIHandler(
static_cast<DrmTUIEventHandler *>(&mExynosDrmEventHandler));
mDrmDevice->event_listener()->UnRegisterPanelIdleHandler(
static_cast<DrmPanelIdleEventHandler *>(&mExynosDrmEventHandler));
}
void ExynosDeviceDrmInterface::init(ExynosDevice *exynosDevice) {
mUseQuery = false;
mExynosDevice = exynosDevice;
mDrmResourceManager.Init();
mDrmDevice = mDrmResourceManager.GetDrmDevice(HWC_DISPLAY_PRIMARY);
assert(mDrmDevice != NULL);
updateRestrictions();
mExynosDrmEventHandler.init(mExynosDevice, mDrmDevice);
mDrmDevice->event_listener()->RegisterHotplugHandler(
static_cast<DrmEventHandler *>(&mExynosDrmEventHandler));
mDrmDevice->event_listener()->RegisterHistogramHandler(
static_cast<DrmHistogramEventHandler *>(&mExynosDrmEventHandler));
mDrmDevice->event_listener()->RegisterTUIHandler(
static_cast<DrmTUIEventHandler *>(&mExynosDrmEventHandler));
mDrmDevice->event_listener()->RegisterPanelIdleHandler(
static_cast<DrmPanelIdleEventHandler *>(&mExynosDrmEventHandler));
if (mDrmDevice->event_listener()->IsDrmInTUI()) {
mExynosDevice->enterToTUI();
ALOGD("%s:: device is already in TUI", __func__);
}
}
int32_t ExynosDeviceDrmInterface::initDisplayInterface(
std::unique_ptr<ExynosDisplayInterface> &dispInterface) {
ExynosDisplayDrmInterface *displayInterface =
static_cast<ExynosDisplayDrmInterface*>(dispInterface.get());
return displayInterface->initDrmDevice(mDrmDevice);
}
void ExynosDeviceDrmInterface::updateRestrictions() {
int32_t ret = 0;
uint32_t channelId = 0;
for (auto &plane : mDrmDevice->planes()) {
struct hwc_dpp_ch_restriction hwc_res;
/* Set size restriction information */
if (plane->hw_restrictions_property().id()) {
uint64_t blobId;
std::tie(ret, blobId) = plane->hw_restrictions_property().value();
if (ret)
break;
struct dpp_ch_restriction *res;
drmModePropertyBlobPtr blob = drmModeGetPropertyBlob(mDrmDevice->fd(), blobId);
if (!blob) {
ALOGE("Fail to get blob for hw_restrictions(%" PRId64 ")", blobId);
ret = HWC2_ERROR_UNSUPPORTED;
break;
}
res = (struct dpp_ch_restriction *)blob->data;
set_dpp_ch_restriction(hwc_res, *res);
drmModeFreePropertyBlob(blob);
} else {
ALOGI("plane[%d] There is no hw restriction information", channelId);
ret = HWC2_ERROR_UNSUPPORTED;
break;
}
/* Set supported format information */
for (auto format : plane->formats()) {
std::vector<uint32_t> halFormats;
if (drmFormatToHalFormats(format, &halFormats) != NO_ERROR) {
ALOGE("Fail to convert drm format(%d)", format);
continue;
}
for (auto halFormat : halFormats) {
hwc_res.restriction.formats.push_back(halFormat);
}
}
if (hwcCheckDebugMessages(eDebugDefault))
printDppRestriction(hwc_res);
if (plane->isFormatSupported(DRM_FORMAT_C8) && plane->getNumFormatSupported() == 1)
mDPUInfo.dpuInfo.spp_chs.push_back(hwc_res);
else
mDPUInfo.dpuInfo.dpp_chs.push_back(hwc_res);
channelId++;
}
DrmCrtc *drmCrtc = mDrmDevice->GetCrtcForDisplay(0);
if (drmCrtc != nullptr) {
/*
* Run makeDPURestrictions() even if there is error
* in getting the value
*/
if (drmCrtc->ppc_property().id()) {
auto [ret_ppc, value] = drmCrtc->ppc_property().value();
if (ret_ppc < 0) {
ALOGE("Failed to get ppc property");
} else {
mDPUInfo.dpuInfo.ppc = static_cast<uint32_t>(value);
}
}
if (drmCrtc->max_disp_freq_property().id()) {
auto [ret_max_freq, value] = drmCrtc->max_disp_freq_property().value();
if (ret_max_freq < 0) {
ALOGE("Failed to get max_disp_freq property");
} else {
mDPUInfo.dpuInfo.max_disp_freq = static_cast<uint32_t>(value);
}
}
} else {
ALOGE("%s:: Fail to get DrmCrtc", __func__);
}
if (ret != NO_ERROR) {
ALOGI("Fail to get restriction (ret: %d)", ret);
mUseQuery = false;
return;
}
if ((ret = makeDPURestrictions()) != NO_ERROR) {
ALOGE("makeDPURestrictions fail");
} else if ((ret = updateFeatureTable()) != NO_ERROR) {
ALOGE("updateFeatureTable fail");
}
if (ret == NO_ERROR)
mUseQuery = true;
else {
ALOGI("There is no hw restriction information, use default values");
mUseQuery = false;
}
}
void ExynosDeviceDrmInterface::ExynosDrmEventHandler::init(ExynosDevice *exynosDevice,
DrmDevice *drmDevice) {
mExynosDevice = exynosDevice;
mDrmDevice = drmDevice;
}
void ExynosDeviceDrmInterface::ExynosDrmEventHandler::handleEvent(uint64_t timestamp_us) {
if (!mExynosDevice->isCallbackAvailable(HWC2_CALLBACK_HOTPLUG)) {
return;
}
for (auto it : mExynosDevice->mDisplays) {
/* Call UpdateModes to get plug status */
uint32_t numConfigs;
it->getDisplayConfigs(&numConfigs, NULL);
mExynosDevice->onHotPlug(getDisplayId(it->mType, it->mIndex), it->mPlugState);
}
/* TODO: Check plug status hear or ExynosExternalDisplay::handleHotplugEvent() */
ExynosExternalDisplayModule *display =
static_cast<ExynosExternalDisplayModule*>(mExynosDevice->getDisplay(getDisplayId(HWC_DISPLAY_EXTERNAL, 0)));
if (display != NULL)
display->handleHotplugEvent();
}
void ExynosDeviceDrmInterface::ExynosDrmEventHandler::handleHistogramEvent(uint32_t crtc_id,
void *bin) {
ExynosDisplayDrmInterface *displayInterface;
DrmProperty crtc;
uint32_t id;
for (auto display : mExynosDevice->mDisplays) {
displayInterface =
static_cast<ExynosDisplayDrmInterface *>(display->mDisplayInterface.get());
id = displayInterface->getCrtcId();
if (id == crtc_id) {
displayInterface->setHistogramData(bin);
}
}
}
void ExynosDeviceDrmInterface::ExynosDrmEventHandler::handleTUIEvent() {
if (mDrmDevice->event_listener()->IsDrmInTUI()) {
/* Received TUI Enter event */
if (!mExynosDevice->isInTUI()) {
mExynosDevice->enterToTUI();
ALOGV("%s:: DRM device in TUI", __func__);
}
} else {
/* Received TUI Exit event */
if (mExynosDevice->isInTUI()) {
mExynosDevice->onRefreshDisplays();
mExynosDevice->exitFromTUI();
ALOGV("%s:: DRM device out TUI", __func__);
}
}
}
constexpr size_t IDLE_ENTER_EVENT_DATA_SIZE = 3;
void ExynosDeviceDrmInterface::ExynosDrmEventHandler::handleIdleEnterEvent(char const *event) {
/* PANEL_IDLE_ENTER=<display index>,<vrefresh>,<idle te vrefresh> */
std::string_view idle_event_str(event);
auto prefix_shift_pos = idle_event_str.find("=");
if (prefix_shift_pos == std::string::npos) {
ALOGE("%s: idle enter event format is incorrect", __func__);
}
int count = 0;
int value[IDLE_ENTER_EVENT_DATA_SIZE] = {0};
const auto &[displayIndex, vrefresh, idleTeVrefresh] = value;
auto start_pos = prefix_shift_pos + 1;
auto end_pos = idle_event_str.find(",", start_pos);
while (end_pos != std::string::npos && count < IDLE_ENTER_EVENT_DATA_SIZE) {
auto info = idle_event_str.substr(start_pos, end_pos - start_pos);
value[count++] = atoi(info.data());
start_pos = end_pos + 1;
end_pos = idle_event_str.find(",", start_pos);
if (end_pos == std::string::npos) {
info = idle_event_str.substr(start_pos, idle_event_str.size() - start_pos);
value[count++] = atoi(info.data());
}
}
if (count != IDLE_ENTER_EVENT_DATA_SIZE) {
ALOGE("%s: idle enter event is incomplete", __func__);
return;
}
ExynosDisplay *primaryDisplay =
mExynosDevice->getDisplay(getDisplayId(HWC_DISPLAY_PRIMARY, displayIndex));
if (primaryDisplay) {
/* sending vsyncIdle callback */
if (vrefresh != idleTeVrefresh) {
mExynosDevice->onVsyncIdle(primaryDisplay->getId());
}
primaryDisplay->handleDisplayIdleEnter(idleTeVrefresh);
}
}