/* * Copyright (c) 2015 - 2019, 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hw_primary.h" #include "hw_color_manager.h" #define __CLASS__ "HWPrimary" #ifndef MDP_COMMIT_CWB_EN #define MDP_COMMIT_CWB_EN 0x800 #endif #ifndef MDP_COMMIT_CWB_DSPP #define MDP_COMMIT_CWB_DSPP 0x1000 #endif #ifndef MDP_COMMIT_AVR_EN #define MDP_COMMIT_AVR_EN 0x08 #endif #ifndef MDP_COMMIT_AVR_ONE_SHOT_MODE #define MDP_COMMIT_AVR_ONE_SHOT_MODE 0x10 #endif #ifndef MDP_COMMIT_PARTIAL_UPDATE_DUAL_ROI #define MDP_COMMIT_PARTIAL_UPDATE_DUAL_ROI 0x20 #endif namespace sdm { using std::string; using std::to_string; using std::fstream; HWPrimary::HWPrimary(BufferSyncHandler *buffer_sync_handler, HWInfoInterface *hw_info_intf) : HWDevice(buffer_sync_handler) { HWDevice::device_type_ = kDeviceBuiltIn; HWDevice::device_name_ = "BuiltIn Display Device"; HWDevice::hw_info_intf_ = hw_info_intf; } DisplayError HWPrimary::Init() { DisplayError error = kErrorNone; error = HWDevice::Init(); if (error != kErrorNone) { return error; } mdp_dest_scalar_data_.resize(hw_resource_.hw_dest_scalar_info.count); error = PopulateDisplayAttributes(); if (error != kErrorNone) { return error; } UpdateMixerAttributes(); // Need to enable HPD, but toggle at start when HDMI is external // This helps for framework reboot or adb shell stop/start EnableHotPlugDetection(0); EnableHotPlugDetection(1); InitializeConfigs(); avr_prop_disabled_ = Debug::IsAVRDisabled(); return error; } bool HWPrimary::GetCurrentModeFromSysfs(size_t *curr_x_pixels, size_t *curr_y_pixels) { bool ret = false; string mode_path = fb_path_ + string("0/mode"); Sys::fstream fs(mode_path, fstream::in); if (!fs.is_open()) { return false; } string line; if (Sys::getline_(fs, line)) { // String is of form "U:1600x2560p-0". Documentation/fb/modedb.txt in // kernel has more info on the format. size_t xpos = line.find(':'); size_t ypos = line.find('x'); if (xpos == string::npos || ypos == string::npos) { DLOGI("Resolution switch not supported"); } else { *curr_x_pixels = static_cast(atoi(line.c_str() + xpos + 1)); *curr_y_pixels = static_cast(atoi(line.c_str() + ypos + 1)); DLOGI("Current Config: %u x %u", *curr_x_pixels, *curr_y_pixels); ret = true; } } return ret; } void HWPrimary::InitializeConfigs() { size_t curr_x_pixels = 0; size_t curr_y_pixels = 0; if (!GetCurrentModeFromSysfs(&curr_x_pixels, &curr_y_pixels)) { return; } string modes_path = string(fb_path_) + string("0/modes"); Sys::fstream fs(modes_path, fstream::in); if (!fs.is_open()) { DLOGI("Unable to process modes"); return; } string line; while (Sys::getline_(fs, line)) { DisplayConfigVariableInfo config; // std::getline (unlike ::getline) removes \n while driver expects it in mode, so add back line += '\n'; size_t xpos = line.find(':'); size_t ypos = line.find('x'); if (xpos == string::npos || ypos == string::npos) { continue; } config.x_pixels = UINT32(atoi(line.c_str() + xpos + 1)); config.y_pixels = UINT32(atoi(line.c_str() + ypos + 1)); DLOGI("Found mode %d x %d", config.x_pixels, config.y_pixels); display_configs_.push_back(config); display_config_strings_.push_back(string(line.c_str())); if (curr_x_pixels == config.x_pixels && curr_y_pixels == config.y_pixels) { active_config_index_ = UINT32(display_configs_.size() - 1); DLOGI("Active config index %u", active_config_index_); } } } DisplayError HWPrimary::GetNumDisplayAttributes(uint32_t *count) { *count = IsResolutionSwitchEnabled() ? UINT32(display_configs_.size()) : 1; return kErrorNone; } DisplayError HWPrimary::GetActiveConfig(uint32_t *active_config_index) { *active_config_index = active_config_index_; return kErrorNone; } DisplayError HWPrimary::GetDisplayAttributes(uint32_t index, HWDisplayAttributes *display_attributes) { if (!display_attributes) { return kErrorParameters; } if (IsResolutionSwitchEnabled() && index >= display_configs_.size()) { return kErrorParameters; } *display_attributes = display_attributes_; if (IsResolutionSwitchEnabled()) { // Overwrite only the parent portion of object display_attributes->x_pixels = display_configs_.at(index).x_pixels; display_attributes->y_pixels = display_configs_.at(index).y_pixels; } return kErrorNone; } DisplayError HWPrimary::PopulateDisplayAttributes() { DTRACE_SCOPED(); // Variable screen info fb_var_screeninfo var_screeninfo = {}; if (Sys::ioctl_(device_fd_, FBIOGET_VSCREENINFO, &var_screeninfo) < 0) { IOCTL_LOGE(FBIOGET_VSCREENINFO, device_type_); return kErrorHardware; } // Frame rate msmfb_metadata meta_data = {}; meta_data.op = metadata_op_frame_rate; if (Sys::ioctl_(device_fd_, MSMFB_METADATA_GET, &meta_data) < 0) { IOCTL_LOGE(MSMFB_METADATA_GET, device_type_); return kErrorHardware; } // If driver doesn't return width/height information, default to 320 dpi if (INT(var_screeninfo.width) <= 0 || INT(var_screeninfo.height) <= 0) { var_screeninfo.width = UINT32(((FLOAT(var_screeninfo.xres) * 25.4f)/320.0f) + 0.5f); var_screeninfo.height = UINT32(((FLOAT(var_screeninfo.yres) * 25.4f)/320.0f) + 0.5f); DLOGW("Driver doesn't report panel physical width and height - defaulting to 320dpi"); } display_attributes_.x_pixels = var_screeninfo.xres; display_attributes_.y_pixels = var_screeninfo.yres; display_attributes_.v_front_porch = var_screeninfo.lower_margin; display_attributes_.v_back_porch = var_screeninfo.upper_margin; display_attributes_.v_pulse_width = var_screeninfo.vsync_len; uint32_t h_blanking = var_screeninfo.right_margin + var_screeninfo.left_margin + var_screeninfo.hsync_len; display_attributes_.h_total = var_screeninfo.xres + h_blanking; display_attributes_.x_dpi = (FLOAT(var_screeninfo.xres) * 25.4f) / FLOAT(var_screeninfo.width); display_attributes_.y_dpi = (FLOAT(var_screeninfo.yres) * 25.4f) / FLOAT(var_screeninfo.height); display_attributes_.fps = meta_data.data.panel_frame_rate; display_attributes_.vsync_period_ns = UINT32(1000000000L / display_attributes_.fps); display_attributes_.is_device_split = (hw_panel_info_.split_info.right_split || (var_screeninfo.xres > hw_resource_.max_mixer_width)); display_attributes_.h_total += (display_attributes_.is_device_split || hw_panel_info_.ping_pong_split)? h_blanking : 0; return kErrorNone; } DisplayError HWPrimary::SetDisplayAttributes(uint32_t index) { DisplayError ret = kErrorNone; if (!IsResolutionSwitchEnabled()) { return kErrorNotSupported; } if (index >= display_configs_.size()) { return kErrorParameters; } string mode_path = string(fb_path_) + string("0/mode"); int fd = Sys::open_(mode_path.c_str(), O_WRONLY); if (fd < 0) { DLOGE("Opening mode failed"); return kErrorNotSupported; } ssize_t written = Sys::pwrite_(fd, display_config_strings_.at(index).c_str(), display_config_strings_.at(index).length(), 0); if (written > 0) { DLOGI("Successfully set config %u", index); PopulateHWPanelInfo(); PopulateDisplayAttributes(); UpdateMixerAttributes(); active_config_index_ = index; } else { DLOGE("Writing config index %u failed with error: %s", index, strerror(errno)); ret = kErrorParameters; } Sys::close_(fd); return ret; } DisplayError HWPrimary::SetRefreshRate(uint32_t refresh_rate) { char node_path[kMaxStringLength] = {0}; if (hw_resource_.has_avr && !avr_prop_disabled_) { return kErrorNotSupported; } snprintf(node_path, sizeof(node_path), "%s%d/dynamic_fps", fb_path_, fb_node_index_); int fd = Sys::open_(node_path, O_WRONLY); if (fd < 0) { DLOGE("Failed to open %s with error %s", node_path, strerror(errno)); return kErrorFileDescriptor; } char refresh_rate_string[kMaxStringLength]; snprintf(refresh_rate_string, sizeof(refresh_rate_string), "%d", refresh_rate); DLOGI_IF(kTagDriverConfig, "Setting refresh rate = %d", refresh_rate); ssize_t len = Sys::pwrite_(fd, refresh_rate_string, strlen(refresh_rate_string), 0); if (len < 0) { DLOGE("Failed to write %d with error %s", refresh_rate, strerror(errno)); Sys::close_(fd); return kErrorUndefined; } Sys::close_(fd); DisplayError error = PopulateDisplayAttributes(); if (error != kErrorNone) { return error; } return kErrorNone; } DisplayError HWPrimary::GetConfigIndex(char *mode, uint32_t *index) { return HWDevice::GetConfigIndex(mode, index); } DisplayError HWPrimary::PowerOff(bool teardown) { if (Sys::ioctl_(device_fd_, FBIOBLANK, FB_BLANK_POWERDOWN) < 0) { IOCTL_LOGE(FB_BLANK_POWERDOWN, device_type_); return kErrorHardware; } auto_refresh_ = false; return kErrorNone; } DisplayError HWPrimary::Doze(const HWQosData &qos_data, int *release_fence) { if (Sys::ioctl_(device_fd_, FBIOBLANK, FB_BLANK_NORMAL) < 0) { IOCTL_LOGE(FB_BLANK_NORMAL, device_type_); return kErrorHardware; } return kErrorNone; } DisplayError HWPrimary::DozeSuspend(const HWQosData &qos_data, int *release_fence) { if (Sys::ioctl_(device_fd_, FBIOBLANK, FB_BLANK_VSYNC_SUSPEND) < 0) { IOCTL_LOGE(FB_BLANK_VSYNC_SUSPEND, device_type_); return kErrorHardware; } return kErrorNone; } DisplayError HWPrimary::Validate(HWLayers *hw_layers) { HWLayersInfo &hw_layer_info = hw_layers->info; LayerStack *stack = hw_layer_info.stack; HWDevice::ResetDisplayParams(); mdp_layer_commit_v1 &mdp_commit = mdp_disp_commit_.commit_v1; LayerRect left_roi = hw_layer_info.left_frame_roi.at(0); LayerRect right_roi = hw_layer_info.right_frame_roi.at(0); mdp_commit.left_roi.x = UINT32(left_roi.left); mdp_commit.left_roi.y = UINT32(left_roi.top); mdp_commit.left_roi.w = UINT32(left_roi.right - left_roi.left); mdp_commit.left_roi.h = UINT32(left_roi.bottom - left_roi.top); // Update second roi information in right_roi if (hw_layer_info.left_frame_roi.size() == 2) { mdp_commit.flags |= MDP_COMMIT_PARTIAL_UPDATE_DUAL_ROI; right_roi = hw_layer_info.left_frame_roi.at(1); } // SDM treats ROI as one full coordinate system. // In case source split is disabled, However, Driver assumes Mixer to operate in // different co-ordinate system. if (IsValid(right_roi)) { mdp_commit.right_roi.x = UINT32(right_roi.left); if (!hw_resource_.is_src_split) { mdp_commit.right_roi.x = UINT32(right_roi.left) - mixer_attributes_.split_left; } mdp_commit.right_roi.y = UINT32(right_roi.top); mdp_commit.right_roi.w = UINT32(right_roi.right - right_roi.left); mdp_commit.right_roi.h = UINT32(right_roi.bottom - right_roi.top); } if (stack->output_buffer && hw_resource_.has_concurrent_writeback) { LayerBuffer *output_buffer = stack->output_buffer; mdp_out_layer_.writeback_ndx = hw_resource_.writeback_index; mdp_out_layer_.buffer.width = output_buffer->unaligned_width; mdp_out_layer_.buffer.height = output_buffer->unaligned_height; mdp_out_layer_.buffer.comp_ratio.denom = 1000; mdp_out_layer_.buffer.comp_ratio.numer = UINT32(hw_layers->output_compression * 1000); mdp_out_layer_.buffer.fence = -1; #ifdef OUT_LAYER_COLOR_SPACE SetCSC(output_buffer->color_metadata, &mdp_out_layer_.color_space); #endif SetFormat(output_buffer->format, &mdp_out_layer_.buffer.format); mdp_commit.flags |= MDP_COMMIT_CWB_EN; mdp_commit.flags |= (stack->flags.post_processed_output) ? MDP_COMMIT_CWB_DSPP : 0; DLOGI_IF(kTagDriverConfig, "****************** Conc WB Output buffer Info ******************"); DLOGI_IF(kTagDriverConfig, "out_w %d, out_h %d, out_f %d, wb_id %d DSPP output %d", mdp_out_layer_.buffer.width, mdp_out_layer_.buffer.height, mdp_out_layer_.buffer.format, mdp_out_layer_.writeback_ndx, stack->flags.post_processed_output); DLOGI_IF(kTagDriverConfig, "****************************************************************"); } if (hw_resource_.has_avr) { SetAVRFlags(hw_layers->hw_avr_info, &mdp_commit.flags); } return HWDevice::Validate(hw_layers); } DisplayError HWPrimary::Commit(HWLayers *hw_layers) { LayerBuffer *output_buffer = hw_layers->info.stack->output_buffer; if (hw_resource_.has_concurrent_writeback && output_buffer) { if (output_buffer->planes[0].fd >= 0) { mdp_out_layer_.buffer.planes[0].fd = output_buffer->planes[0].fd; mdp_out_layer_.buffer.planes[0].offset = output_buffer->planes[0].offset; SetStride(device_type_, output_buffer->format, output_buffer->planes[0].stride, &mdp_out_layer_.buffer.planes[0].stride); mdp_out_layer_.buffer.plane_count = 1; mdp_out_layer_.buffer.fence = -1; DLOGI_IF(kTagDriverConfig, "****************** Conc WB Output buffer Info ****************"); DLOGI_IF(kTagDriverConfig, "out_fd %d, out_offset %d, out_stride %d", mdp_out_layer_.buffer.planes[0].fd, mdp_out_layer_.buffer.planes[0].offset, mdp_out_layer_.buffer.planes[0].stride); DLOGI_IF(kTagDriverConfig, "**************************************************************"); } else { DLOGE("Invalid output buffer fd"); return kErrorParameters; } } DisplayError ret = HWDevice::Commit(hw_layers); if (ret == kErrorNone && hw_resource_.has_concurrent_writeback && output_buffer) { output_buffer->release_fence_fd = mdp_out_layer_.buffer.fence; } return ret; } void HWPrimary::SetIdleTimeoutMs(uint32_t timeout_ms) { char node_path[kMaxStringLength] = {0}; DLOGI_IF(kTagDriverConfig, "Setting idle timeout to = %d ms", timeout_ms); snprintf(node_path, sizeof(node_path), "%s%d/idle_time", fb_path_, fb_node_index_); // Open a sysfs node to send the timeout value to driver. int fd = Sys::open_(node_path, O_WRONLY); if (fd < 0) { DLOGE("Unable to open %s, node %s", node_path, strerror(errno)); return; } char timeout_string[64]; snprintf(timeout_string, sizeof(timeout_string), "%d", timeout_ms); // Notify driver about the timeout value ssize_t length = Sys::pwrite_(fd, timeout_string, strlen(timeout_string), 0); if (length <= 0) { DLOGE("Unable to write into %s, node %s", node_path, strerror(errno)); } Sys::close_(fd); } DisplayError HWPrimary::SetVSyncState(bool enable) { DTRACE_SCOPED(); return HWDevice::SetVSyncState(enable); } DisplayError HWPrimary::SetDisplayMode(const HWDisplayMode hw_display_mode) { uint32_t mode = kModeDefault; switch (hw_display_mode) { case kModeVideo: mode = kModeLPMVideo; break; case kModeCommand: mode = kModeLPMCommand; break; default: DLOGW("Failed to translate SDE display mode %d to a MSMFB_LPM_ENABLE mode", hw_display_mode); return kErrorParameters; } if (Sys::ioctl_(device_fd_, INT(MSMFB_LPM_ENABLE), &mode) < 0) { IOCTL_LOGE(MSMFB_LPM_ENABLE, device_type_); return kErrorHardware; } DLOGI("Triggering display mode change to %d on next commit.", hw_display_mode); synchronous_commit_ = true; return kErrorNone; } DisplayError HWPrimary::SetPanelBrightness(int level) { char buffer[kMaxSysfsCommandLength] = {0}; DLOGV_IF(kTagDriverConfig, "Set brightness level to %d", level); int fd = Sys::open_(kBrightnessNode, O_RDWR); if (fd < 0) { DLOGV_IF(kTagDriverConfig, "Failed to open node = %s, error = %s ", kBrightnessNode, strerror(errno)); return kErrorFileDescriptor; } int32_t bytes = snprintf(buffer, kMaxSysfsCommandLength, "%d\n", level); if (bytes < 0) { DLOGV_IF(kTagDriverConfig, "Failed to copy new brightness level = %d", level); Sys::close_(fd); return kErrorUndefined; } ssize_t ret = Sys::pwrite_(fd, buffer, static_cast(bytes), 0); if (ret <= 0) { DLOGV_IF(kTagDriverConfig, "Failed to write to node = %s, error = %s ", kBrightnessNode, strerror(errno)); Sys::close_(fd); return kErrorUndefined; } Sys::close_(fd); return kErrorNone; } DisplayError HWPrimary::GetPanelBrightness(int *level) { char brightness[kMaxStringLength] = {0}; if (!level) { DLOGV_IF(kTagDriverConfig, "Invalid input, null pointer."); return kErrorParameters; } int fd = Sys::open_(kBrightnessNode, O_RDWR); if (fd < 0) { DLOGV_IF(kTagDriverConfig, "Failed to open brightness node = %s, error = %s", kBrightnessNode, strerror(errno)); return kErrorFileDescriptor; } if (Sys::pread_(fd, brightness, sizeof(brightness), 0) > 0) { *level = atoi(brightness); DLOGV_IF(kTagDriverConfig, "Brightness level = %d", *level); } Sys::close_(fd); return kErrorNone; } DisplayError HWPrimary::SetAutoRefresh(bool enable) { const int kWriteLength = 2; char buffer[kWriteLength] = {'\0'}; ssize_t bytes = snprintf(buffer, kWriteLength, "%d", enable); if (enable == auto_refresh_) { return kErrorNone; } if (HWDevice::SysFsWrite(kAutoRefreshNode, buffer, bytes) <= 0) { // Returns bytes written return kErrorUndefined; } auto_refresh_ = enable; return kErrorNone; } DisplayError HWPrimary::GetPPFeaturesVersion(PPFeatureVersion *vers) { mdp_pp_feature_version version = {}; #ifdef PA_DITHER uint32_t feature_id_mapping[kMaxNumPPFeatures] = { PCC, IGC, GC, GC, PA, DITHER, GAMUT, PA_DITHER }; #else uint32_t feature_id_mapping[kMaxNumPPFeatures] = { PCC, IGC, GC, GC, PA, DITHER, GAMUT }; #endif for (int i(0); i < kMaxNumPPFeatures; i++) { version.pp_feature = feature_id_mapping[i]; if (Sys::ioctl_(device_fd_, INT(MSMFB_MDP_PP_GET_FEATURE_VERSION), &version) < 0) { IOCTL_LOGE(MSMFB_MDP_PP_GET_FEATURE_VERSION, device_type_); return kErrorHardware; } vers->version[i] = version.version_info; } return kErrorNone; } // It was entered with PPFeaturesConfig::locker_ being hold. DisplayError HWPrimary::SetPPFeatures(PPFeaturesConfig *feature_list) { msmfb_mdp_pp kernel_params = {}; int ret = 0; PPFeatureInfo *feature = NULL; while (true) { ret = feature_list->RetrieveNextFeature(&feature); if (ret) break; if (feature) { DLOGV_IF(kTagDriverConfig, "feature_id = %d", feature->feature_id_); if ((feature->feature_id_ < kMaxNumPPFeatures)) { HWColorManager::SetFeature[feature->feature_id_](*feature, &kernel_params); if (Sys::ioctl_(device_fd_, INT(MSMFB_MDP_PP), &kernel_params) < 0) { IOCTL_LOGE(MSMFB_MDP_PP, device_type_); feature_list->Reset(); return kErrorHardware; } } } } // while(true) // Once all features were consumed, then destroy all feature instance from feature_list, // Then mark it as non-dirty of PPFeaturesConfig cache. feature_list->Reset(); return kErrorNone; } DisplayError HWPrimary::SetMixerAttributes(const HWMixerAttributes &mixer_attributes) { if (IsResolutionSwitchEnabled()) { return kErrorNotSupported; } return HWDevice::SetMixerAttributes(mixer_attributes); } void HWPrimary::UpdateMixerAttributes() { mixer_attributes_.width = display_attributes_.x_pixels; mixer_attributes_.height = display_attributes_.y_pixels; mixer_attributes_.split_left = display_attributes_.is_device_split ? hw_panel_info_.split_info.left_split : mixer_attributes_.width; } void HWPrimary::SetAVRFlags(const HWAVRInfo &hw_avr_info, uint32_t *avr_flags) { // TODO(user): Add explicit cont. flag. if (hw_avr_info.mode == kOneShotMode) { *avr_flags |= MDP_COMMIT_AVR_ONE_SHOT_MODE | MDP_COMMIT_AVR_EN; } } } // namespace sdm