diff options
Diffstat (limited to 'msm8909/sdm/libs/core/fb/hw_events.cpp')
-rw-r--r-- | msm8909/sdm/libs/core/fb/hw_events.cpp | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/msm8909/sdm/libs/core/fb/hw_events.cpp b/msm8909/sdm/libs/core/fb/hw_events.cpp new file mode 100644 index 00000000..52a80112 --- /dev/null +++ b/msm8909/sdm/libs/core/fb/hw_events.cpp @@ -0,0 +1,237 @@ +/* +* Copyright (c) 2015 - 2017, 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 <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/resource.h> +#include <sys/prctl.h> +#include <utils/debug.h> +#include <utils/sys.h> +#include <pthread.h> +#include <algorithm> +#include <vector> +#include <map> +#include <utility> + +#include "hw_events.h" + +#define __CLASS__ "HWEvents" + +namespace sdm { + +pollfd HWEvents::InitializePollFd(HWEventData *event_data) { + char node_path[kMaxStringLength] = {0}; + char data[kMaxStringLength] = {0}; + pollfd poll_fd = {0}; + poll_fd.fd = -1; + + if (event_data->event_type == HWEvent::EXIT) { + // Create an eventfd to be used to unblock the poll system call when + // a thread is exiting. + poll_fd.fd = Sys::eventfd_(0, 0); + poll_fd.events |= POLLIN; + exit_fd_ = poll_fd.fd; + } else { + snprintf(node_path, sizeof(node_path), "%s%d/%s", fb_path_, fb_num_, + map_event_to_node_[event_data->event_type]); + poll_fd.fd = Sys::open_(node_path, O_RDONLY); + poll_fd.events |= POLLPRI | POLLERR; + } + + if (poll_fd.fd < 0) { + DLOGW("open failed for display=%d event=%s, error=%s", fb_num_, + map_event_to_node_[event_data->event_type], strerror(errno)); + return poll_fd; + } + + // Read once on all fds to clear data on all fds. + Sys::pread_(poll_fd.fd, data , kMaxStringLength, 0); + + return poll_fd; +} + +DisplayError HWEvents::SetEventParser(HWEvent event_type, HWEventData *event_data) { + DisplayError error = kErrorNone; + switch (event_type) { + case HWEvent::VSYNC: + event_data->event_parser = &HWEvents::HandleVSync; + break; + case HWEvent::IDLE_NOTIFY: + event_data->event_parser = &HWEvents::HandleIdleTimeout; + break; + case HWEvent::EXIT: + event_data->event_parser = &HWEvents::HandleThreadExit; + break; + case HWEvent::SHOW_BLANK_EVENT: + event_data->event_parser = &HWEvents::HandleBlank; + break; + case HWEvent::THERMAL_LEVEL: + event_data->event_parser = &HWEvents::HandleThermal; + break; + case HWEvent::IDLE_POWER_COLLAPSE: + event_data->event_parser = &HWEvents::HandleIdlePowerCollapse; + break; + default: + error = kErrorParameters; + break; + } + + return error; +} + +void HWEvents::PopulateHWEventData() { + for (uint32_t i = 0; i < event_list_.size(); i++) { + HWEventData event_data; + event_data.event_type = event_list_[i]; + SetEventParser(event_list_[i], &event_data); + poll_fds_[i] = InitializePollFd(&event_data); + event_data_list_.push_back(event_data); + } +} + +DisplayError HWEvents::Init(int fb_num, HWEventHandler *event_handler, + const vector<HWEvent> &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_); + map_event_to_node_ = {{HWEvent::VSYNC, "vsync_event"}, {HWEvent::EXIT, "thread_exit"}, + {HWEvent::IDLE_NOTIFY, "idle_notify"}, {HWEvent::SHOW_BLANK_EVENT, "show_blank_event"}, + {HWEvent::THERMAL_LEVEL, "msm_fb_thermal_level"}, {HWEvent::IDLE_POWER_COLLAPSE, "idle_power_collapse"}}; + + 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 HWEvents::Deinit() { + exit_threads_ = true; + Sys::pthread_cancel_(event_thread_); + + uint64_t exit_value = 1; + ssize_t write_size = Sys::write_(exit_fd_, &exit_value, sizeof(uint64_t)); + if (write_size != sizeof(uint64_t)) + DLOGW("Error triggering exit_fd_ (%d). write size = %d, error = %s", exit_fd_, write_size, + strerror(errno)); + + pthread_join(event_thread_, NULL); + + for (uint32_t i = 0; i < event_list_.size(); i++) { + Sys::close_(poll_fds_[i].fd); + poll_fds_[i].fd = -1; + } + + return kErrorNone; +} + +void* HWEvents::DisplayEventThread(void *context) { + if (context) { + return reinterpret_cast<HWEvents *>(context)->DisplayEventHandler(); + } + + return NULL; +} + +void* HWEvents::DisplayEventHandler() { + char data[kMaxStringLength] = {0}; + + prctl(PR_SET_NAME, event_thread_name_.c_str(), 0, 0, 0); + setpriority(PRIO_PROCESS, 0, kThreadPriorityUrgent); + + while (!exit_threads_) { + int error = Sys::poll_(poll_fds_.data(), UINT32(event_list_.size()), -1); + + if (error <= 0) { + DLOGW("poll failed. error = %s", strerror(errno)); + continue; + } + + for (uint32_t event = 0; event < event_list_.size(); event++) { + pollfd &poll_fd = poll_fds_[event]; + + if (event_list_.at(event) == HWEvent::EXIT) { + if ((poll_fd.revents & POLLIN) && (Sys::read_(poll_fd.fd, data, kMaxStringLength) > 0)) { + (this->*(event_data_list_[event]).event_parser)(data); + } + } else { + if ((poll_fd.revents & POLLPRI) && + (Sys::pread_(poll_fd.fd, data, kMaxStringLength, 0) > 0)) { + (this->*(event_data_list_[event]).event_parser)(data); + } + } + } + } + + pthread_exit(0); + + return NULL; +} + +void HWEvents::HandleVSync(char *data) { + int64_t timestamp = 0; + if (!strncmp(data, "VSYNC=", strlen("VSYNC="))) { + timestamp = strtoll(data + strlen("VSYNC="), NULL, 0); + } + + event_handler_->VSync(timestamp); +} + +void HWEvents::HandleIdleTimeout(char *data) { + event_handler_->IdleTimeout(); +} + +void HWEvents::HandleThermal(char *data) { + int64_t thermal_level = 0; + if (!strncmp(data, "thermal_level=", strlen("thermal_level="))) { + thermal_level = strtoll(data + strlen("thermal_level="), NULL, 0); + } + + DLOGI("Received thermal notification with thermal level = %d", thermal_level); + + event_handler_->ThermalEvent(thermal_level); +} + +void HWEvents::HandleIdlePowerCollapse(char *data) { + event_handler_->IdlePowerCollapse(); +} + +} // namespace sdm |