/*
* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*    * Redistributions of source code must retain the above copyright
*      notice, this list of conditions and the following disclaimer.
*    * Redistributions in binary form must reproduce the above
*      copyright notice, this list of conditions and the following
*      disclaimer in the documentation and/or other materials provided
*      with the distribution.
*    * Neither the name of The Linux Foundation nor the names of its
*      contributors may be used to endorse or promote products derived
*      from this software without specific prior written permission.

* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <cstring>
#include <errno.h>
#include <drm_logger.h>

#include "libdrm_macros.h"
#include "drm/drm_fourcc.h"
#include "drm_dpps_mgr_imp.h"

#define __CLASS__ "DRMDppsManagerImp"
namespace sde_drm {

static DRMDppsManagerImp dpps_mgr;
static DRMDppsManagerDummyImp dpps_dummy_mgr;

DRMDppsManagerIntf* GetDppsManagerIntf()
{
#if (defined(__ANDROID__))
    return &dpps_mgr;
#else
    return &dpps_dummy_mgr;
#endif
}

DRMDppsManagerImp::~DRMDppsManagerImp() {
  /* clean up the ION buffers for LTM */
  DeInitLtmBuffers();

  conn_id_ = -1;
  crtc_id_ = -1;
  drm_fd_ = -1;
}

int DRMDppsManagerImp::GetDrmResources(drmModeRes* res) {
  int enc_id = -1;
  drmModeConnector *conn = NULL;
  drmModeEncoder *enc = NULL;
  drmModeCrtc *crtc = NULL;

  if (drm_fd_ < 0 ) {
    DRM_LOGE("Invalid drm_fd_ %d", drm_fd_);
    return -EINVAL;
  }

  for (auto i = 0; i < res->count_connectors; i++) {
    conn = drmModeGetConnector(drm_fd_, res->connectors[i]);
    if (conn && conn->connector_type == DRM_MODE_CONNECTOR_DSI &&
        conn->count_modes && conn->connection == DRM_MODE_CONNECTED) {
      DRM_LOGI("Found connector %d", conn->connector_id);
      conn_id_ = conn->connector_id;
      break;
    }
    drmModeFreeConnector(conn);
    conn = NULL;
  }
  if (conn_id_ < 0 || !conn) {
    DRM_LOGE("Cannot find valid connector");
    conn_id_ = -1;
    return -EINVAL;
  }

  for (auto i = 0; i < conn->count_encoders; i++) {
    enc = drmModeGetEncoder(drm_fd_, conn->encoders[i]);
    if (enc && enc->encoder_type == DRM_MODE_ENCODER_DSI) {
      DRM_LOGI("Found encoder %d", enc->encoder_id);
      enc_id = enc->encoder_id;
      break;
    }
    drmModeFreeEncoder(enc);
    enc = NULL;
  }
  if (enc_id < 0 || !enc) {
    DRM_LOGE("Cannot find valid encoder");
    drmModeFreeConnector(conn);
    conn = NULL;
    res = NULL;
    conn_id_ = -1;
    return -EINVAL;
  }

  for (auto i = 0; i < res->count_crtcs; i++) {
    if (enc->possible_crtcs & (1 << i)) {
      crtc = drmModeGetCrtc(drm_fd_, res->crtcs[i]);
      if (crtc) {
        DRM_LOGI("Found crtc %d", crtc->crtc_id);
        crtc_id_ = crtc->crtc_id;
        break;
      }
      drmModeFreeCrtc(crtc);
      crtc = NULL;
    }
  }
  if (crtc_id_ < 0 || !crtc) {
    DRM_LOGE("Cannot find valid crtc");
    drmModeFreeEncoder(enc);
    drmModeFreeConnector(conn);
    enc = NULL;
    conn = NULL;
    conn_id_ = -1;
    crtc_id_ = -1;
    return -EINVAL;
  }
  return 0;
}

int DRMDppsManagerImp::InitCrtcProps() {
  if (drm_fd_ < 0 || crtc_id_ < 0) {
    DRM_LOGE("Invalid drm_fd_ %d or crtc_id_ %d", drm_fd_, crtc_id_);
    return -EINVAL;
  }

  drmModeObjectProperties *props =
    drmModeObjectGetProperties(drm_fd_, crtc_id_, DRM_MODE_OBJECT_CRTC);
  if (!props || !props->props || !props->prop_values) {
    drmModeFreeObjectProperties(props);
    return -EINVAL;
  }

  for (uint32_t j = 0; j < props->count_props; j++) {
    drmModePropertyRes *info = drmModeGetProperty(drm_fd_, props->props[j]);
    if (!info) {
      continue;
    }

    std::string property_name(info->name);
    DRMProperty prop_enum = prop_mgr_.GetPropertyEnum(property_name);
    if (prop_enum == DRMProperty::INVALID) {
      DRM_LOGD("DRMProperty %s missing from global property mapping", info->name);
      drmModeFreeProperty(info);
      continue;
    }

    prop_mgr_.SetPropertyId(prop_enum, info->prop_id);
    drmModeFreeProperty(info);
  }

  drmModeFreeObjectProperties(props);
  return 0;
}

int DRMDppsManagerImp::InitConnProps()
{
  if (drm_fd_ < 0 || conn_id_ < 0) {
    DRM_LOGE("Invalid drm_fd_ %d or conn_id_ %d", drm_fd_, conn_id_);
    return -EINVAL;
  }

  drmModeObjectProperties *props =
      drmModeObjectGetProperties(drm_fd_, conn_id_, DRM_MODE_OBJECT_CONNECTOR);
  if (!props || !props->props || !props->prop_values) {
    drmModeFreeObjectProperties(props);
    return -EINVAL;
  }

  for (uint32_t j = 0; j < props->count_props; j++) {
    drmModePropertyRes *info = drmModeGetProperty(drm_fd_, props->props[j]);
    if (!info) {
      continue;
    }

    std::string property_name(info->name);
    DRMProperty prop_enum = prop_mgr_.GetPropertyEnum(property_name);
    if (prop_enum == DRMProperty::INVALID) {
      DRM_LOGD("DRMProperty %s missing from global property mapping", info->name);
      drmModeFreeProperty(info);
      continue;
    }

    prop_mgr_.SetPropertyId(prop_enum, info->prop_id);
    drmModeFreeProperty(info);
  }

  drmModeFreeObjectProperties(props);
  return 0;
}

void DRMDppsManagerImp::Init(int fd, drmModeRes* res) {
  std::lock_guard<std::mutex> guard(api_lock_);
  int ret = 0;

  if (fd < 0 || !res) {
    DRM_LOGE("Invalid drm fd %d or res %p", fd, res);
    return;
  }

  drm_fd_ = fd;
  ret = GetDrmResources(res);
  if (ret) {
    DRM_LOGE("Failed to get DRM resources %d", ret);
    return;
  } else {
    ret = InitCrtcProps();
    if (ret) {
      DRM_LOGE("Failed to initialize crtc properties %d", ret);
      return;
    }
    ret = InitConnProps();
    if (ret) {
      DRM_LOGE("Failed to initialize conn properties %d", ret);
      return;
    }
  }

  memset(&dpps_feature_, 0, sizeof(dpps_feature_));
  dpps_feature_[kFeatureAd4Mode] = DRMDppsPropInfo {
    (prop_mgr_.GetPropertyId(DRMProperty::SDE_DSPP_AD4_MODE) == 0 ? 0U : 4U) /* version */,
    DRMProperty::SDE_DSPP_AD4_MODE, prop_mgr_.GetPropertyId(DRMProperty::SDE_DSPP_AD4_MODE),
    false /* is_event */};
  dpps_feature_[kFeatureAd4Init] = DRMDppsPropInfo {4 /* version */,
    DRMProperty::SDE_DSPP_AD4_INIT, prop_mgr_.GetPropertyId(DRMProperty::SDE_DSPP_AD4_INIT),
    false /* is_event */};
  dpps_feature_[kFeatureAd4Cfg] = DRMDppsPropInfo { 4 /* version */,
    DRMProperty::SDE_DSPP_AD4_CFG, prop_mgr_.GetPropertyId(DRMProperty::SDE_DSPP_AD4_CFG),
    false /* is_event */};
  dpps_feature_[kFeatureAd4Input] = DRMDppsPropInfo {4 /* version */,
    DRMProperty::SDE_DSPP_AD4_INPUT, prop_mgr_.GetPropertyId(DRMProperty::SDE_DSPP_AD4_INPUT),
    false /* is_event */};
  dpps_feature_[kFeatureAd4Backlight] = DRMDppsPropInfo {4 /* version */,
    DRMProperty::SDE_DSPP_AD4_BACKLIGHT, prop_mgr_.GetPropertyId(DRMProperty::SDE_DSPP_AD4_BACKLIGHT),
    false /* is_event */};
  dpps_feature_[kFeatureAd4Assertiveness] = DRMDppsPropInfo {4 /* version */,
    DRMProperty::SDE_DSPP_AD4_ASSERTIVENESS, prop_mgr_.GetPropertyId(DRMProperty::SDE_DSPP_AD4_ASSERTIVENESS),
    false /* is_event */};
  dpps_feature_[kFeatureAd4Roi] = DRMDppsPropInfo {4 /* version */,
    DRMProperty::SDE_DSPP_AD4_ROI, prop_mgr_.GetPropertyId(DRMProperty::SDE_DSPP_AD4_ROI),
    false /* is_event */};
  dpps_feature_[kFeatureAd4ManualStrength] = DRMDppsPropInfo {4 /* version */,
    DRMProperty::SDE_DSPP_AD4_STRENGTH, prop_mgr_.GetPropertyId(DRMProperty::SDE_DSPP_AD4_STRENGTH),
    false /* is_event */};
  dpps_feature_[kFeatureAbaHistCtrl] = DRMDppsPropInfo {1 /* version */,
    DRMProperty::SDE_DSPP_ABA_HIST_CTRL, prop_mgr_.GetPropertyId(DRMProperty::SDE_DSPP_ABA_HIST_CTRL),
    false /* is_event */};
  dpps_feature_[kFeatureAbaHistIRQ] = DRMDppsPropInfo {1 /* version */,
    DRMProperty::SDE_DSPP_ABA_HIST_IRQ, prop_mgr_.GetPropertyId(DRMProperty::SDE_DSPP_ABA_HIST_IRQ),
    false /* is_event */};
  dpps_feature_[kFeatureAbaLut] = DRMDppsPropInfo {1 /* version */,
    DRMProperty::SDE_DSPP_ABA_LUT, prop_mgr_.GetPropertyId(DRMProperty::SDE_DSPP_ABA_LUT),
    false /* is_event */};
  dpps_feature_[kFeatureSvBlScale] = DRMDppsPropInfo {1 /* version */,
    DRMProperty::SDE_DSPP_SV_BL_SCALE, prop_mgr_.GetPropertyId(DRMProperty::SDE_DSPP_SV_BL_SCALE),
    false /* is_event */};
  dpps_feature_[kFeatureBacklightScale] = DRMDppsPropInfo {1 /* version */,
    DRMProperty::SDE_DSPP_BL_SCALE, prop_mgr_.GetPropertyId(DRMProperty::SDE_DSPP_BL_SCALE),
    false /* is_event */};

  if (prop_mgr_.IsPropertyAvailable(DRMProperty::SDE_LTM_VERSION)) {
    dpps_feature_[kFeatureLtm] = DRMDppsPropInfo {1 /* version */,
      DRMProperty::SDE_LTM_VERSION, prop_mgr_.GetPropertyId(DRMProperty::SDE_LTM_VERSION),
      false /* is_event */};
    dpps_feature_[kFeatureLtmInit] = DRMDppsPropInfo {1 /* version */,
      DRMProperty::SDE_LTM_INIT, prop_mgr_.GetPropertyId(DRMProperty::SDE_LTM_INIT),
      false /* is_event */};
    dpps_feature_[kFeatureLtmCfg] = DRMDppsPropInfo {1 /* version */,
      DRMProperty::SDE_LTM_CFG, prop_mgr_.GetPropertyId(DRMProperty::SDE_LTM_CFG),
      false /* is_event */};
    dpps_feature_[kFeatureLtmNoiseThresh] = DRMDppsPropInfo {1 /* version */,
      DRMProperty::SDE_LTM_NOISE_THRESH, prop_mgr_.GetPropertyId(DRMProperty::SDE_LTM_NOISE_THRESH),
      false /* is_event */};
    dpps_feature_[kFeatureLtmBufferCtrl] = DRMDppsPropInfo {1 /* version */,
      DRMProperty::SDE_LTM_BUFFER_CTRL, prop_mgr_.GetPropertyId(DRMProperty::SDE_LTM_BUFFER_CTRL),
      false /* is_event */};
    dpps_feature_[kFeatureLtmQueueBuffer] = DRMDppsPropInfo {1 /* version */,
      DRMProperty::SDE_LTM_QUEUE_BUFFER, prop_mgr_.GetPropertyId(DRMProperty::SDE_LTM_QUEUE_BUFFER),
      false /* is_event */};
    dpps_feature_[kFeatureLtmQueueBuffer2] = DRMDppsPropInfo {1 /* version */,
      DRMProperty::SDE_LTM_QUEUE_BUFFER2, prop_mgr_.GetPropertyId(DRMProperty::SDE_LTM_QUEUE_BUFFER2),
      false /* is_event */};
    dpps_feature_[kFeatureLtmQueueBuffer3] = DRMDppsPropInfo {1 /* version */,
      DRMProperty::SDE_LTM_QUEUE_BUFFER3, prop_mgr_.GetPropertyId(DRMProperty::SDE_LTM_QUEUE_BUFFER3),
      false /* is_event */};
    dpps_feature_[kFeatureLtmHistCtrl] = DRMDppsPropInfo {1 /* version */,
      DRMProperty::SDE_LTM_HIST_CTRL, prop_mgr_.GetPropertyId(DRMProperty::SDE_LTM_HIST_CTRL),
      false /* is_event */};
    dpps_feature_[kFeatureLtmVlut] = DRMDppsPropInfo {1 /* version */,
      DRMProperty::SDE_LTM_VLUT, prop_mgr_.GetPropertyId(DRMProperty::SDE_LTM_VLUT),
      false /* is_event */};
  } else {
    DRM_LOGI("LTM properties are not available");
  }

  dpps_feature_[kFeaturePowerEvent] = DRMDppsPropInfo{1, DRMProperty::INVALID, 0, true /* is_event */};
  dpps_feature_[kFeatureAbaHistEvent] = DRMDppsPropInfo{1, DRMProperty::INVALID, 0, true /* is_event */};
  dpps_feature_[kFeatureBackLightEvent] = DRMDppsPropInfo{1, DRMProperty::INVALID, 0, true /* is_event */};
  dpps_feature_[kFeatureAdAttBlEvent] = DRMDppsPropInfo{1, DRMProperty::INVALID, 0, true /* is_event */};
  dpps_feature_[kFeatureLtmHistEvent] = DRMDppsPropInfo{1, DRMProperty::INVALID, 0, true /* is_event */};
  dpps_feature_[kFeatureLtmWbPbEvent] = DRMDppsPropInfo{1, DRMProperty::INVALID, 0, true /* is_event */};
  dpps_feature_[kFeatureLtmOffEvent] = DRMDppsPropInfo{1, DRMProperty::INVALID, 0, true /* is_event */};
}

void DRMDppsManagerImp::CacheDppsFeature(uint32_t obj_id, va_list args) {
  std::lock_guard<std::mutex> guard(api_lock_);
  uint32_t feature_id = va_arg(args, uint32_t);
  uint64_t value = va_arg(args, uint64_t);
  struct DRMDppsPropInfo* info;

  if (feature_id >= kDppsFeaturesMax) {
    DRM_LOGE("Invalid feature id %d for obj_id 0x%x", feature_id, obj_id);
    return;
  }

  info = &dpps_feature_[feature_id];
  info->obj_id = obj_id;
  info->value = value;
  if (info->is_event) {
    dpps_dirty_event_.push_back(*info);
  } else {
    for (auto &it : dpps_dirty_prop_) {
      if ((it.obj_id == info->obj_id) && (it.prop_id == info->prop_id)) {
        it.value = info->value;
        return;
      }
    }
    dpps_dirty_prop_.push_back(*info);
  }
}

void DRMDppsManagerImp::CommitDppsFeatures(drmModeAtomicReq *req, const DRMDisplayToken &tok) {
  std::lock_guard<std::mutex> guard(api_lock_);
  int ret = 0;

  if (!req)
    return;

  // Set Dpps properties
  if (!dpps_dirty_prop_.empty()) {
    for (auto it = dpps_dirty_prop_.begin(); it != dpps_dirty_prop_.end();) {
      if (it->obj_id == tok.crtc_id || it->obj_id == tok.conn_id) {
        ret = drmModeAtomicAddProperty(req, it->obj_id, it->prop_id, it->value);
        if (ret < 0)
          DRM_LOGE("AtomicAddProperty failed obj_id 0x%x, prop_id %d ret %d ", it->obj_id,
                   it->prop_id, ret);
        else
          it = dpps_dirty_prop_.erase(it);
      } else {
        it++;
      }
    }
  }

  // Set Dpps events
  if (!dpps_dirty_event_.empty()) {
    for (auto it = dpps_dirty_event_.begin(); it != dpps_dirty_event_.end();) {
      if (!it->value)
        continue;

      struct DRMDppsEventInfo info = *(struct DRMDppsEventInfo*)it->value;
      struct drm_msm_event_req event_req = {};
      int ret;
      if (it->obj_id == tok.crtc_id || it->obj_id == tok.conn_id) {
        event_req.object_id = it->obj_id;
        event_req.object_type = info.object_type;
        event_req.event = info.event_type;
        if (info.enable)
          ret = drmIoctl(info.drm_fd, DRM_IOCTL_MSM_REGISTER_EVENT, &event_req);
        else
          ret = drmIoctl(info.drm_fd, DRM_IOCTL_MSM_DEREGISTER_EVENT, &event_req);
        if (ret) {
          ret = -errno;
          if (ret == -EALREADY) {
            DRM_LOGI("Duplicated request to set event 0x%x, object_id %u, object_type 0x%x, enable %d",
                      event_req.event, event_req.object_id, info.object_type, info.enable);
          } else {
            DRM_LOGE("Failed to set event 0x%x, object_id %u, object_type 0x%x, enable %d, ret %d",
                      event_req.event, event_req.object_id, info.object_type, info.enable, ret);
          }
        }
        if (ret != -ENODEV)
          it = dpps_dirty_event_.erase(it);
        else
          it++;
      } else {
        it++;
      }
    }
  }
}

void DRMDppsManagerImp::GetDppsFeatureInfo(DRMDppsFeatureInfo *info)
{
  std::lock_guard<std::mutex> guard(api_lock_);
  int ret = 0;
  struct DRMDppsPropInfo* prop_info;

  if (!info) {
    DRM_LOGE("Invalid info NULL");
    return;
  }

  DRMDPPSFeatureID id = info->id;
  if (id >= kDppsFeaturesMax) {
    DRM_LOGE("Invalid feature id %d", id);
    return;
  }
  info->version = dpps_feature_[id].version;

  if ((id == kFeatureLtmBufferCtrl) && (dpps_feature_[kFeatureLtm].prop_id != 0)) {
    /* setup ION buffers for LTM */
    ret = InitLtmBuffers(info);
    if (ret) {
      DRM_LOGE("Failed to init LTM buffers %d", ret);
      return;
    } else {
      prop_info = &dpps_feature_[kFeatureLtmBufferCtrl];
      prop_info->obj_id = info->obj_id;
      for (const auto& it : ltm_buffers_ctrl_map_) {
        if (it.first == info->obj_id)
          prop_info->value = (uint64_t)&(it.second);
      }
      dpps_dirty_prop_.push_back(*prop_info);
    }
  }
}

int DRMDppsManagerImp::InitLtmBuffers(struct DRMDppsFeatureInfo *info) {
  int ret = 0;
  uint32_t buffer_size, i = 0, bpp = 0;
  void* uva;
  struct DRMDppsLtmBuffers *buffers;
  DRMDppsLtmBuffers ltm_buffers = {};
  drm_msm_ltm_buffers_ctrl ltm_buffers_ctrl = {};
  struct drm_prime_handle prime_req;
  struct drm_mode_fb_cmd2 fb_obj;
  struct drm_gem_close gem_close;

  if (drm_fd_ < 0) {
    DRM_LOGE("Invalid drm_fd_ %d", drm_fd_);
    return -EINVAL;
  }

  for (const auto& it : ltm_buffers_map_) {
    if (it.first == info->obj_id) {
      DRM_LOGE("LTM buffer already initialized, obj id %d", info->obj_id);
      return -EALREADY;
    }
  }

  if (!info->payload || info->payload_size != sizeof(struct DRMDppsLtmBuffers)) {
    DRM_LOGE("Invalid payload %p size %d expected %zu", info->payload, info->payload_size,
       sizeof(struct DRMDppsLtmBuffers));
    return -EINVAL;
  }

  buffers = (struct DRMDppsLtmBuffers *)info->payload;
  if (buffers->num_of_buffers <= 0 || buffers->num_of_buffers > LTM_BUFFER_SIZE) {
    DRM_LOGE("Invalid number of buffers %d", buffers->num_of_buffers);
    return -EINVAL;
  }

  std::memset(&ltm_buffers, 0, sizeof(ltm_buffers));
  std::memset(&ltm_buffers_ctrl, 0, sizeof(ltm_buffers_ctrl));

  ltm_buffers.num_of_buffers = buffers->num_of_buffers;

  buffer_size = sizeof(struct drm_msm_ltm_stats_data) + LTM_GUARD_BYTES;
  std::memset(&fb_obj, 0, sizeof(drm_mode_fb_cmd2));
  fb_obj.pixel_format = DRM_FORMAT_YVYU;
  /* YVYU gives us a bpp of 16 (2 bytes) so we must take that into account */
  fb_obj.height = 2;
  /* add extra one to compensate integer rounding */
  fb_obj.width = buffer_size / (2 * fb_obj.height) + 1;
  /* bpp for YVYU is 16 */
  bpp = 16;
  fb_obj.flags = DRM_MODE_FB_MODIFIERS;
  fb_obj.pitches[0] = fb_obj.width * bpp / 8;

  for (i = 0; i < buffers->num_of_buffers && !ret; i++) {
    std::memset(&prime_req, 0, sizeof(drm_prime_handle));
    prime_req.fd = buffers->ion_buffer_fd[i];
    ret = drmIoctl(drm_fd_, DRM_IOCTL_PRIME_FD_TO_HANDLE, &prime_req);
    if (ret) {
      ret = -errno;
      DRM_LOGE("failed get prime handle: %d", ret);
      break;
    }
    ltm_buffers.ion_buffer_fd[i] = buffers->ion_buffer_fd[i];

    fb_obj.handles[0] = prime_req.handle;
    ret = drmIoctl(drm_fd_, DRM_IOCTL_MODE_ADDFB2, &fb_obj);
    if (ret) {
      ret = -errno;
      DRM_LOGE("return value from addFB2: %d", ret);
      break;
    }
    ltm_buffers.drm_fb_id[i] = buffers->drm_fb_id[i] = fb_obj.fb_id;
    ltm_buffers_ctrl.fds[i] = ltm_buffers.drm_fb_id[i];

    /**
     * ADDFB2 will take reference to GEM handles,
     * so drop reference taken by PrimeFDtoHandle now
     */
    std::memset(&gem_close, 0, sizeof(gem_close));
    gem_close.handle = prime_req.handle;
    ret = drmIoctl(drm_fd_, DRM_IOCTL_GEM_CLOSE, &gem_close);
    if(ret) {
      ret = -errno;
      DRM_LOGE("return value from GEM_CLOSE: %d\n", ret);
      break;
    }

    uva = drm_mmap(0, buffers->buffer_size, PROT_READ | PROT_WRITE, MAP_SHARED,
                   buffers->ion_buffer_fd[i], 0);
    if (uva == MAP_FAILED) {
      ret = -errno;
      DRM_LOGE("failed to get uva: %d", ret);
      break;
    }
    ltm_buffers.uva[i] = buffers->uva[i] = uva;
  }

  if (!ret) {
    buffers->status = 0;
    ltm_buffers.buffer_size = buffers->buffer_size;
    ltm_buffers_ctrl.num_of_buffers = ltm_buffers.num_of_buffers;
    DRM_LOGV("InitLtmBuffers return successful");
  } else {
    DeInitLtmBuffers();
    buffers->status = ret;
    return ret;
  }

  ltm_buffers_map_.push_back(std::make_pair(info->obj_id, std::move(ltm_buffers)));
  ltm_buffers_ctrl_map_.push_back(std::make_pair(info->obj_id, std::move(ltm_buffers_ctrl)));
  return ret;
}

int DRMDppsManagerImp::DeInitLtmBuffers() {
  int ret = 0;
  uint32_t i = 0;

  if (drm_fd_ < 0) {
    return -EINVAL;
  }

  for (auto& it : ltm_buffers_map_) {
    DRMDppsLtmBuffers& ltm_buffers = it.second;
    for (i = 0; i < ltm_buffers.num_of_buffers; i++) {
      if (ltm_buffers.uva[i]) {
        drm_munmap(ltm_buffers.uva[i], ltm_buffers.buffer_size);
        ltm_buffers.uva[i] = NULL;
      }

      if (ltm_buffers.drm_fb_id[i] >= 0) {
#ifdef DRM_IOCTL_MSM_RMFB2
        ret = drmIoctl(drm_fd_, DRM_IOCTL_MSM_RMFB2, &ltm_buffers.drm_fb_id[i]);
        if (ret) {
          ret = errno;
          DRM_LOGE("RMFB2 failed for fb_id %d with error %d", ltm_buffers.drm_fb_id[i], ret);
        }
#endif
        ltm_buffers.drm_fb_id[i] = -1;
      }
      ltm_buffers.ion_buffer_fd[i] = -1;
    }

    ltm_buffers.num_of_buffers = 0;
    ltm_buffers.buffer_size = 0;
  }

  for (auto& it : ltm_buffers_ctrl_map_) {
    drm_msm_ltm_buffers_ctrl& ltm_buffers_ctrl = it.second;
    std::memset(&ltm_buffers_ctrl, 0, sizeof(ltm_buffers_ctrl));
  }

  ltm_buffers_map_.clear();
  ltm_buffers_ctrl_map_.clear();
  return 0;
}

}  // namespace sde_drm
