blob: e5ffda99c2fe483290564958f62e53f8a548c7bf [file] [log] [blame]
/*
* 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 <core/dump_interface.h>
#include <core/buffer_allocator.h>
#include <utils/constants.h>
#include <utils/String16.h>
#include <cutils/properties.h>
#include <hardware_legacy/uevent.h>
#include <sys/resource.h>
#include <sys/prctl.h>
#include <binder/Parcel.h>
#include <QService.h>
#include <gr.h>
#include <gralloc_priv.h>
#include <display_config.h>
#include "hwc_buffer_allocator.h"
#include "hwc_buffer_sync_handler.h"
#include "hwc_session.h"
#include "hwc_debugger.h"
#define __CLASS__ "HWCSession"
#define HWC_UEVENT_SWITCH_HDMI "change@/devices/virtual/switch/hdmi"
#define HWC_UEVENT_GRAPHICS_FB0 "change@/devices/virtual/graphics/fb0"
static sdm::HWCSession::HWCModuleMethods g_hwc_module_methods;
hwc_module_t HAL_MODULE_INFO_SYM = {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 2,
version_minor: 0,
id: HWC_HARDWARE_MODULE_ID,
name: "QTI Hardware Composer Module",
author: "CodeAurora Forum",
methods: &g_hwc_module_methods,
dso: 0,
reserved: {0},
}
};
namespace sdm {
Locker HWCSession::locker_;
bool HWCSession::reset_panel_ = false;
HWCSession::HWCSession(const hw_module_t *module) : core_intf_(NULL), hwc_procs_(NULL),
display_primary_(NULL), display_external_(NULL), display_virtual_(NULL),
uevent_thread_exit_(false), uevent_thread_name_("HWC_UeventThread") {
hwc_composer_device_1_t::common.tag = HARDWARE_DEVICE_TAG;
hwc_composer_device_1_t::common.version = HWC_DEVICE_API_VERSION_1_4;
hwc_composer_device_1_t::common.module = const_cast<hw_module_t*>(module);
hwc_composer_device_1_t::common.close = Close;
hwc_composer_device_1_t::prepare = Prepare;
hwc_composer_device_1_t::set = Set;
hwc_composer_device_1_t::eventControl = EventControl;
hwc_composer_device_1_t::setPowerMode = SetPowerMode;
hwc_composer_device_1_t::query = Query;
hwc_composer_device_1_t::registerProcs = RegisterProcs;
hwc_composer_device_1_t::dump = Dump;
hwc_composer_device_1_t::getDisplayConfigs = GetDisplayConfigs;
hwc_composer_device_1_t::getDisplayAttributes = GetDisplayAttributes;
hwc_composer_device_1_t::getActiveConfig = GetActiveConfig;
hwc_composer_device_1_t::setActiveConfig = SetActiveConfig;
}
int HWCSession::Init() {
int status = -EINVAL;
const char *qservice_name = "display.qservice";
// Start QService and connect to it.
qService::QService::init();
android::sp<qService::IQService> qservice = android::interface_cast<qService::IQService>(
android::defaultServiceManager()->getService(android::String16(qservice_name)));
if (qservice.get()) {
qservice->connect(android::sp<qClient::IQClient>(this));
} else {
DLOGE("Failed to acquire %s", qservice_name);
return -EINVAL;
}
buffer_allocator_ = new HWCBufferAllocator();
if (buffer_allocator_ == NULL) {
DLOGE("Display core initialization failed due to no memory");
return -ENOMEM;
}
buffer_sync_handler_ = new HWCBufferSyncHandler();
if (buffer_sync_handler_ == NULL) {
DLOGE("Display core initialization failed due to no memory");
return -ENOMEM;
}
DisplayError error = CoreInterface::CreateCore(this, HWCDebugHandler::Get(), buffer_allocator_,
buffer_sync_handler_, &core_intf_);
if (error != kErrorNone) {
DLOGE("Display core initialization failed. Error = %d", error);
return -EINVAL;
}
// Create and power on primary display
display_primary_ = new HWCDisplayPrimary(core_intf_, &hwc_procs_);
if (!display_primary_) {
CoreInterface::DestroyCore();
return -ENOMEM;
}
status = display_primary_->Init();
if (status) {
CoreInterface::DestroyCore();
delete display_primary_;
return status;
}
status = display_primary_->SetPowerMode(HWC_POWER_MODE_NORMAL);
if (status) {
display_primary_->Deinit();
delete display_primary_;
CoreInterface::DestroyCore();
return status;
}
if (pthread_create(&uevent_thread_, NULL, &HWCUeventThread, this) < 0) {
DLOGE("Failed to start = %s, error = %s", uevent_thread_name_);
display_primary_->Deinit();
delete display_primary_;
CoreInterface::DestroyCore();
return -errno;
}
SetFrameBufferResolution(HWC_DISPLAY_PRIMARY, NULL);
return 0;
}
int HWCSession::Deinit() {
display_primary_->SetPowerMode(HWC_POWER_MODE_OFF);
display_primary_->Deinit();
delete display_primary_;
uevent_thread_exit_ = true;
pthread_join(uevent_thread_, NULL);
DisplayError error = CoreInterface::DestroyCore();
if (error != kErrorNone) {
DLOGE("Display core de-initialization failed. Error = %d", error);
}
return 0;
}
int HWCSession::Open(const hw_module_t *module, const char *name, hw_device_t **device) {
if (!module || !name || !device) {
DLOGE("Invalid parameters.");
return -EINVAL;
}
if (!strcmp(name, HWC_HARDWARE_COMPOSER)) {
HWCSession *hwc_session = new HWCSession(module);
if (!hwc_session) {
return -ENOMEM;
}
int status = hwc_session->Init();
if (status != 0) {
delete hwc_session;
return status;
}
hwc_composer_device_1_t *composer_device = hwc_session;
*device = reinterpret_cast<hw_device_t *>(composer_device);
}
return 0;
}
int HWCSession::Close(hw_device_t *device) {
if (!device) {
return -EINVAL;
}
hwc_composer_device_1_t *composer_device = reinterpret_cast<hwc_composer_device_1_t *>(device);
HWCSession *hwc_session = static_cast<HWCSession *>(composer_device);
hwc_session->Deinit();
delete hwc_session;
return 0;
}
int HWCSession::Prepare(hwc_composer_device_1 *device, size_t num_displays,
hwc_display_contents_1_t **displays) {
DTRACE_SCOPED();
SEQUENCE_ENTRY_SCOPE_LOCK(locker_);
if (!device || !displays) {
return -EINVAL;
}
HWCSession *hwc_session = static_cast<HWCSession *>(device);
if (reset_panel_) {
DLOGW("panel is in bad state, resetting the panel");
hwc_session->ResetPanel();
}
for (ssize_t i = (num_displays-1); i >= 0; i--) {
hwc_display_contents_1_t *content_list = displays[i];
switch (i) {
case HWC_DISPLAY_PRIMARY:
hwc_session->display_primary_->Prepare(content_list);
break;
case HWC_DISPLAY_EXTERNAL:
if (hwc_session->display_external_) {
hwc_session->display_external_->Prepare(content_list);
}
break;
case HWC_DISPLAY_VIRTUAL:
if (hwc_session->display_external_) {
break;
}
if (hwc_session->ValidateContentList(content_list)) {
hwc_session->CreateVirtualDisplay(content_list);
} else {
hwc_session->DestroyVirtualDisplay();
}
if (hwc_session->display_virtual_) {
hwc_session->display_virtual_->Prepare(content_list);
}
break;
default:
break;
}
}
// Return 0, else client will go into bad state
return 0;
}
int HWCSession::Set(hwc_composer_device_1 *device, size_t num_displays,
hwc_display_contents_1_t **displays) {
DTRACE_SCOPED();
SEQUENCE_EXIT_SCOPE_LOCK(locker_);
if (!device || !displays) {
return -EINVAL;
}
HWCSession *hwc_session = static_cast<HWCSession *>(device);
for (size_t i = 0; i < num_displays; i++) {
hwc_display_contents_1_t *content_list = displays[i];
switch (i) {
case HWC_DISPLAY_PRIMARY:
hwc_session->display_primary_->Commit(content_list);
break;
case HWC_DISPLAY_EXTERNAL:
if (hwc_session->display_external_) {
hwc_session->display_external_->Commit(content_list);
}
break;
case HWC_DISPLAY_VIRTUAL:
if (hwc_session->display_external_) {
if (content_list) {
for (size_t i = 0; i < content_list->numHwLayers; i++) {
if (content_list->hwLayers[i].acquireFenceFd >= 0) {
close(content_list->hwLayers[i].acquireFenceFd);
content_list->hwLayers[i].acquireFenceFd = -1;
}
}
if (content_list->outbufAcquireFenceFd >= 0) {
close(content_list->outbufAcquireFenceFd);
content_list->outbufAcquireFenceFd = -1;
}
content_list->retireFenceFd = -1;
}
}
if (hwc_session->display_virtual_) {
hwc_session->display_virtual_->Commit(content_list);
}
break;
default:
break;
}
}
// Return 0, else client will go into bad state
return 0;
}
int HWCSession::EventControl(hwc_composer_device_1 *device, int disp, int event, int enable) {
SEQUENCE_WAIT_SCOPE_LOCK(locker_);
if (!device) {
return -EINVAL;
}
HWCSession *hwc_session = static_cast<HWCSession *>(device);
int status = -EINVAL;
switch (disp) {
case HWC_DISPLAY_PRIMARY:
status = hwc_session->display_primary_->EventControl(event, enable);
break;
case HWC_DISPLAY_EXTERNAL:
if (hwc_session->display_external_) {
status = hwc_session->display_external_->EventControl(event, enable);
}
break;
case HWC_DISPLAY_VIRTUAL:
break;
default:
status = -EINVAL;
}
return status;
}
int HWCSession::SetPowerMode(hwc_composer_device_1 *device, int disp, int mode) {
SEQUENCE_WAIT_SCOPE_LOCK(locker_);
if (!device) {
return -EINVAL;
}
HWCSession *hwc_session = static_cast<HWCSession *>(device);
int status = -EINVAL;
switch (disp) {
case HWC_DISPLAY_PRIMARY:
status = hwc_session->display_primary_->SetPowerMode(mode);
// Set the power mode for virtual display while setting power mode for primary, as surfaceflinger
// does not invoke SetPowerMode() for virtual display.
case HWC_DISPLAY_VIRTUAL:
if (hwc_session->display_virtual_) {
status = hwc_session->display_virtual_->SetPowerMode(mode);
}
break;
case HWC_DISPLAY_EXTERNAL:
if (hwc_session->display_external_) {
status = hwc_session->display_external_->SetPowerMode(mode);
}
break;
default:
status = -EINVAL;
}
return status;
}
int HWCSession::Query(hwc_composer_device_1 *device, int param, int *value) {
SEQUENCE_WAIT_SCOPE_LOCK(locker_);
if (!device || !value) {
return -EINVAL;
}
return -EINVAL;
}
void HWCSession::RegisterProcs(hwc_composer_device_1 *device, hwc_procs_t const *procs) {
if (!device || !procs) {
return;
}
HWCSession *hwc_session = static_cast<HWCSession *>(device);
hwc_session->hwc_procs_ = procs;
}
void HWCSession::Dump(hwc_composer_device_1 *device, char *buffer, int length) {
SEQUENCE_WAIT_SCOPE_LOCK(locker_);
if (!device || !buffer || !length) {
return;
}
DumpInterface::GetDump(buffer, length);
}
int HWCSession::GetDisplayConfigs(hwc_composer_device_1 *device, int disp, uint32_t *configs,
size_t *num_configs) {
SEQUENCE_WAIT_SCOPE_LOCK(locker_);
if (!device || !configs || !num_configs) {
return -EINVAL;
}
HWCSession *hwc_session = static_cast<HWCSession *>(device);
int status = -EINVAL;
switch (disp) {
case HWC_DISPLAY_PRIMARY:
status = hwc_session->display_primary_->GetDisplayConfigs(configs, num_configs);
break;
case HWC_DISPLAY_EXTERNAL:
if (hwc_session->display_external_) {
status = hwc_session->display_external_->GetDisplayConfigs(configs, num_configs);
}
break;
case HWC_DISPLAY_VIRTUAL:
if (hwc_session->display_virtual_) {
status = hwc_session->display_virtual_->GetDisplayConfigs(configs, num_configs);
}
break;
default:
status = -EINVAL;
}
return status;
}
int HWCSession::GetDisplayAttributes(hwc_composer_device_1 *device, int disp, uint32_t config,
const uint32_t *attributes, int32_t *values) {
SEQUENCE_WAIT_SCOPE_LOCK(locker_);
if (!device || !attributes || !values) {
return -EINVAL;
}
HWCSession *hwc_session = static_cast<HWCSession *>(device);
int status = -EINVAL;
switch (disp) {
case HWC_DISPLAY_PRIMARY:
status = hwc_session->display_primary_->GetDisplayAttributes(config, attributes, values);
break;
case HWC_DISPLAY_EXTERNAL:
if (hwc_session->display_external_) {
status = hwc_session->display_external_->GetDisplayAttributes(config, attributes, values);
}
break;
case HWC_DISPLAY_VIRTUAL:
if (hwc_session->display_virtual_) {
status = hwc_session->display_virtual_->GetDisplayAttributes(config, attributes, values);
}
break;
default:
status = -EINVAL;
}
return status;
}
int HWCSession::GetActiveConfig(hwc_composer_device_1 *device, int disp) {
SEQUENCE_WAIT_SCOPE_LOCK(locker_);
if (!device) {
return -1;
}
HWCSession *hwc_session = static_cast<HWCSession *>(device);
int active_config = -1;
switch (disp) {
case HWC_DISPLAY_PRIMARY:
active_config = hwc_session->display_primary_->GetActiveConfig();
break;
case HWC_DISPLAY_EXTERNAL:
if (hwc_session->display_external_) {
active_config = hwc_session->display_external_->GetActiveConfig();
}
break;
case HWC_DISPLAY_VIRTUAL:
if (hwc_session->display_virtual_) {
active_config = hwc_session->display_virtual_->GetActiveConfig();
}
break;
default:
active_config = -1;
}
return active_config;
}
int HWCSession::SetActiveConfig(hwc_composer_device_1 *device, int disp, int index) {
SEQUENCE_WAIT_SCOPE_LOCK(locker_);
if (!device) {
return -EINVAL;
}
HWCSession *hwc_session = static_cast<HWCSession *>(device);
int status = -EINVAL;
switch (disp) {
case HWC_DISPLAY_PRIMARY:
status = hwc_session->display_primary_->SetActiveConfig(index);
break;
case HWC_DISPLAY_EXTERNAL:
if (hwc_session->display_external_) {
// TODO(user): Uncomment it. HDMI does not support resolution change currently.
status = 0; // hwc_session->display_external_->SetActiveConfig(index);
}
break;
case HWC_DISPLAY_VIRTUAL:
break;
default:
status = -EINVAL;
}
return status;
}
bool HWCSession::ValidateContentList(hwc_display_contents_1_t *content_list) {
return (content_list && content_list->numHwLayers > 0 && content_list->outbuf);
}
int HWCSession::CreateVirtualDisplay(hwc_display_contents_1_t *content_list) {
int status = 0;
if (!display_virtual_) {
// Create virtual display device
display_virtual_ = new HWCDisplayVirtual(core_intf_, &hwc_procs_);
if (!display_virtual_) {
// This is not catastrophic. Leave a warning message for now.
DLOGW("Virtual Display creation failed");
return -ENOMEM;
}
status = display_virtual_->Init();
if (status) {
goto CleanupOnError;
}
status = display_virtual_->SetPowerMode(HWC_POWER_MODE_NORMAL);
if (status) {
goto CleanupOnError;
}
}
if (display_virtual_) {
SetFrameBufferResolution(HWC_DISPLAY_VIRTUAL, content_list);
status = display_virtual_->SetActiveConfig(content_list);
}
return status;
CleanupOnError:
return DestroyVirtualDisplay();
}
int HWCSession::DestroyVirtualDisplay() {
int status = 0;
if (display_virtual_) {
status = display_virtual_->Deinit();
if (!status) {
delete display_virtual_;
display_virtual_ = NULL;
// Signal the HotPlug thread to continue with the external display connection
locker_.Signal();
}
}
return status;
}
DisplayError HWCSession::Hotplug(const CoreEventHotplug &hotplug) {
return kErrorNone;
}
android::status_t HWCSession::notifyCallback(uint32_t command, const android::Parcel *input_parcel,
android::Parcel */*output_parcel*/) {
SEQUENCE_WAIT_SCOPE_LOCK(locker_);
android::status_t status = 0;
switch (command) {
case qService::IQService::DYNAMIC_DEBUG:
DynamicDebug(input_parcel);
break;
case qService::IQService::SCREEN_REFRESH:
hwc_procs_->invalidate(hwc_procs_);
break;
case qService::IQService::SET_IDLE_TIMEOUT:
if (display_primary_) {
uint32_t timeout = UINT32(input_parcel->readInt32());
display_primary_->SetIdleTimeoutMs(timeout);
}
break;
case qService::IQService::SET_FRAME_DUMP_CONFIG:
SetFrameDumpConfig(input_parcel);
break;
case qService::IQService::SET_MAX_PIPES_PER_MIXER:
status = SetMaxMixerStages(input_parcel);
break;
case qService::IQService::SET_DISPLAY_MODE:
status = SetDisplayMode(input_parcel);
break;
case qService::IQService::SET_SECONDARY_DISPLAY_STATUS:
status = SetSecondaryDisplayStatus(input_parcel);
break;
case qService::IQService::CONFIGURE_DYN_REFRESH_RATE:
status = ConfigureRefreshRate(input_parcel);
break;
default:
DLOGW("QService command = %d is not supported", command);
return -EINVAL;
}
return status;
}
android::status_t HWCSession::SetSecondaryDisplayStatus(const android::Parcel *input_parcel) {
uint32_t display_id = UINT32(input_parcel->readInt32());
uint32_t display_status = UINT32(input_parcel->readInt32());
HWCDisplay *display = NULL;
DLOGI("Display %d Status %d", display_id, display_status);
switch (display_id) {
case HWC_DISPLAY_EXTERNAL:
display = display_external_;
break;
case HWC_DISPLAY_VIRTUAL:
display = display_virtual_;
break;
default:
DLOGW("Not supported for display %d", display_id);
return -EINVAL;
}
return display->SetDisplayStatus(display_status);
}
android::status_t HWCSession::ConfigureRefreshRate(const android::Parcel *input_parcel) {
uint32_t operation = UINT32(input_parcel->readInt32());
switch (operation) {
case qdutils::DISABLE_METADATA_DYN_REFRESH_RATE:
display_primary_->SetMetaDataRefreshRateFlag(false);
break;
case qdutils::ENABLE_METADATA_DYN_REFRESH_RATE:
display_primary_->SetMetaDataRefreshRateFlag(true);
break;
case qdutils::SET_BINDER_DYN_REFRESH_RATE:
{
uint32_t refresh_rate = UINT32(input_parcel->readInt32());
display_primary_->SetRefreshRate(refresh_rate);
break;
}
default:
DLOGW("Invalid operation %d", operation);
return -EINVAL;
}
return 0;
}
android::status_t HWCSession::SetDisplayMode(const android::Parcel *input_parcel) {
DisplayError error = kErrorNone;
uint32_t mode = UINT32(input_parcel->readInt32());
error = display_primary_->SetDisplayMode(mode);
if (error != kErrorNone) {
return -EINVAL;
}
return 0;
}
android::status_t HWCSession::SetMaxMixerStages(const android::Parcel *input_parcel) {
DisplayError error = kErrorNone;
uint32_t bit_mask_display_type = UINT32(input_parcel->readInt32());
uint32_t max_mixer_stages = UINT32(input_parcel->readInt32());
if (IS_BIT_SET(bit_mask_display_type, HWC_DISPLAY_PRIMARY)) {
if (display_primary_) {
error = display_primary_->SetMaxMixerStages(max_mixer_stages);
if (error != kErrorNone) {
return -EINVAL;
}
}
}
if (IS_BIT_SET(bit_mask_display_type, HWC_DISPLAY_EXTERNAL)) {
if (display_external_) {
error = display_external_->SetMaxMixerStages(max_mixer_stages);
if (error != kErrorNone) {
return -EINVAL;
}
}
}
if (IS_BIT_SET(bit_mask_display_type, HWC_DISPLAY_VIRTUAL)) {
if (display_virtual_) {
error = display_virtual_->SetMaxMixerStages(max_mixer_stages);
if (error != kErrorNone) {
return -EINVAL;
}
}
}
return 0;
}
void HWCSession::SetFrameDumpConfig(const android::Parcel *input_parcel) {
uint32_t frame_dump_count = UINT32(input_parcel->readInt32());
uint32_t bit_mask_display_type = UINT32(input_parcel->readInt32());
uint32_t bit_mask_layer_type = UINT32(input_parcel->readInt32());
if (IS_BIT_SET(bit_mask_display_type, HWC_DISPLAY_PRIMARY)) {
if (display_primary_) {
display_primary_->SetFrameDumpConfig(frame_dump_count, bit_mask_layer_type);
}
}
if (IS_BIT_SET(bit_mask_display_type, HWC_DISPLAY_EXTERNAL)) {
if (display_external_) {
display_external_->SetFrameDumpConfig(frame_dump_count, bit_mask_layer_type);
}
}
if (IS_BIT_SET(bit_mask_display_type, HWC_DISPLAY_VIRTUAL)) {
if (display_virtual_) {
display_virtual_->SetFrameDumpConfig(frame_dump_count, bit_mask_layer_type);
}
}
}
void HWCSession::DynamicDebug(const android::Parcel *input_parcel) {
int type = input_parcel->readInt32();
bool enable = (input_parcel->readInt32() > 0);
DLOGI("type = %d enable = %d", type, enable);
switch (type) {
case qService::IQService::DEBUG_ALL:
HWCDebugHandler::DebugAll(enable);
break;
case qService::IQService::DEBUG_MDPCOMP:
HWCDebugHandler::DebugStrategy(enable);
HWCDebugHandler::DebugCompManager(enable);
break;
case qService::IQService::DEBUG_PIPE_LIFECYCLE:
HWCDebugHandler::DebugResources(enable);
break;
case qService::IQService::DEBUG_DRIVER_CONFIG:
HWCDebugHandler::DebugDriverConfig(enable);
break;
case qService::IQService::DEBUG_ROTATOR:
HWCDebugHandler::DebugResources(enable);
HWCDebugHandler::DebugDriverConfig(enable);
HWCDebugHandler::DebugRotator(enable);
break;
default:
DLOGW("type = %d is not supported", type);
}
}
void* HWCSession::HWCUeventThread(void *context) {
if (context) {
return reinterpret_cast<HWCSession *>(context)->HWCUeventThreadHandler();
}
return NULL;
}
void* HWCSession::HWCUeventThreadHandler() {
static char uevent_data[PAGE_SIZE];
int length = 0;
prctl(PR_SET_NAME, uevent_thread_name_, 0, 0, 0);
setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
if (!uevent_init()) {
DLOGE("Failed to init uevent");
pthread_exit(0);
return NULL;
}
while (!uevent_thread_exit_) {
// keep last 2 zeroes to ensure double 0 termination
length = uevent_next_event(uevent_data, INT32(sizeof(uevent_data)) - 2);
if (strcasestr(HWC_UEVENT_SWITCH_HDMI, uevent_data)) {
DLOGI("Uevent HDMI = %s", uevent_data);
int connected = GetEventValue(uevent_data, length, "SWITCH_STATE=");
if (connected >= 0) {
DLOGI("HDMI = %s", connected ? "connected" : "disconnected");
if (HotPlugHandler(connected) == -1) {
DLOGE("Failed handling Hotplug = %s", connected ? "connected" : "disconnected");
}
}
} else if (strcasestr(HWC_UEVENT_GRAPHICS_FB0, uevent_data)) {
DLOGI("Uevent FB0 = %s", uevent_data);
int panel_reset = GetEventValue(uevent_data, length, "PANEL_ALIVE=");
if (panel_reset == 0) {
if (hwc_procs_) {
reset_panel_ = true;
hwc_procs_->invalidate(hwc_procs_);
} else {
DLOGW("Ignore resetpanel - hwc_proc not registered");
}
}
}
}
pthread_exit(0);
return NULL;
}
int HWCSession::GetEventValue(const char *uevent_data, int length, const char *event_info) {
const char *iterator_str = uevent_data;
while (((iterator_str - uevent_data) <= length) && (*iterator_str)) {
char *pstr = strstr(iterator_str, event_info);
if (pstr != NULL) {
return (atoi(iterator_str + strlen(event_info)));
}
iterator_str += strlen(iterator_str) + 1;
}
return -1;
}
void HWCSession::ResetPanel() {
int status = -EINVAL;
DLOGI("Powering off primary");
status = display_primary_->SetPowerMode(HWC_POWER_MODE_OFF);
if (status) {
DLOGE("power-off on primary failed with error = %d", status);
}
DLOGI("Restoring power mode on primary");
uint32_t mode = display_primary_->GetLastPowerMode();
status = display_primary_->SetPowerMode(mode);
if (status) {
DLOGE("Setting power mode = %d on primary failed with error = %d", mode, status);
}
status = display_primary_->EventControl(HWC_EVENT_VSYNC, 1);
if (status) {
DLOGE("enabling vsync failed for primary with error = %d", status);
}
reset_panel_ = false;
}
int HWCSession::HotPlugHandler(bool connected) {
if (!hwc_procs_) {
DLOGW("Ignore hotplug - hwc_proc not registered");
return -1;
}
if (connected) {
SEQUENCE_WAIT_SCOPE_LOCK(locker_);
if (display_virtual_) {
// Wait for the virtual display to tear down
int status = locker_.WaitFinite(kExternalConnectionTimeoutMs);
if (status != 0) {
DLOGE("Timed out while waiting for virtual display to tear down.");
return -1;
}
}
if (display_external_) {
DLOGE("HDMI already connected");
return -1;
}
// Create hdmi display
display_external_ = new HWCDisplayExternal(core_intf_, &hwc_procs_);
if (!display_external_) {
return -1;
}
int status = display_external_->Init();
if (status) {
delete display_external_;
display_external_ = NULL;
return -1;
}
SetFrameBufferResolution(HWC_DISPLAY_EXTERNAL, NULL);
} else {
SEQUENCE_WAIT_SCOPE_LOCK(locker_);
if (!display_external_) {
DLOGE("HDMI not connected");
return -1;
}
display_external_->SetPowerMode(HWC_POWER_MODE_OFF);
display_external_->Deinit();
delete display_external_;
display_external_ = NULL;
}
// notify client and trigger a screen refresh
hwc_procs_->hotplug(hwc_procs_, HWC_DISPLAY_EXTERNAL, connected);
hwc_procs_->invalidate(hwc_procs_);
return 0;
}
void HWCSession::SetFrameBufferResolution(int disp, hwc_display_contents_1_t *content_list) {
char property[PROPERTY_VALUE_MAX];
uint32_t primary_width = 0;
uint32_t primary_height = 0;
switch (disp) {
case HWC_DISPLAY_PRIMARY:
{
display_primary_->GetPanelResolution(&primary_width, &primary_height);
if (property_get("debug.hwc.fbsize", property, NULL) > 0) {
char *yptr = strcasestr(property, "x");
primary_width = atoi(property);
primary_height = atoi(yptr + 1);
}
display_primary_->SetFrameBufferResolution(primary_width, primary_height);
break;
}
case HWC_DISPLAY_EXTERNAL:
{
uint32_t external_width = 0;
uint32_t external_height = 0;
display_external_->GetPanelResolution(&external_width, &external_height);
if (property_get("sys.hwc.mdp_downscale_enabled", property, "false") &&
!strcmp(property, "true")) {
display_primary_->GetFrameBufferResolution(&primary_width, &primary_height);
uint32_t primary_area = primary_width * primary_height;
uint32_t external_area = external_width * external_height;
if (primary_area > external_area) {
if (primary_height > primary_width) {
Swap(primary_height, primary_width);
}
AdjustSourceResolution(primary_width, primary_height,
&external_width, &external_height);
}
}
display_external_->SetFrameBufferResolution(external_width, external_height);
break;
}
case HWC_DISPLAY_VIRTUAL:
{
if (ValidateContentList(content_list)) {
const private_handle_t *output_handle =
static_cast<const private_handle_t *>(content_list->outbuf);
int virtual_width = 0;
int virtual_height = 0;
getBufferSizeAndDimensions(output_handle->width, output_handle->height, output_handle->format,
virtual_width, virtual_height);
display_virtual_->SetFrameBufferResolution(virtual_width, virtual_height);
}
break;
}
default:
break;
}
}
void HWCSession::AdjustSourceResolution(uint32_t dst_width, uint32_t dst_height,
uint32_t *src_width, uint32_t *src_height) {
*src_height = (dst_width * (*src_height)) / (*src_width);
*src_width = dst_width;
}
} // namespace sdm