| /* |
| * Copyright (c) 2014 - 2016, 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 <math.h> |
| #include <utils/constants.h> |
| #include <utils/debug.h> |
| #include <utils/rect.h> |
| #include <utils/formats.h> |
| #include <utils/sys.h> |
| #include <dlfcn.h> |
| |
| #include "resource_default.h" |
| |
| #define __CLASS__ "ResourceDefault" |
| |
| namespace sdm { |
| |
| DisplayError ResourceDefault::Init(const HWResourceInfo &hw_res_info) { |
| 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_ = new SourcePipe[num_pipe_]; |
| hw_res_info_ = hw_res_info; |
| |
| // 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; |
| } |
| |
| DisplayError ResourceDefault::Deinit() { |
| delete[] src_pipes_; |
| return kErrorNone; |
| } |
| |
| DisplayError ResourceDefault::RegisterDisplay(DisplayType type, |
| const HWDisplayAttributes &attributes, |
| const HWPanelInfo &hw_panel_info, |
| Handle *display_ctx) { |
| DisplayError error = kErrorNone; |
| |
| HWBlockType hw_block_id = kHWBlockMax; |
| switch (type) { |
| case kPrimary: |
| if (!hw_block_ctx_[kHWPrimary].is_in_use) { |
| hw_block_id = kHWPrimary; |
| } |
| break; |
| |
| case kHDMI: |
| if (!hw_block_ctx_[kHWHDMI].is_in_use) { |
| hw_block_id = kHWHDMI; |
| } |
| break; |
| |
| default: |
| DLOGW("RegisterDisplay, invalid type %d", type); |
| return kErrorParameters; |
| } |
| |
| if (hw_block_id == kHWBlockMax) { |
| return kErrorResources; |
| } |
| |
| DisplayResourceContext *display_resource_ctx = new DisplayResourceContext(); |
| if (!display_resource_ctx) { |
| return kErrorMemory; |
| } |
| |
| hw_block_ctx_[hw_block_id].is_in_use = true; |
| |
| display_resource_ctx->display_attributes = attributes; |
| display_resource_ctx->hw_block_id = hw_block_id; |
| |
| if (!display_resource_ctx->display_attributes.is_device_split) { |
| display_resource_ctx->display_attributes.split_left = attributes.x_pixels; |
| } |
| |
| *display_ctx = display_resource_ctx; |
| return error; |
| } |
| |
| DisplayError ResourceDefault::UnregisterDisplay(Handle display_ctx) { |
| DisplayResourceContext *display_resource_ctx = |
| reinterpret_cast<DisplayResourceContext *>(display_ctx); |
| Purge(display_ctx); |
| |
| hw_block_ctx_[display_resource_ctx->hw_block_id].is_in_use = false; |
| |
| delete display_resource_ctx; |
| |
| return kErrorNone; |
| } |
| |
| void ResourceDefault::ReconfigureDisplay(Handle display_ctx, const HWDisplayAttributes &attributes, |
| const HWPanelInfo &hw_panel_info) { |
| SCOPE_LOCK(locker_); |
| |
| DisplayResourceContext *display_resource_ctx = |
| reinterpret_cast<DisplayResourceContext *>(display_ctx); |
| |
| display_resource_ctx->display_attributes = attributes; |
| } |
| |
| DisplayError ResourceDefault::Start(Handle display_ctx) { |
| locker_.Lock(); |
| |
| return kErrorNone; |
| } |
| |
| DisplayError ResourceDefault::Stop(Handle display_ctx) { |
| locker_.Unlock(); |
| |
| return kErrorNone; |
| } |
| |
| DisplayError ResourceDefault::Acquire(Handle display_ctx, HWLayers *hw_layers) { |
| DisplayResourceContext *display_resource_ctx = |
| reinterpret_cast<DisplayResourceContext *>(display_ctx); |
| |
| DisplayError error = kErrorNone; |
| const struct HWLayersInfo &layer_info = hw_layers->info; |
| HWBlockType hw_block_id = display_resource_ctx->hw_block_id; |
| |
| DLOGV_IF(kTagResources, "==== Resource reserving start: hw_block = %d ====", hw_block_id); |
| |
| if (layer_info.count > 1) { |
| DLOGV_IF(kTagResources, "More than one FB layers"); |
| return kErrorResources; |
| } |
| |
| Layer *layer = layer_info.stack->layers.at(layer_info.index[0]); |
| |
| if (layer->composition != kCompositionGPUTarget) { |
| DLOGV_IF(kTagResources, "Not an FB layer"); |
| return kErrorParameters; |
| } |
| |
| error = Config(display_resource_ctx, hw_layers); |
| if (error != kErrorNone) { |
| DLOGV_IF(kTagResources, "Resource config failed"); |
| return error; |
| } |
| |
| for (uint32_t i = 0; i < num_pipe_; i++) { |
| if (src_pipes_[i].hw_block_id == hw_block_id && src_pipes_[i].owner == kPipeOwnerUserMode) { |
| src_pipes_[i].ResetState(); |
| } |
| } |
| |
| uint32_t left_index = num_pipe_; |
| uint32_t right_index = num_pipe_; |
| bool need_scale = false; |
| |
| struct HWLayerConfig &layer_config = hw_layers->config[0]; |
| |
| HWPipeInfo *left_pipe = &layer_config.left_pipe; |
| HWPipeInfo *right_pipe = &layer_config.right_pipe; |
| |
| // left pipe is needed |
| if (left_pipe->valid) { |
| need_scale = IsScalingNeeded(left_pipe); |
| left_index = GetPipe(hw_block_id, need_scale); |
| if (left_index >= num_pipe_) { |
| DLOGV_IF(kTagResources, "Get left pipe failed: hw_block_id = %d, need_scale = %d", |
| hw_block_id, need_scale); |
| ResourceStateLog(); |
| goto CleanupOnError; |
| } |
| } |
| |
| error = SetDecimationFactor(left_pipe); |
| if (error != kErrorNone) { |
| goto CleanupOnError; |
| } |
| |
| if (!right_pipe->valid) { |
| // assign single pipe |
| if (left_index < num_pipe_) { |
| left_pipe->pipe_id = src_pipes_[left_index].mdss_pipe_id; |
| } |
| DLOGV_IF(kTagResources, "1 pipe acquired for FB layer, left_pipe = %x", left_pipe->pipe_id); |
| return kErrorNone; |
| } |
| |
| need_scale = IsScalingNeeded(right_pipe); |
| |
| right_index = GetPipe(hw_block_id, need_scale); |
| if (right_index >= num_pipe_) { |
| DLOGV_IF(kTagResources, "Get right pipe failed: hw_block_id = %d, need_scale = %d", hw_block_id, |
| need_scale); |
| ResourceStateLog(); |
| goto CleanupOnError; |
| } |
| |
| if (src_pipes_[right_index].priority < src_pipes_[left_index].priority) { |
| // Swap pipe based on priority |
| Swap(left_index, right_index); |
| } |
| |
| // assign dual pipes |
| left_pipe->pipe_id = src_pipes_[left_index].mdss_pipe_id; |
| right_pipe->pipe_id = src_pipes_[right_index].mdss_pipe_id; |
| |
| error = SetDecimationFactor(right_pipe); |
| if (error != kErrorNone) { |
| goto CleanupOnError; |
| } |
| |
| DLOGV_IF(kTagResources, "2 pipes acquired for FB layer, left_pipe = %x, right_pipe = %x", |
| left_pipe->pipe_id, right_pipe->pipe_id); |
| |
| return kErrorNone; |
| |
| CleanupOnError: |
| DLOGV_IF(kTagResources, "Resource reserving failed! hw_block = %d", hw_block_id); |
| |
| return kErrorResources; |
| } |
| |
| DisplayError ResourceDefault::PostPrepare(Handle display_ctx, HWLayers *hw_layers) { |
| SCOPE_LOCK(locker_); |
| |
| return kErrorNone; |
| } |
| |
| DisplayError ResourceDefault::PostCommit(Handle display_ctx, HWLayers *hw_layers) { |
| SCOPE_LOCK(locker_); |
| DisplayResourceContext *display_resource_ctx = |
| reinterpret_cast<DisplayResourceContext *>(display_ctx); |
| HWBlockType hw_block_id = display_resource_ctx->hw_block_id; |
| uint64_t frame_count = display_resource_ctx->frame_count; |
| |
| DLOGV_IF(kTagResources, "Resource for hw_block = %d, frame_count = %d", hw_block_id, frame_count); |
| |
| // handoff pipes which are used by splash screen |
| if ((frame_count == 0) && (hw_block_id == kHWPrimary)) { |
| for (uint32_t i = 0; i < num_pipe_; i++) { |
| if (src_pipes_[i].hw_block_id == hw_block_id && src_pipes_[i].owner == kPipeOwnerKernelMode) { |
| src_pipes_[i].owner = kPipeOwnerUserMode; |
| } |
| } |
| } |
| |
| if (hw_layers->info.sync_handle >= 0) |
| Sys::close_(hw_layers->info.sync_handle); |
| |
| display_resource_ctx->frame_count++; |
| |
| return kErrorNone; |
| } |
| |
| void ResourceDefault::Purge(Handle display_ctx) { |
| SCOPE_LOCK(locker_); |
| |
| DisplayResourceContext *display_resource_ctx = |
| reinterpret_cast<DisplayResourceContext *>(display_ctx); |
| HWBlockType hw_block_id = display_resource_ctx->hw_block_id; |
| |
| for (uint32_t i = 0; i < num_pipe_; i++) { |
| if (src_pipes_[i].hw_block_id == hw_block_id && src_pipes_[i].owner == kPipeOwnerUserMode) { |
| src_pipes_[i].ResetState(); |
| } |
| } |
| DLOGV_IF(kTagResources, "display id = %d", display_resource_ctx->hw_block_id); |
| } |
| |
| DisplayError ResourceDefault::SetMaxMixerStages(Handle display_ctx, uint32_t max_mixer_stages) { |
| SCOPE_LOCK(locker_); |
| |
| return kErrorNone; |
| } |
| |
| uint32_t ResourceDefault::SearchPipe(HWBlockType hw_block_id, SourcePipe *src_pipes, |
| uint32_t num_pipe) { |
| uint32_t index = num_pipe_; |
| SourcePipe *src_pipe; |
| |
| // search the pipe being used |
| for (uint32_t i = 0; i < num_pipe; i++) { |
| src_pipe = &src_pipes[i]; |
| if (src_pipe->owner == kPipeOwnerUserMode && src_pipe->hw_block_id == kHWBlockMax) { |
| index = src_pipe->index; |
| src_pipe->hw_block_id = hw_block_id; |
| break; |
| } |
| } |
| |
| return index; |
| } |
| |
| uint32_t ResourceDefault::NextPipe(PipeType type, HWBlockType hw_block_id) { |
| uint32_t num_pipe = 0; |
| SourcePipe *src_pipes = NULL; |
| |
| switch (type) { |
| case kPipeTypeVIG: |
| src_pipes = &src_pipes_[0]; |
| num_pipe = hw_res_info_.num_vig_pipe; |
| break; |
| case kPipeTypeRGB: |
| src_pipes = &src_pipes_[hw_res_info_.num_vig_pipe]; |
| num_pipe = hw_res_info_.num_rgb_pipe; |
| break; |
| case kPipeTypeDMA: |
| default: |
| src_pipes = &src_pipes_[hw_res_info_.num_vig_pipe + hw_res_info_.num_rgb_pipe]; |
| num_pipe = hw_res_info_.num_dma_pipe; |
| break; |
| } |
| |
| return SearchPipe(hw_block_id, src_pipes, num_pipe); |
| } |
| |
| uint32_t ResourceDefault::GetPipe(HWBlockType hw_block_id, bool need_scale) { |
| uint32_t index = num_pipe_; |
| |
| // The default behavior is to assume RGB and VG pipes have scalars |
| if (!need_scale) { |
| index = NextPipe(kPipeTypeDMA, hw_block_id); |
| } |
| |
| if ((index >= num_pipe_) && (!need_scale || !hw_res_info_.has_non_scalar_rgb)) { |
| index = NextPipe(kPipeTypeRGB, hw_block_id); |
| } |
| |
| if (index >= num_pipe_) { |
| index = NextPipe(kPipeTypeVIG, hw_block_id); |
| } |
| |
| return index; |
| } |
| |
| bool ResourceDefault::IsScalingNeeded(const HWPipeInfo *pipe_info) { |
| const LayerRect &src_roi = pipe_info->src_roi; |
| const LayerRect &dst_roi = pipe_info->dst_roi; |
| |
| return ((dst_roi.right - dst_roi.left) != (src_roi.right - src_roi.left)) || |
| ((dst_roi.bottom - dst_roi.top) != (src_roi.bottom - src_roi.top)); |
| } |
| |
| void ResourceDefault::ResourceStateLog() { |
| DLOGV_IF(kTagResources, "==== resource manager pipe state ===="); |
| uint32_t i; |
| for (i = 0; i < num_pipe_; i++) { |
| SourcePipe *src_pipe = &src_pipes_[i]; |
| DLOGV_IF(kTagResources, "index = %d, id = %x, hw_block = %d, owner = %s", |
| src_pipe->index, src_pipe->mdss_pipe_id, src_pipe->hw_block_id, |
| (src_pipe->owner == kPipeOwnerUserMode) ? "user mode" : "kernel mode"); |
| } |
| } |
| |
| DisplayError ResourceDefault::SrcSplitConfig(DisplayResourceContext *display_resource_ctx, |
| const LayerRect &src_rect, const LayerRect &dst_rect, |
| HWLayerConfig *layer_config) { |
| HWPipeInfo *left_pipe = &layer_config->left_pipe; |
| HWPipeInfo *right_pipe = &layer_config->right_pipe; |
| float src_width = src_rect.right - src_rect.left; |
| float dst_width = dst_rect.right - dst_rect.left; |
| |
| // Layer cannot qualify for SrcSplit if source or destination width exceeds max pipe width. |
| if ((src_width > hw_res_info_.max_pipe_width) || (dst_width > hw_res_info_.max_pipe_width)) { |
| SplitRect(src_rect, dst_rect, &left_pipe->src_roi, &left_pipe->dst_roi, &right_pipe->src_roi, |
| &right_pipe->dst_roi); |
| left_pipe->valid = true; |
| right_pipe->valid = true; |
| } else { |
| left_pipe->src_roi = src_rect; |
| left_pipe->dst_roi = dst_rect; |
| left_pipe->valid = true; |
| right_pipe->Reset(); |
| } |
| |
| return kErrorNone; |
| } |
| |
| DisplayError ResourceDefault::DisplaySplitConfig(DisplayResourceContext *display_resource_ctx, |
| const LayerRect &src_rect, const LayerRect &dst_rect, |
| HWLayerConfig *layer_config) { |
| HWDisplayAttributes &display_attributes = display_resource_ctx->display_attributes; |
| |
| // for display split case |
| HWPipeInfo *left_pipe = &layer_config->left_pipe; |
| HWPipeInfo *right_pipe = &layer_config->right_pipe; |
| LayerRect scissor_left, scissor_right, dst_left, crop_left, crop_right, dst_right; |
| |
| scissor_left.right = FLOAT(display_attributes.split_left); |
| scissor_left.bottom = FLOAT(display_attributes.y_pixels); |
| |
| scissor_right.left = FLOAT(display_attributes.split_left); |
| scissor_right.top = 0.0f; |
| scissor_right.right = FLOAT(display_attributes.x_pixels); |
| scissor_right.bottom = FLOAT(display_attributes.y_pixels); |
| |
| crop_left = src_rect; |
| dst_left = dst_rect; |
| crop_right = crop_left; |
| dst_right = dst_left; |
| |
| bool crop_left_valid = CalculateCropRects(scissor_left, &crop_left, &dst_left); |
| bool crop_right_valid = false; |
| |
| if (IsValid(scissor_right)) { |
| crop_right_valid = CalculateCropRects(scissor_right, &crop_right, &dst_right); |
| } |
| |
| // Reset left_pipe and right_pipe to invalid by default |
| left_pipe->Reset(); |
| right_pipe->Reset(); |
| |
| if (crop_left_valid) { |
| // assign left pipe |
| left_pipe->src_roi = crop_left; |
| left_pipe->dst_roi = dst_left; |
| left_pipe->valid = true; |
| } |
| |
| // assign right pipe if needed |
| if (crop_right_valid) { |
| right_pipe->src_roi = crop_right; |
| right_pipe->dst_roi = dst_right; |
| right_pipe->valid = true; |
| } |
| |
| return kErrorNone; |
| } |
| |
| DisplayError ResourceDefault::Config(DisplayResourceContext *display_resource_ctx, |
| HWLayers *hw_layers) { |
| HWLayersInfo &layer_info = hw_layers->info; |
| DisplayError error = kErrorNone; |
| Layer *layer = layer_info.stack->layers.at(layer_info.index[0]); |
| |
| error = ValidateLayerParams(layer); |
| if (error != kErrorNone) { |
| return error; |
| } |
| |
| struct HWLayerConfig *layer_config = &hw_layers->config[0]; |
| HWPipeInfo &left_pipe = layer_config->left_pipe; |
| HWPipeInfo &right_pipe = layer_config->right_pipe; |
| |
| LayerRect src_rect = layer->src_rect; |
| LayerRect dst_rect = layer->dst_rect; |
| |
| error = ValidateDimensions(src_rect, dst_rect); |
| if (error != kErrorNone) { |
| return error; |
| } |
| |
| bool ubwc_tiled = IsUBWCFormat(layer->input_buffer->format); |
| error = ValidateScaling(src_rect, dst_rect, false /*rotated90 */, ubwc_tiled, |
| false /* use_rotator_downscale */); |
| if (error != kErrorNone) { |
| return error; |
| } |
| |
| if (hw_res_info_.is_src_split) { |
| error = SrcSplitConfig(display_resource_ctx, src_rect, dst_rect, layer_config); |
| } else { |
| error = DisplaySplitConfig(display_resource_ctx, src_rect, dst_rect, layer_config); |
| } |
| |
| if (error != kErrorNone) { |
| return error; |
| } |
| |
| error = AlignPipeConfig(layer, &left_pipe, &right_pipe); |
| if (error != kErrorNone) { |
| return error; |
| } |
| |
| // set z_order, left_pipe should always be valid |
| left_pipe.z_order = 0; |
| |
| DLOGV_IF(kTagResources, "==== FB layer Config ===="); |
| Log(kTagResources, "input layer src_rect", layer->src_rect); |
| Log(kTagResources, "input layer dst_rect", layer->dst_rect); |
| Log(kTagResources, "cropped src_rect", src_rect); |
| Log(kTagResources, "cropped dst_rect", dst_rect); |
| Log(kTagResources, "left pipe src", layer_config->left_pipe.src_roi); |
| Log(kTagResources, "left pipe dst", layer_config->left_pipe.dst_roi); |
| if (right_pipe.valid) { |
| right_pipe.z_order = 0; |
| Log(kTagResources, "right pipe src", layer_config->right_pipe.src_roi); |
| Log(kTagResources, "right pipe dst", layer_config->right_pipe.dst_roi); |
| } |
| |
| return error; |
| } |
| |
| bool ResourceDefault::CalculateCropRects(const LayerRect &scissor, LayerRect *crop, |
| LayerRect *dst) { |
| float &crop_left = crop->left; |
| float &crop_top = crop->top; |
| float &crop_right = crop->right; |
| float &crop_bottom = crop->bottom; |
| float crop_width = crop->right - crop->left; |
| float crop_height = crop->bottom - crop->top; |
| |
| float &dst_left = dst->left; |
| float &dst_top = dst->top; |
| float &dst_right = dst->right; |
| float &dst_bottom = dst->bottom; |
| float dst_width = dst->right - dst->left; |
| float dst_height = dst->bottom - dst->top; |
| |
| const float &sci_left = scissor.left; |
| const float &sci_top = scissor.top; |
| const float &sci_right = scissor.right; |
| const float &sci_bottom = scissor.bottom; |
| |
| float left_cut_ratio = 0.0, right_cut_ratio = 0.0, top_cut_ratio = 0.0, bottom_cut_ratio = 0.0; |
| bool need_cut = false; |
| |
| if (dst_left < sci_left) { |
| left_cut_ratio = (sci_left - dst_left) / dst_width; |
| dst_left = sci_left; |
| need_cut = true; |
| } |
| |
| if (dst_right > sci_right) { |
| right_cut_ratio = (dst_right - sci_right) / dst_width; |
| dst_right = sci_right; |
| need_cut = true; |
| } |
| |
| if (dst_top < sci_top) { |
| top_cut_ratio = (sci_top - dst_top) / (dst_height); |
| dst_top = sci_top; |
| need_cut = true; |
| } |
| |
| if (dst_bottom > sci_bottom) { |
| bottom_cut_ratio = (dst_bottom - sci_bottom) / (dst_height); |
| dst_bottom = sci_bottom; |
| need_cut = true; |
| } |
| |
| if (!need_cut) |
| return true; |
| |
| crop_left += crop_width * left_cut_ratio; |
| crop_top += crop_height * top_cut_ratio; |
| crop_right -= crop_width * right_cut_ratio; |
| crop_bottom -= crop_height * bottom_cut_ratio; |
| Normalize(1, 1, crop); |
| Normalize(1, 1, dst); |
| if (IsValid(*crop) && IsValid(*dst)) |
| return true; |
| else |
| return false; |
| } |
| |
| DisplayError ResourceDefault::ValidateLayerParams(const Layer *layer) { |
| const LayerRect &src = layer->src_rect; |
| const LayerRect &dst = layer->dst_rect; |
| const LayerBuffer *input_buffer = layer->input_buffer; |
| |
| if (input_buffer->format == kFormatInvalid) { |
| DLOGV_IF(kTagResources, "Invalid input buffer format %d", input_buffer->format); |
| return kErrorNotSupported; |
| } |
| |
| if (!IsValid(src) || !IsValid(dst)) { |
| Log(kTagResources, "input layer src_rect", src); |
| Log(kTagResources, "input layer dst_rect", dst); |
| return kErrorNotSupported; |
| } |
| |
| // Make sure source in integral only if it is a non secure layer. |
| if (!input_buffer->flags.secure && |
| ((src.left - roundf(src.left) != 0.0f) || |
| (src.top - roundf(src.top) != 0.0f) || |
| (src.right - roundf(src.right) != 0.0f) || |
| (src.bottom - roundf(src.bottom) != 0.0f))) { |
| DLOGV_IF(kTagResources, "Input ROI is not integral"); |
| return kErrorNotSupported; |
| } |
| |
| return kErrorNone; |
| } |
| |
| DisplayError ResourceDefault::ValidateDimensions(const LayerRect &crop, const LayerRect &dst) { |
| if (!IsValid(crop)) { |
| Log(kTagResources, "Invalid crop rect", crop); |
| return kErrorNotSupported; |
| } |
| |
| if (!IsValid(dst)) { |
| Log(kTagResources, "Invalid dst rect", dst); |
| return kErrorNotSupported; |
| } |
| |
| float crop_width = crop.right - crop.left; |
| float crop_height = crop.bottom - crop.top; |
| float dst_width = dst.right - dst.left; |
| float dst_height = dst.bottom - dst.top; |
| |
| if ((UINT32(crop_width - dst_width) == 1) || (UINT32(crop_height - dst_height) == 1)) { |
| DLOGV_IF(kTagResources, "One pixel downscaling detected crop_w = %.0f, dst_w = %.0f, " \ |
| "crop_h = %.0f, dst_h = %.0f", crop_width, dst_width, crop_height, dst_height); |
| return kErrorNotSupported; |
| } |
| |
| return kErrorNone; |
| } |
| |
| DisplayError ResourceDefault::ValidatePipeParams(HWPipeInfo *pipe_info, bool ubwc_tiled) { |
| DisplayError error = kErrorNone; |
| |
| const LayerRect &src_rect = pipe_info->src_roi; |
| const LayerRect &dst_rect = pipe_info->dst_roi; |
| |
| error = ValidateDimensions(src_rect, dst_rect); |
| if (error != kErrorNone) { |
| return error; |
| } |
| |
| error = ValidateScaling(src_rect, dst_rect, false /* rotated90 */, ubwc_tiled, |
| false /* use_rotator_downscale */); |
| if (error != kErrorNone) { |
| return error; |
| } |
| |
| return kErrorNone; |
| } |
| |
| DisplayError ResourceDefault::ValidateScaling(const LayerRect &crop, const LayerRect &dst, |
| bool rotate90, bool ubwc_tiled, |
| bool use_rotator_downscale) { |
| DisplayError error = kErrorNone; |
| |
| float scale_x = 1.0f; |
| float scale_y = 1.0f; |
| |
| error = GetScaleFactor(crop, dst, &scale_x, &scale_y); |
| if (error != kErrorNone) { |
| return error; |
| } |
| |
| error = ValidateDownScaling(scale_x, scale_y, ubwc_tiled); |
| if (error != kErrorNone) { |
| return error; |
| } |
| |
| error = ValidateUpScaling(scale_x, scale_y); |
| if (error != kErrorNone) { |
| return error; |
| } |
| |
| return kErrorNone; |
| } |
| |
| DisplayError ResourceDefault::ValidateDownScaling(float scale_x, float scale_y, bool ubwc_tiled) { |
| if ((UINT32(scale_x) > 1) || (UINT32(scale_y) > 1)) { |
| float max_scale_down = FLOAT(hw_res_info_.max_scale_down); |
| |
| // MDP H/W cannot apply decimation on UBWC tiled framebuffer |
| if (!ubwc_tiled && hw_res_info_.has_decimation) { |
| max_scale_down *= FLOAT(kMaxDecimationDownScaleRatio); |
| } |
| |
| if (scale_x > max_scale_down || scale_y > max_scale_down) { |
| DLOGV_IF(kTagResources, |
| "Scaling down is over the limit: scale_x = %.0f, scale_y = %.0f, " \ |
| "has_deci = %d", scale_x, scale_y, hw_res_info_.has_decimation); |
| return kErrorNotSupported; |
| } |
| } |
| |
| DLOGV_IF(kTagResources, "scale_x = %.4f, scale_y = %.4f", scale_x, scale_y); |
| |
| return kErrorNone; |
| } |
| |
| DisplayError ResourceDefault::ValidateUpScaling(float scale_x, float scale_y) { |
| float max_scale_up = FLOAT(hw_res_info_.max_scale_up); |
| |
| if (UINT32(scale_x) < 1 && scale_x > 0.0f) { |
| if ((1.0f / scale_x) > max_scale_up) { |
| DLOGV_IF(kTagResources, "Scaling up is over limit scale_x = %f", 1.0f / scale_x); |
| return kErrorNotSupported; |
| } |
| } |
| |
| if (UINT32(scale_y) < 1 && scale_y > 0.0f) { |
| if ((1.0f / scale_y) > max_scale_up) { |
| DLOGV_IF(kTagResources, "Scaling up is over limit scale_y = %f", 1.0f / scale_y); |
| return kErrorNotSupported; |
| } |
| } |
| |
| DLOGV_IF(kTagResources, "scale_x = %.4f, scale_y = %.4f", scale_x, scale_y); |
| |
| return kErrorNone; |
| } |
| |
| DisplayError ResourceDefault::GetScaleFactor(const LayerRect &crop, const LayerRect &dst, |
| float *scale_x, float *scale_y) { |
| float crop_width = crop.right - crop.left; |
| float crop_height = crop.bottom - crop.top; |
| float dst_width = dst.right - dst.left; |
| float dst_height = dst.bottom - dst.top; |
| |
| *scale_x = crop_width / dst_width; |
| *scale_y = crop_height / dst_height; |
| |
| return kErrorNone; |
| } |
| |
| DisplayError ResourceDefault::SetDecimationFactor(HWPipeInfo *pipe) { |
| float src_h = pipe->src_roi.bottom - pipe->src_roi.top; |
| float dst_h = pipe->dst_roi.bottom - pipe->dst_roi.top; |
| float down_scale_h = src_h / dst_h; |
| |
| float src_w = pipe->src_roi.right - pipe->src_roi.left; |
| float dst_w = pipe->dst_roi.right - pipe->dst_roi.left; |
| float down_scale_w = src_w / dst_w; |
| |
| pipe->horizontal_decimation = 0; |
| pipe->vertical_decimation = 0; |
| |
| if (CalculateDecimation(down_scale_w, &pipe->horizontal_decimation) != kErrorNone) { |
| return kErrorNotSupported; |
| } |
| |
| if (CalculateDecimation(down_scale_h, &pipe->vertical_decimation) != kErrorNone) { |
| return kErrorNotSupported; |
| } |
| |
| DLOGI_IF(kTagResources, "horizontal_decimation %d, vertical_decimation %d", |
| pipe->horizontal_decimation, pipe->vertical_decimation); |
| |
| return kErrorNone; |
| } |
| |
| void ResourceDefault::SplitRect(const LayerRect &src_rect, const LayerRect &dst_rect, |
| LayerRect *src_left, LayerRect *dst_left, LayerRect *src_right, |
| LayerRect *dst_right) { |
| // Split rectangle horizontally and evenly into two. |
| float src_width = src_rect.right - src_rect.left; |
| float dst_width = dst_rect.right - dst_rect.left; |
| float src_width_ori = src_width; |
| src_width = ROUND_UP_ALIGN_DOWN(src_width / 2, 1); |
| dst_width = ROUND_UP_ALIGN_DOWN(dst_width * src_width / src_width_ori, 1); |
| |
| src_left->left = src_rect.left; |
| src_left->right = src_rect.left + src_width; |
| src_right->left = src_left->right; |
| src_right->right = src_rect.right; |
| |
| src_left->top = src_rect.top; |
| src_left->bottom = src_rect.bottom; |
| src_right->top = src_rect.top; |
| src_right->bottom = src_rect.bottom; |
| |
| dst_left->top = dst_rect.top; |
| dst_left->bottom = dst_rect.bottom; |
| dst_right->top = dst_rect.top; |
| dst_right->bottom = dst_rect.bottom; |
| |
| dst_left->left = dst_rect.left; |
| dst_left->right = dst_rect.left + dst_width; |
| dst_right->left = dst_left->right; |
| dst_right->right = dst_rect.right; |
| } |
| |
| DisplayError ResourceDefault::AlignPipeConfig(const Layer *layer, HWPipeInfo *left_pipe, |
| HWPipeInfo *right_pipe) { |
| DisplayError error = kErrorNone; |
| if (!left_pipe->valid) { |
| DLOGE_IF(kTagResources, "left_pipe should not be invalid"); |
| return kErrorNotSupported; |
| } |
| |
| bool ubwc_tiled = IsUBWCFormat(layer->input_buffer->format); |
| error = ValidatePipeParams(left_pipe, ubwc_tiled); |
| if (error != kErrorNone) { |
| goto PipeConfigExit; |
| } |
| |
| if (right_pipe->valid) { |
| // Make sure the left and right ROI are conjunct |
| right_pipe->src_roi.left = left_pipe->src_roi.right; |
| right_pipe->dst_roi.left = left_pipe->dst_roi.right; |
| error = ValidatePipeParams(right_pipe, ubwc_tiled); |
| } |
| |
| PipeConfigExit: |
| if (error != kErrorNone) { |
| DLOGV_IF(kTagResources, "AlignPipeConfig failed"); |
| } |
| return error; |
| } |
| |
| 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; |
| } |
| |
| DisplayError ResourceDefault::ValidateCursorConfig(Handle display_ctx, const Layer *layer, |
| bool is_top) { |
| return kErrorNotSupported; |
| } |
| |
| DisplayError ResourceDefault::ValidateCursorPosition(Handle display_ctx, HWLayers *hw_layers, |
| int x, int y) { |
| return kErrorNotSupported; |
| } |
| |
| DisplayError ResourceDefault::SetMaxBandwidthMode(HWBwModes mode) { |
| return kErrorNotSupported; |
| } |
| |
| DisplayError ResourceDefault::GetScaleLutConfig(HWScaleLutInfo *lut_info) { |
| return kErrorNone; |
| } |
| |
| } // namespace sdm |