| /* |
| * Copyright (c) 2014 - 2015, 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 <stdio.h> |
| #include <utils/constants.h> |
| #include <utils/debug.h> |
| |
| #include "display_base.h" |
| |
| #define __CLASS__ "DisplayBase" |
| |
| namespace sdm { |
| |
| // TODO(user): Have a single structure handle carries all the interface pointers and variables. |
| DisplayBase::DisplayBase(DisplayType display_type, DisplayEventHandler *event_handler, |
| HWDeviceType hw_device_type, BufferSyncHandler *buffer_sync_handler, |
| CompManager *comp_manager, RotatorInterface *rotator_intf) |
| : display_type_(display_type), event_handler_(event_handler), hw_device_type_(hw_device_type), |
| buffer_sync_handler_(buffer_sync_handler), comp_manager_(comp_manager), |
| rotator_intf_(rotator_intf), state_(kStateOff), hw_device_(0), display_comp_ctx_(0), |
| display_attributes_(NULL), num_modes_(0), active_mode_index_(0), pending_commit_(false), |
| vsync_enable_(false), underscan_supported_(false) { |
| } |
| |
| DisplayError DisplayBase::Init() { |
| DisplayError error = kErrorNone; |
| hw_panel_info_ = HWPanelInfo(); |
| hw_intf_->GetHWPanelInfo(&hw_panel_info_); |
| |
| error = hw_intf_->GetNumDisplayAttributes(&num_modes_); |
| if (error != kErrorNone) { |
| goto CleanupOnError; |
| } |
| |
| display_attributes_ = new HWDisplayAttributes[num_modes_]; |
| if (!display_attributes_) { |
| error = kErrorMemory; |
| goto CleanupOnError; |
| } |
| |
| for (uint32_t i = 0; i < num_modes_; i++) { |
| error = hw_intf_->GetDisplayAttributes(&display_attributes_[i], i); |
| if (error != kErrorNone) { |
| goto CleanupOnError; |
| } |
| } |
| |
| active_mode_index_ = GetBestConfig(); |
| |
| error = hw_intf_->SetDisplayAttributes(active_mode_index_); |
| if (error != kErrorNone) { |
| goto CleanupOnError; |
| } |
| |
| error = comp_manager_->RegisterDisplay(display_type_, display_attributes_[active_mode_index_], |
| hw_panel_info_, &display_comp_ctx_); |
| if (error != kErrorNone) { |
| goto CleanupOnError; |
| } |
| |
| if (rotator_intf_) { |
| error = rotator_intf_->RegisterDisplay(display_type_, &display_rotator_ctx_); |
| if (error != kErrorNone) { |
| goto CleanupOnError; |
| } |
| } |
| |
| return kErrorNone; |
| |
| CleanupOnError: |
| if (display_comp_ctx_) { |
| comp_manager_->UnregisterDisplay(display_comp_ctx_); |
| } |
| |
| if (display_attributes_) { |
| delete[] display_attributes_; |
| display_attributes_ = NULL; |
| } |
| |
| hw_intf_->Close(); |
| |
| return error; |
| } |
| |
| DisplayError DisplayBase::Deinit() { |
| if (rotator_intf_) { |
| rotator_intf_->UnregisterDisplay(display_rotator_ctx_); |
| } |
| |
| comp_manager_->UnregisterDisplay(display_comp_ctx_); |
| |
| if (display_attributes_) { |
| delete[] display_attributes_; |
| display_attributes_ = NULL; |
| } |
| |
| hw_intf_->Close(); |
| |
| return kErrorNone; |
| } |
| |
| DisplayError DisplayBase::Prepare(LayerStack *layer_stack) { |
| DisplayError error = kErrorNone; |
| |
| if (!layer_stack) { |
| return kErrorParameters; |
| } |
| |
| pending_commit_ = false; |
| |
| if (state_ == kStateOn) { |
| // Clean hw layers for reuse. |
| hw_layers_.info = HWLayersInfo(); |
| hw_layers_.info.stack = layer_stack; |
| hw_layers_.output_compression = 1.0f; |
| |
| comp_manager_->PrePrepare(display_comp_ctx_, &hw_layers_); |
| while (true) { |
| error = comp_manager_->Prepare(display_comp_ctx_, &hw_layers_); |
| if (error != kErrorNone) { |
| break; |
| } |
| |
| if (IsRotationRequired(&hw_layers_)) { |
| if (!rotator_intf_) { |
| continue; |
| } |
| error = rotator_intf_->Prepare(display_rotator_ctx_, &hw_layers_); |
| } else { |
| // Release all the previous rotator sessions. |
| if (rotator_intf_) { |
| error = rotator_intf_->Purge(display_rotator_ctx_, &hw_layers_); |
| } |
| } |
| |
| if (error == kErrorNone) { |
| error = hw_intf_->Validate(&hw_layers_); |
| if (error == kErrorNone) { |
| // Strategy is successful now, wait for Commit(). |
| pending_commit_ = true; |
| break; |
| } |
| } |
| } |
| comp_manager_->PostPrepare(display_comp_ctx_, &hw_layers_); |
| } else { |
| return kErrorNotSupported; |
| } |
| |
| return error; |
| } |
| |
| DisplayError DisplayBase::Commit(LayerStack *layer_stack) { |
| DisplayError error = kErrorNone; |
| |
| if (!layer_stack) { |
| return kErrorParameters; |
| } |
| |
| if (state_ != kStateOn) { |
| return kErrorNotSupported; |
| } |
| |
| if (!pending_commit_) { |
| DLOGE("Commit: Corresponding Prepare() is not called for display = %d", display_type_); |
| return kErrorUndefined; |
| } |
| |
| pending_commit_ = false; |
| |
| if (rotator_intf_ && IsRotationRequired(&hw_layers_)) { |
| error = rotator_intf_->Commit(display_rotator_ctx_, &hw_layers_); |
| if (error != kErrorNone) { |
| return error; |
| } |
| } |
| |
| error = hw_intf_->Commit(&hw_layers_); |
| if (error != kErrorNone) { |
| return error; |
| } |
| |
| if (rotator_intf_ && IsRotationRequired(&hw_layers_)) { |
| error = rotator_intf_->PostCommit(display_rotator_ctx_, &hw_layers_); |
| if (error != kErrorNone) { |
| return error; |
| } |
| } |
| |
| error = comp_manager_->PostCommit(display_comp_ctx_, &hw_layers_); |
| if (error != kErrorNone) { |
| return error; |
| } |
| |
| return kErrorNone; |
| } |
| |
| DisplayError DisplayBase::Flush() { |
| DisplayError error = kErrorNone; |
| |
| if (state_ != kStateOn) { |
| return kErrorNone; |
| } |
| |
| hw_layers_.info.count = 0; |
| error = hw_intf_->Flush(); |
| if (error == kErrorNone) { |
| comp_manager_->Purge(display_comp_ctx_); |
| pending_commit_ = false; |
| } else { |
| DLOGV("Failed to flush device."); |
| } |
| |
| return error; |
| } |
| |
| DisplayError DisplayBase::GetDisplayState(DisplayState *state) { |
| if (!state) { |
| return kErrorParameters; |
| } |
| |
| *state = state_; |
| return kErrorNone; |
| } |
| |
| DisplayError DisplayBase::GetNumVariableInfoConfigs(uint32_t *count) { |
| if (!count) { |
| return kErrorParameters; |
| } |
| |
| *count = num_modes_; |
| |
| return kErrorNone; |
| } |
| |
| DisplayError DisplayBase::GetConfig(DisplayConfigFixedInfo *fixed_info) { |
| if (!fixed_info) { |
| return kErrorParameters; |
| } |
| |
| return kErrorNone; |
| } |
| |
| DisplayError DisplayBase::GetConfig(uint32_t index, DisplayConfigVariableInfo *variable_info) { |
| if (!variable_info || index >= num_modes_) { |
| return kErrorParameters; |
| } |
| |
| *variable_info = display_attributes_[index]; |
| |
| return kErrorNone; |
| } |
| |
| DisplayError DisplayBase::GetActiveConfig(uint32_t *index) { |
| if (!index) { |
| return kErrorParameters; |
| } |
| |
| *index = active_mode_index_; |
| |
| return kErrorNone; |
| } |
| |
| DisplayError DisplayBase::GetVSyncState(bool *enabled) { |
| if (!enabled) { |
| return kErrorParameters; |
| } |
| |
| return kErrorNone; |
| } |
| |
| bool DisplayBase::IsUnderscanSupported() { |
| return underscan_supported_; |
| } |
| |
| DisplayError DisplayBase::SetDisplayState(DisplayState state) { |
| DisplayError error = kErrorNone; |
| |
| DLOGI("Set state = %d, display %d", state, display_type_); |
| |
| if (state == state_) { |
| DLOGI("Same state transition is requested."); |
| return kErrorNone; |
| } |
| |
| switch (state) { |
| case kStateOff: |
| hw_layers_.info.count = 0; |
| error = hw_intf_->Flush(); |
| if (error == kErrorNone) { |
| comp_manager_->Purge(display_comp_ctx_); |
| |
| error = hw_intf_->PowerOff(); |
| } |
| break; |
| |
| case kStateOn: |
| error = hw_intf_->PowerOn(); |
| break; |
| |
| case kStateDoze: |
| error = hw_intf_->Doze(); |
| break; |
| |
| case kStateDozeSuspend: |
| error = hw_intf_->DozeSuspend(); |
| break; |
| |
| case kStateStandby: |
| error = hw_intf_->Standby(); |
| break; |
| |
| default: |
| DLOGE("Spurious state = %d transition requested.", state); |
| break; |
| } |
| |
| if (error == kErrorNone) { |
| state_ = state; |
| } |
| |
| return error; |
| } |
| |
| DisplayError DisplayBase::SetActiveConfig(uint32_t index) { |
| DisplayError error = kErrorNone; |
| |
| if (index >= num_modes_) { |
| return kErrorParameters; |
| } |
| |
| error = hw_intf_->SetDisplayAttributes(index); |
| if (error != kErrorNone) { |
| return error; |
| } |
| |
| active_mode_index_ = index; |
| |
| if (display_comp_ctx_) { |
| comp_manager_->UnregisterDisplay(display_comp_ctx_); |
| } |
| |
| error = comp_manager_->RegisterDisplay(display_type_, display_attributes_[index], hw_panel_info_, |
| &display_comp_ctx_); |
| |
| return error; |
| } |
| |
| DisplayError DisplayBase::SetMaxMixerStages(uint32_t max_mixer_stages) { |
| DisplayError error = kErrorNone; |
| |
| if (comp_manager_) { |
| error = comp_manager_->SetMaxMixerStages(display_comp_ctx_, max_mixer_stages); |
| } |
| |
| return error; |
| } |
| |
| DisplayError DisplayBase::SetDisplayMode(uint32_t mode) { |
| return kErrorNotSupported; |
| } |
| |
| DisplayError DisplayBase::IsScalingValid(const LayerRect &crop, const LayerRect &dst, |
| bool rotate90) { |
| return comp_manager_->ValidateScaling(crop, dst, rotate90); |
| } |
| |
| void DisplayBase::AppendDump(char *buffer, uint32_t length) { |
| DumpImpl::AppendString(buffer, length, "\n-----------------------"); |
| DumpImpl::AppendString(buffer, length, "\ndevice type: %u", display_type_); |
| DumpImpl::AppendString(buffer, length, "\nstate: %u, vsync on: %u", state_, INT(vsync_enable_)); |
| DumpImpl::AppendString(buffer, length, "\nnum configs: %u, active config index: %u", |
| num_modes_, active_mode_index_); |
| |
| DisplayConfigVariableInfo &info = display_attributes_[active_mode_index_]; |
| DumpImpl::AppendString(buffer, length, "\nres:%u x %u, dpi:%.2f x %.2f, fps:%.2f," |
| "vsync period: %u", info.x_pixels, info.y_pixels, info.x_dpi, |
| info.y_dpi, info.fps, info.vsync_period_ns); |
| |
| DumpImpl::AppendString(buffer, length, "\n"); |
| |
| uint32_t num_layers = 0; |
| uint32_t num_hw_layers = 0; |
| if (hw_layers_.info.stack) { |
| num_layers = hw_layers_.info.stack->layer_count; |
| num_hw_layers = hw_layers_.info.count; |
| } |
| |
| if (num_hw_layers == 0) { |
| DumpImpl::AppendString(buffer, length, "\nNo hardware layers programmed"); |
| return; |
| } |
| |
| HWLayersInfo &layer_info = hw_layers_.info; |
| LayerRect &l_roi = layer_info.left_partial_update; |
| LayerRect &r_roi = layer_info.right_partial_update; |
| DumpImpl::AppendString(buffer, length, "\nROI(L T R B) : LEFT(%d %d %d %d), RIGHT(%d %d %d %d)", |
| INT(l_roi.left), INT(l_roi.top), INT(l_roi.right), INT(l_roi.bottom), |
| INT(r_roi.left), INT(r_roi.top), INT(r_roi.right), INT(r_roi.bottom)); |
| |
| const char *header = "\n| Idx | Comp Type | Split | WB | Pipe | W x H | Format | Src Rect (L T R B) | Dst Rect (L T R B) | Z | Flags | Deci(HxV) |"; //NOLINT |
| const char *newline = "\n|-----|-------------|--------|----|-------|-------------|--------------------|---------------------|---------------------|----|------------|-----------|"; //NOLINT |
| const char *format = "\n| %3s | %11s " "| %6s " "| %2s | 0x%03x | %4d x %4d | %18s " "| %4d %4d %4d %4d " "| %4d %4d %4d %4d " "| %2s | %10s " "| %9s |"; //NOLINT |
| |
| DumpImpl::AppendString(buffer, length, "\n"); |
| DumpImpl::AppendString(buffer, length, newline); |
| DumpImpl::AppendString(buffer, length, header); |
| DumpImpl::AppendString(buffer, length, newline); |
| |
| for (uint32_t i = 0; i < num_hw_layers; i++) { |
| uint32_t layer_index = hw_layers_.info.index[i]; |
| Layer &layer = hw_layers_.info.stack->layers[layer_index]; |
| LayerBuffer *input_buffer = layer.input_buffer; |
| HWLayerConfig &layer_config = hw_layers_.config[i]; |
| HWRotatorSession &hw_rotator_session = layer_config.hw_rotator_session; |
| |
| char idx[8] = { 0 }; |
| const char *comp_type = GetName(layer.composition); |
| const char *buffer_format = GetName(input_buffer->format); |
| const char *rotate_split[2] = { "Rot-L", "Rot-R" }; |
| const char *comp_split[2] = { "Comp-L", "Comp-R" }; |
| |
| snprintf(idx, sizeof(idx), "%d", layer_index); |
| |
| for (uint32_t count = 0; count < hw_rotator_session.hw_block_count; count++) { |
| char writeback_id[8]; |
| HWRotateInfo &rotate = hw_rotator_session.hw_rotate_info[count]; |
| LayerRect &src_roi = rotate.src_roi; |
| LayerRect &dst_roi = rotate.dst_roi; |
| |
| snprintf(writeback_id, sizeof(writeback_id), "%d", rotate.writeback_id); |
| |
| DumpImpl::AppendString(buffer, length, format, idx, comp_type, rotate_split[count], |
| writeback_id, rotate.pipe_id, input_buffer->width, |
| input_buffer->height, buffer_format, INT(src_roi.left), |
| INT(src_roi.top), INT(src_roi.right), INT(src_roi.bottom), |
| INT(dst_roi.left), INT(dst_roi.top), INT(dst_roi.right), |
| INT(dst_roi.bottom), "-", "- ", "- "); |
| |
| // print the below only once per layer block, fill with spaces for rest. |
| idx[0] = 0; |
| comp_type = ""; |
| } |
| |
| if (hw_rotator_session.hw_block_count > 0) { |
| input_buffer = &hw_rotator_session.output_buffer; |
| buffer_format = GetName(input_buffer->format); |
| } |
| |
| for (uint32_t count = 0; count < 2; count++) { |
| char decimation[16]; |
| char flags[16]; |
| char z_order[8]; |
| HWPipeInfo &pipe = (count == 0) ? layer_config.left_pipe : layer_config.right_pipe; |
| |
| if (!pipe.valid) { |
| continue; |
| } |
| |
| LayerRect &src_roi = pipe.src_roi; |
| LayerRect &dst_roi = pipe.dst_roi; |
| |
| snprintf(z_order, sizeof(z_order), "%d", pipe.z_order); |
| snprintf(flags, sizeof(flags), "0x%08x", layer.flags.flags); |
| snprintf(decimation, sizeof(decimation), "%3d x %3d", pipe.horizontal_decimation, |
| pipe.vertical_decimation); |
| |
| DumpImpl::AppendString(buffer, length, format, idx, comp_type, comp_split[count], |
| "-", pipe.pipe_id, input_buffer->width, input_buffer->height, |
| buffer_format, INT(src_roi.left), INT(src_roi.top), |
| INT(src_roi.right), INT(src_roi.bottom), INT(dst_roi.left), |
| INT(dst_roi.top), INT(dst_roi.right), INT(dst_roi.bottom), |
| z_order, flags, decimation); |
| |
| // print the below only once per layer block, fill with spaces for rest. |
| idx[0] = 0; |
| comp_type = ""; |
| } |
| |
| DumpImpl::AppendString(buffer, length, newline); |
| } |
| } |
| |
| int DisplayBase::GetBestConfig() { |
| return (num_modes_ == 1) ? 0 : -1; |
| } |
| |
| bool DisplayBase::IsRotationRequired(HWLayers *hw_layers) { |
| HWLayersInfo &layer_info = hw_layers->info; |
| |
| for (uint32_t i = 0; i < layer_info.count; i++) { |
| Layer& layer = layer_info.stack->layers[layer_info.index[i]]; |
| HWRotatorSession *hw_rotator_session = &hw_layers->config[i].hw_rotator_session; |
| |
| if (hw_rotator_session->hw_block_count) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| const char * DisplayBase::GetName(const LayerComposition &composition) { |
| switch (composition) { |
| case kCompositionGPU: return "GPU"; |
| case kCompositionSDE: return "SDE"; |
| case kCompositionGPUTarget: return "GPU_TARGET"; |
| default: return "UNKNOWN"; |
| } |
| } |
| |
| const char * DisplayBase::GetName(const LayerBufferFormat &format) { |
| switch (format) { |
| case kFormatARGB8888: return "ARGB_8888"; |
| case kFormatRGBA8888: return "RGBA_8888"; |
| case kFormatBGRA8888: return "BGRA_8888"; |
| case kFormatXRGB8888: return "XRGB_8888"; |
| case kFormatRGBX8888: return "RGBX_8888"; |
| case kFormatBGRX8888: return "BGRX_8888"; |
| case kFormatRGBA5551: return "RGBA_5551"; |
| case kFormatRGBA4444: return "RGBA_4444"; |
| case kFormatRGB888: return "RGB_888"; |
| case kFormatBGR888: return "BGR_888"; |
| case kFormatRGB565: return "RGB_565"; |
| case kFormatRGBA8888Ubwc: return "RGBA_8888_UBWC"; |
| case kFormatRGBX8888Ubwc: return "RGBX_8888_UBWC"; |
| case kFormatRGB565Ubwc: return "RGB_565_UBWC"; |
| case kFormatYCbCr420Planar: return "Y_CB_CR_420"; |
| case kFormatYCrCb420Planar: return "Y_CR_CB_420"; |
| case kFormatYCbCr420SemiPlanar: return "Y_CBCR_420"; |
| case kFormatYCrCb420SemiPlanar: return "Y_CRCB_420"; |
| case kFormatYCbCr420SemiPlanarVenus: return "Y_CBCR_420_VENUS"; |
| case kFormatYCbCr422H1V2SemiPlanar: return "Y_CBCR_422_H1V2"; |
| case kFormatYCrCb422H1V2SemiPlanar: return "Y_CRCB_422_H1V2"; |
| case kFormatYCbCr422H2V1SemiPlanar: return "Y_CBCR_422_H2V1"; |
| case kFormatYCrCb422H2V1SemiPlanar: return "Y_CRCB_422_H2V2"; |
| case kFormatYCbCr420SPVenusUbwc: return "Y_CBCR_420_VENUS_UBWC"; |
| case kFormatYCbCr422H2V1Packed: return "YCBYCR_422_H2V1"; |
| default: return "UNKNOWN"; |
| } |
| } |
| |
| } // namespace sdm |