DisplayError ResourceDefault::Init() {
  DisplayError error = kErrorNone;

  num_pipe_ = hw_res_info_.num_vig_pipe + hw_res_info_.num_rgb_pipe + hw_res_info_.num_dma_pipe;

  if (!num_pipe_) {
    DLOGE("Number of H/W pipes is Zero!");
    return kErrorParameters;
  }

  src_pipes_.resize(num_pipe_);

  // Priority order of pipes: VIG, RGB, DMA
  uint32_t vig_index = 0;
  uint32_t rgb_index = hw_res_info_.num_vig_pipe;
  uint32_t dma_index = rgb_index + hw_res_info_.num_rgb_pipe;

  for (uint32_t i = 0; i < num_pipe_; i++) {
    const HWPipeCaps &pipe_caps = hw_res_info_.hw_pipes.at(i);
    if (pipe_caps.type == kPipeTypeVIG) {
      src_pipes_[vig_index].type = kPipeTypeVIG;
      src_pipes_[vig_index].index = i;
      src_pipes_[vig_index].mdss_pipe_id = pipe_caps.id;
      vig_index++;
    } else if (pipe_caps.type == kPipeTypeRGB) {
      src_pipes_[rgb_index].type = kPipeTypeRGB;
      src_pipes_[rgb_index].index = i;
      src_pipes_[rgb_index].mdss_pipe_id = pipe_caps.id;
      rgb_index++;
    } else if (pipe_caps.type == kPipeTypeDMA) {
      src_pipes_[dma_index].type = kPipeTypeDMA;
      src_pipes_[dma_index].index = i;
      src_pipes_[dma_index].mdss_pipe_id = pipe_caps.id;
      dma_index++;
    }
  }

  for (uint32_t i = 0; i < num_pipe_; i++) {
    src_pipes_[i].priority = INT(i);
  }

  DLOGI("hw_rev=%x, DMA=%d RGB=%d VIG=%d", hw_res_info_.hw_revision, hw_res_info_.num_dma_pipe,
    hw_res_info_.num_rgb_pipe, hw_res_info_.num_vig_pipe);

  if (hw_res_info_.max_scale_down < 1 || hw_res_info_.max_scale_up < 1) {
    DLOGE("Max scaling setting is invalid! max_scale_down = %d, max_scale_up = %d",
          hw_res_info_.max_scale_down, hw_res_info_.max_scale_up);
    hw_res_info_.max_scale_down = 1;
    hw_res_info_.max_scale_up = 1;
  }

  // TODO(user): clean it up, query from driver for initial pipe status.
#ifndef SDM_VIRTUAL_DRIVER
  rgb_index = hw_res_info_.num_vig_pipe;
  src_pipes_[rgb_index].owner = kPipeOwnerKernelMode;
  src_pipes_[rgb_index + 1].owner = kPipeOwnerKernelMode;
#endif

  return error;
}
void HWCDisplayPrimary::HandleFrameDump() {
  if (dump_frame_count_ && output_buffer_.release_fence_fd >= 0) {
    int ret = sync_wait(output_buffer_.release_fence_fd, 1000);
    ::close(output_buffer_.release_fence_fd);
    output_buffer_.release_fence_fd = -1;
    if (ret < 0) {
      DLOGE("sync_wait error errno = %d, desc = %s", errno, strerror(errno));
    } else {
      DumpOutputBuffer(output_buffer_info_, output_buffer_base_, layer_stack_.retire_fence_fd);
    }
  }

  if (0 == dump_frame_count_) {
    dump_output_to_file_ = false;
    // Unmap and Free buffer
    if (munmap(output_buffer_base_, output_buffer_info_.alloc_buffer_info.size) != 0) {
      DLOGE("unmap failed with err %d", errno);
    }
    if (buffer_allocator_->FreeBuffer(&output_buffer_info_) != 0) {
      DLOGE("FreeBuffer failed");
    }

    post_processed_output_ = false;
    output_buffer_ = {};
    output_buffer_info_ = {};
    output_buffer_base_ = nullptr;
  }
}
int HWCDisplayPrimary::FrameCaptureAsync(const BufferInfo &output_buffer_info,
                                         bool post_processed_output) {
  // Note: This function is called in context of a binder thread and a lock is already held
  if (output_buffer_info.alloc_buffer_info.fd < 0) {
    DLOGE("Invalid fd %d", output_buffer_info.alloc_buffer_info.fd);
    return -1;
  }

  auto panel_width = 0u;
  auto panel_height = 0u;
  auto fb_width = 0u;
  auto fb_height = 0u;

  GetPanelResolution(&panel_width, &panel_height);
  GetFrameBufferResolution(&fb_width, &fb_height);

  if (post_processed_output && (output_buffer_info_.buffer_config.width < panel_width ||
                                output_buffer_info_.buffer_config.height < panel_height)) {
    DLOGE("Buffer dimensions should not be less than panel resolution");
    return -1;
  } else if (!post_processed_output && (output_buffer_info_.buffer_config.width < fb_width ||
                                        output_buffer_info_.buffer_config.height < fb_height)) {
    DLOGE("Buffer dimensions should not be less than FB resolution");
    return -1;
  }

  SetLayerBuffer(output_buffer_info, &output_buffer_);
  post_processed_output_ = post_processed_output;
  frame_capture_buffer_queued_ = true;
  // Status is only cleared on a new call to dump and remains valid otherwise
  frame_capture_status_ = -EAGAIN;
  DisablePartialUpdateOneFrame();

  return 0;
}
HWCColorManager *HWCColorManager::CreateColorManager(HWCBufferAllocator * buffer_allocator) {
  HWCColorManager *color_mgr = new HWCColorManager(buffer_allocator);

  if (color_mgr) {
    // Load display API interface library. And retrieve color API function tables.
    DynLib &color_apis_lib = color_mgr->color_apis_lib_;
    if (color_apis_lib.Open(DISPLAY_API_INTERFACE_LIBRARY_NAME)) {
      if (!color_apis_lib.Sym(DISPLAY_API_FUNC_TABLES, &color_mgr->color_apis_)) {
        DLOGE("Fail to retrieve = %s from %s", DISPLAY_API_FUNC_TABLES,
              DISPLAY_API_INTERFACE_LIBRARY_NAME);
        delete color_mgr;
        return NULL;
      }
    } else {
      DLOGW("Unable to load = %s", DISPLAY_API_INTERFACE_LIBRARY_NAME);
      delete color_mgr;
      return NULL;
    }
    DLOGI("Successfully loaded %s", DISPLAY_API_INTERFACE_LIBRARY_NAME);

    // Load diagclient library and invokes its entry point to pass in display APIs.
    DynLib &diag_client_lib = color_mgr->diag_client_lib_;
    if (diag_client_lib.Open(QDCM_DIAG_CLIENT_LIBRARY_NAME)) {
      if (!diag_client_lib.Sym(INIT_QDCM_DIAG_CLIENT_NAME,
                               reinterpret_cast<void **>(&color_mgr->qdcm_diag_init_)) ||
        !diag_client_lib.Sym(DEINIT_QDCM_DIAG_CLIENT_NAME,
                               reinterpret_cast<void **>(&color_mgr->qdcm_diag_deinit_))) {
        DLOGE("Fail to retrieve = %s from %s", INIT_QDCM_DIAG_CLIENT_NAME,
              QDCM_DIAG_CLIENT_LIBRARY_NAME);
      } else {
        // invoke Diag Client entry point to initialize.
        color_mgr->qdcm_diag_init_(color_mgr->color_apis_);
        DLOGI("Successfully loaded %s and %s and diag_init'ed", DISPLAY_API_INTERFACE_LIBRARY_NAME,
              QDCM_DIAG_CLIENT_LIBRARY_NAME);
      }
    } else {
      DLOGW("Unable to load = %s", QDCM_DIAG_CLIENT_LIBRARY_NAME);
      // only QDCM Diag client failed to be loaded and system still should function.
    }
  } else {
    DLOGE("Unable to create HWCColorManager");
    return NULL;
  }

  return color_mgr;
}
DisplayError CompManager::RegisterDisplay(DisplayType type,
                                          const HWDisplayAttributes &display_attributes,
                                          const HWPanelInfo &hw_panel_info,
                                          const HWMixerAttributes &mixer_attributes,
                                          const DisplayConfigVariableInfo &fb_config,
                                          Handle *display_ctx) {
  SCOPE_LOCK(locker_);

  DisplayError error = kErrorNone;

  DisplayCompositionContext *display_comp_ctx = new DisplayCompositionContext();
  if (!display_comp_ctx) {
    return kErrorMemory;
  }

  Strategy *&strategy = display_comp_ctx->strategy;
  strategy = new Strategy(extension_intf_, type, hw_res_info_, hw_panel_info, mixer_attributes,
                          display_attributes, fb_config);
  if (!strategy) {
    DLOGE("Unable to create strategy");
    delete display_comp_ctx;
    return kErrorMemory;
  }

  error = strategy->Init();
  if (error != kErrorNone) {
    delete strategy;
    delete display_comp_ctx;
    return error;
  }

  error = resource_intf_->RegisterDisplay(type, display_attributes, hw_panel_info, mixer_attributes,
                                          &display_comp_ctx->display_resource_ctx);
  if (error != kErrorNone) {
    strategy->Deinit();
    delete strategy;
    delete display_comp_ctx;
    display_comp_ctx = NULL;
    return error;
  }

  registered_displays_[type] = 1;
  display_comp_ctx->is_primary_panel = hw_panel_info.is_primary_panel;
  display_comp_ctx->display_type = type;
  *display_ctx = display_comp_ctx;
  // New non-primary display device has been added, so move the composition mode to safe mode until
  // resources for the added display is configured properly.
  if (!display_comp_ctx->is_primary_panel) {
    safe_mode_ = true;
  }

  DLOGV_IF(kTagCompManager, "registered display bit mask 0x%x, configured display bit mask 0x%x, " \
           "display type %d", registered_displays_.to_ulong(), configured_displays_.to_ulong(),
           display_comp_ctx->display_type);

  return kErrorNone;
}
HWC2::Error HWCDisplayPrimary::SetColorModeById(int32_t color_mode_id) {
  auto status = color_mode_->SetColorModeById(color_mode_id);
  if (status != HWC2::Error::None) {
    DLOGE("failed for mode = %d", color_mode_id);
    return status;
  }

  callbacks_->Refresh(HWC_DISPLAY_PRIMARY);

  return status;
}
DisplayError DisplayHDMI::Init() {
  lock_guard<recursive_mutex> obj(recursive_mutex_);

  DisplayError error = HWInterface::Create(kHDMI, hw_info_intf_, buffer_sync_handler_,
                                           &hw_intf_);
  if (error != kErrorNone) {
    return error;
  }

  uint32_t active_mode_index;
  char value[64] = "0";
  Debug::GetProperty("sdm.hdmi.s3d_mode", value);
  HWS3DMode mode = (HWS3DMode)atoi(value);
  if (mode > kS3DModeNone && mode < kS3DModeMax) {
    active_mode_index = GetBestConfig(mode);
  } else {
    active_mode_index = GetBestConfig(kS3DModeNone);
  }

  error = hw_intf_->SetDisplayAttributes(active_mode_index);
  if (error != kErrorNone) {
    HWInterface::Destroy(hw_intf_);
  }

  error = DisplayBase::Init();
  if (error != kErrorNone) {
    HWInterface::Destroy(hw_intf_);
    return error;
  }

  GetScanSupport();
  underscan_supported_ = (scan_support_ == kScanAlwaysUnderscanned) || (scan_support_ == kScanBoth);

  s3d_format_to_mode_.insert(std::pair<LayerBufferS3DFormat, HWS3DMode>
                            (kS3dFormatNone, kS3DModeNone));
  s3d_format_to_mode_.insert(std::pair<LayerBufferS3DFormat, HWS3DMode>
                            (kS3dFormatLeftRight, kS3DModeLR));
  s3d_format_to_mode_.insert(std::pair<LayerBufferS3DFormat, HWS3DMode>
                            (kS3dFormatRightLeft, kS3DModeRL));
  s3d_format_to_mode_.insert(std::pair<LayerBufferS3DFormat, HWS3DMode>
                            (kS3dFormatTopBottom, kS3DModeTB));
  s3d_format_to_mode_.insert(std::pair<LayerBufferS3DFormat, HWS3DMode>
                            (kS3dFormatFramePacking, kS3DModeFP));

  error = HWEventsInterface::Create(INT(display_type_), this, event_list_, &hw_events_intf_);
  if (error != kErrorNone) {
    DisplayBase::Deinit();
    HWInterface::Destroy(hw_intf_);
    DLOGE("Failed to create hardware events interface. Error = %d", error);
  }

  return error;
}
HWC2::Error HWCDisplayPrimary::SetColorMode(android_color_mode_t mode) {
  validated_ = false;
  auto status = color_mode_->SetColorMode(mode);
  if (status != HWC2::Error::None) {
    DLOGE("failed for mode = %d", mode);
    return status;
  }

  callbacks_->Refresh(HWC_DISPLAY_PRIMARY);

  return status;
}
void HWCDisplayPrimary::SetFrameDumpConfig(uint32_t count, uint32_t bit_mask_layer_type) {
  HWCDisplay::SetFrameDumpConfig(count, bit_mask_layer_type);
  dump_output_to_file_ = bit_mask_layer_type & (1 << OUTPUT_LAYER_DUMP);
  DLOGI("output_layer_dump_enable %d", dump_output_to_file_);

  if (!count || !dump_output_to_file_) {
    return;
  }

  // Allocate and map output buffer
  output_buffer_info_ = {};
  // Since we dump DSPP output use Panel resolution.
  GetPanelResolution(&output_buffer_info_.buffer_config.width,
                     &output_buffer_info_.buffer_config.height);
  output_buffer_info_.buffer_config.format = kFormatRGB888;
  output_buffer_info_.buffer_config.buffer_count = 1;
  if (buffer_allocator_->AllocateBuffer(&output_buffer_info_) != 0) {
    DLOGE("Buffer allocation failed");
    output_buffer_info_ = {};
    return;
  }

  void *buffer = mmap(NULL, output_buffer_info_.alloc_buffer_info.size, PROT_READ | PROT_WRITE,
                      MAP_SHARED, output_buffer_info_.alloc_buffer_info.fd, 0);

  if (buffer == MAP_FAILED) {
    DLOGE("mmap failed with err %d", errno);
    buffer_allocator_->FreeBuffer(&output_buffer_info_);
    output_buffer_info_ = {};
    return;
  }

  output_buffer_base_ = buffer;
  post_processed_output_ = true;
  DisablePartialUpdateOneFrame();
}
int HWCColorManager::EnableQDCMMode(bool enable, HWCDisplay *hwc_display) {
  int ret = 0;

  if (!qdcm_mode_mgr_) {
    qdcm_mode_mgr_ = HWCQDCMModeManager::CreateQDCMModeMgr();
    if (!qdcm_mode_mgr_) {
      DLOGE("Unable to create QDCM operating mode manager.");
      ret = -EFAULT;
    }
  }

  if (qdcm_mode_mgr_) {
    ret = qdcm_mode_mgr_->EnableQDCMMode(enable, hwc_display);
  }

  return ret;
}
DisplayError ResourceDefault::CalculateDecimation(float downscale, uint8_t *decimation) {
  float max_down_scale = FLOAT(hw_res_info_.max_scale_down);

  if (downscale <= max_down_scale) {
    *decimation = 0;
    return kErrorNone;
  } else if (!hw_res_info_.has_decimation) {
    DLOGE("Downscaling exceeds the maximum MDP downscale limit but decimation not enabled");
    return kErrorNotSupported;
  }

  // Decimation is the remaining downscale factor after doing max SDE downscale.
  // In SDE, decimation is supported in powers of 2.
  // For ex: If a pipe needs downscale of 8 but max_down_scale is 4
  // So decimation = powf(2.0, ceilf(log2f(8 / 4))) = powf(2.0, 1.0) = 2
  *decimation = UINT8(ceilf(log2f(downscale / max_down_scale)));
  return kErrorNone;
}
HWC2::Error HWCDisplayPrimary::SetColorTransform(const float *matrix,
                                                 android_color_transform_t hint) {
  validated_ = false;
  if (!matrix) {
    return HWC2::Error::BadParameter;
  }

  auto status = color_mode_->SetColorTransform(matrix, hint);
  if (status != HWC2::Error::None) {
    DLOGE("failed for hint = %d", hint);
    color_tranform_failed_ = true;
    return status;
  }

  callbacks_->Refresh(HWC_DISPLAY_PRIMARY);
  color_tranform_failed_ = false;

  return status;
}
Beispiel #13
0
DisplayError Strategy::Init() {
  DisplayError error = kErrorNone;

  if (extension_intf_) {
    error = extension_intf_->CreateStrategyExtn(display_type_, hw_panel_info_.mode,
                                                hw_panel_info_.s3d_mode, mixer_attributes_,
                                                fb_config_, &strategy_intf_);
    if (error != kErrorNone) {
      DLOGE("Failed to create strategy");
      return error;
    }

    error = extension_intf_->CreatePartialUpdate(display_type_, hw_resource_info_, hw_panel_info_,
                                                 mixer_attributes_, display_attributes_,
                                                 &partial_update_intf_);
  }

  return kErrorNone;
}
DisplayError HWEvents::Init(int fb_num, HWEventHandler *event_handler,
                            vector<const char *> *event_list) {
  if (!event_handler)
    return kErrorParameters;

  event_handler_ = event_handler;
  fb_num_ = fb_num;
  event_list_ = event_list;
  poll_fds_.resize(event_list_->size());
  event_thread_name_ += " - " + std::to_string(fb_num_);

  PopulateHWEventData();

  if (pthread_create(&event_thread_, NULL, &DisplayEventThread, this) < 0) {
    DLOGE("Failed to start %s, error = %s", event_thread_name_.c_str());
    return kErrorResources;
  }

  return kErrorNone;
}
DisplayError CompManager::ReconfigureDisplay(Handle comp_handle,
                                             const HWDisplayAttributes &display_attributes,
                                             const HWPanelInfo &hw_panel_info,
                                             const HWMixerAttributes &mixer_attributes,
                                             const DisplayConfigVariableInfo &fb_config) {
  SCOPE_LOCK(locker_);

  DisplayError error = kErrorNone;
  DisplayCompositionContext *display_comp_ctx =
                             reinterpret_cast<DisplayCompositionContext *>(comp_handle);

  error = resource_intf_->ReconfigureDisplay(display_comp_ctx->display_resource_ctx,
                                             display_attributes, hw_panel_info, mixer_attributes);
  if (error != kErrorNone) {
    return error;
  }

  if (display_comp_ctx->strategy) {
    error = display_comp_ctx->strategy->Reconfigure(hw_panel_info, display_attributes,
                                                    mixer_attributes, fb_config);
    if (error != kErrorNone) {
      DLOGE("Unable to Reconfigure strategy.");
      display_comp_ctx->strategy->Deinit();
      delete display_comp_ctx->strategy;
      display_comp_ctx->strategy = NULL;
      return error;
    }
  }

  // For HDMI S3D mode, set max_layers_ to 0 so that primary display would fall back
  // to GPU composition to release pipes for HDMI.
  if (display_comp_ctx->display_type == kHDMI) {
    if (hw_panel_info.s3d_mode != kS3DModeNone) {
      max_layers_ = 0;
    } else {
      max_layers_ = kMaxSDELayers;
    }
  }

  return error;
}
DisplayError CoreImpl::CreateDisplay(DisplayType type, DisplayEventHandler *event_handler,
                                     DisplayInterface **intf) {
  SCOPE_LOCK(locker_);

  if (UNLIKELY(!event_handler || !intf)) {
    return kErrorParameters;
  }

  DisplayBase *display_base = NULL;
  switch (type) {
  case kPrimary:
    display_base = new DisplayPrimary(event_handler, hw_intf_, &comp_mgr_, &offline_ctrl_);
    break;

  case kHDMI:
    display_base = new DisplayHDMI(event_handler, hw_intf_, &comp_mgr_, &offline_ctrl_);
    break;

  case kVirtual:
    display_base = new DisplayVirtual(event_handler, hw_intf_, &comp_mgr_, &offline_ctrl_);
    break;

  default:
    DLOGE("Spurious display type %d", type);
    return kErrorParameters;
  }

  if (UNLIKELY(!display_base)) {
    return kErrorMemory;
  }

  DisplayError error = display_base->Init();
  if (UNLIKELY(error != kErrorNone)) {
    delete display_base;
    display_base = NULL;
    return error;
  }

  *intf = display_base;
  return kErrorNone;
}
DisplayError CompManager::ReConfigure(Handle display_ctx, HWLayers *hw_layers) {
  SCOPE_LOCK(locker_);

  DisplayCompositionContext *display_comp_ctx =
                             reinterpret_cast<DisplayCompositionContext *>(display_ctx);
  Handle &display_resource_ctx = display_comp_ctx->display_resource_ctx;

  DisplayError error = kErrorUndefined;
  resource_intf_->Start(display_resource_ctx);
  error = resource_intf_->Acquire(display_resource_ctx, hw_layers);

  if (error != kErrorNone) {
    DLOGE("Reconfigure failed for display = %d", display_comp_ctx->display_type);
  }

  resource_intf_->Stop(display_resource_ctx);
  if (error != kErrorNone) {
      error = resource_intf_->PostPrepare(display_resource_ctx, hw_layers);
  }

  return error;
}
DisplayError CompManager::Prepare(Handle display_ctx, HWLayers *hw_layers) {
  SCOPE_LOCK(locker_);

  DisplayCompositionContext *display_comp_ctx =
                             reinterpret_cast<DisplayCompositionContext *>(display_ctx);
  Handle &display_resource_ctx = display_comp_ctx->display_resource_ctx;

  DisplayError error = kErrorUndefined;

  PrepareStrategyConstraints(display_ctx, hw_layers);

  // Select a composition strategy, and try to allocate resources for it.
  resource_intf_->Start(display_resource_ctx);

  bool exit = false;
  uint32_t &count = display_comp_ctx->remaining_strategies;
  for (; !exit && count > 0; count--) {
    error = display_comp_ctx->strategy->GetNextStrategy(&display_comp_ctx->constraints);
    if (error != kErrorNone) {
      // Composition strategies exhausted. Resource Manager could not allocate resources even for
      // GPU composition. This will never happen.
      exit = true;
    }

    if (!exit) {
      error = resource_intf_->Acquire(display_resource_ctx, hw_layers);
      // Exit if successfully allocated resource, else try next strategy.
      exit = (error == kErrorNone);
    }
  }

  if (error != kErrorNone) {
    DLOGE("Composition strategies exhausted for display = %d", display_comp_ctx->display_type);
  }

  resource_intf_->Stop(display_resource_ctx);

  return error;
}
HWC2::Error HWCDisplayPrimary::Present(int32_t *out_retire_fence) {
  auto status = HWC2::Error::None;
  if (display_paused_) {
    // TODO(user): From old HWC implementation
    // If we do not handle the frame set retireFenceFd to outbufAcquireFenceFd
    // Revisit this when validating display_paused
    DisplayError error = display_intf_->Flush();
    if (error != kErrorNone) {
      DLOGE("Flush failed. Error = %d", error);
    }
  } else {
    status = HWCDisplay::CommitLayerStack();
    if (status == HWC2::Error::None) {
      HandleFrameOutput();
      SolidFillCommit();
      status = HWCDisplay::PostCommitLayerStack(out_retire_fence);
    }
  }

  CloseAcquireFds();
  return status;
}
int HWCColorManager::SetFrameCapture(void *params, bool enable, HWCDisplay *hwc_display) {
  SCOPE_LOCK(locker_);
  int ret = 0;

  PPFrameCaptureData *frame_capture_data = reinterpret_cast<PPFrameCaptureData *>(params);

  if (enable) {
    std::memset(&buffer_info, 0x00, sizeof(buffer_info));
    hwc_display->GetPanelResolution(&buffer_info.buffer_config.width,
                                    &buffer_info.buffer_config.height);
    if (frame_capture_data->input_params.out_pix_format == PP_PIXEL_FORMAT_RGB_888) {
      buffer_info.buffer_config.format = kFormatRGB888;
    } else if (frame_capture_data->input_params.out_pix_format == PP_PIXEL_FORMAT_RGB_2101010) {
      buffer_info.buffer_config.format = kFormatRGBA1010102;
    } else {
      DLOGE("Pixel-format: %d NOT support.", frame_capture_data->input_params.out_pix_format);
      return -EFAULT;
    }

    buffer_info.buffer_config.buffer_count = 1;
    buffer_info.alloc_buffer_info.fd = -1;
    buffer_info.alloc_buffer_info.stride = 0;
    buffer_info.alloc_buffer_info.size = 0;

    ret = buffer_allocator_->AllocateBuffer(&buffer_info);
    if (ret != 0) {
      DLOGE("Buffer allocation failed. ret: %d", ret);
      return -ENOMEM;
    } else {
      void *buffer = mmap(NULL, buffer_info.alloc_buffer_info.size, PROT_READ | PROT_WRITE,
                          MAP_SHARED, buffer_info.alloc_buffer_info.fd, 0);

      if (buffer == MAP_FAILED) {
        DLOGE("mmap failed. err = %d", errno);
        frame_capture_data->buffer = NULL;
        ret = buffer_allocator_->FreeBuffer(&buffer_info);
        return -EFAULT;
      } else {
        frame_capture_data->buffer = reinterpret_cast<uint8_t *>(buffer);
        frame_capture_data->buffer_stride = buffer_info.buffer_config.width;
        frame_capture_data->buffer_size = buffer_info.alloc_buffer_info.size;
      }
      ret = hwc_display->FrameCaptureAsync(buffer_info, 1);
      if (ret < 0) {
        DLOGE("FrameCaptureAsync failed. ret = %d", ret);
      }
    }
  } else {
    ret = hwc_display->GetFrameCaptureStatus();
    if (!ret) {
      if (frame_capture_data->buffer != NULL) {
        if (munmap(frame_capture_data->buffer, buffer_info.alloc_buffer_info.size) != 0) {
          DLOGE("munmap failed. err = %d", errno);
        }
      }
      if (buffer_allocator_ != NULL) {
        std::memset(frame_capture_data, 0x00, sizeof(PPFrameCaptureData));
        ret = buffer_allocator_->FreeBuffer(&buffer_info);
        if (ret != 0) {
          DLOGE("FreeBuffer failed. ret = %d", ret);
        }
      }
    } else {
      DLOGE("GetFrameCaptureStatus failed. ret = %d", ret);
    }
  }
  return ret;
}