blob: 22c199d9a3a0e34a25198263d194d9d6b811ac70 [file] [log] [blame]
/*
* Copyright (C) 2016 The CyanogenMod Project
* Copyright (C) 2017-2018 The LineageOS Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// #define LOG_NDEBUG 0
#define LOG_TAG "LiveDisplay-SDM"
#include "impl/SDM.h"
#include "Utils.h"
#include "controller/SDMController.h"
#include <android-base/logging.h>
#include <android-base/properties.h>
namespace {
constexpr int kDppsBufSize = 64;
enum {
FEATURE_VER_SW_PA_API = 0x00000001,
FEATURE_VER_SW_SAVEMODES_API = 0x00000004,
};
constexpr char kFossProperty[] = "ro.qualcomm.foss";
constexpr char kFossOn[] = "foss:on";
constexpr char kFossOff[] = "foss:off";
// For use when only sysfs modes are available
constexpr int kStandardNodeId = 600;
constexpr char kSrgbNode[] = "/sys/class/graphics/fb0/srgb";
constexpr int kSrgbNodeId = 601;
constexpr char kDciP3Node[] = "/sys/class/graphics/fb0/dci_p3";
constexpr int kDciP3NodeId = 602;
enum {
PRIV_MODE_FLAG_SDM = 1,
PRIV_MODE_FLAG_SYSFS,
};
struct hsic_config {
uint32_t flags;
vendor::lineage::livedisplay::V1_0::HSIC data;
};
struct hsic_float_range {
float max;
float min;
float step;
};
struct hsic_int_range {
int32_t max;
int32_t min;
uint32_t step;
};
struct hsic_ranges {
uint32_t flags;
struct hsic_int_range hue;
struct hsic_float_range saturation;
struct hsic_float_range intensity;
struct hsic_float_range contrast;
struct hsic_float_range saturationThreshold;
};
} // anonymous namespace
namespace vendor {
namespace lineage {
namespace livedisplay {
namespace V1_0 {
namespace implementation {
using ::android::BAD_VALUE;
using ::android::NO_INIT;
using ::android::OK;
using ::android::sp;
using ::android::status_t;
SDM::SDM() : mController(nullptr) {
}
SDM::~SDM() {
}
status_t SDM::initialize() {
mController = std::make_unique<SDMController>();
if (mController == nullptr) {
LOG(ERROR) << "Failed to create SDMController";
return NO_INIT;
}
status_t rc = mController->init(&mHandle, 0);
if (rc != OK) {
return rc;
}
mActiveModeId = -1;
if (hasFeature(Feature::DISPLAY_MODES)) {
rc = saveInitialDisplayMode();
if (rc != OK) {
LOG(ERROR) << "Failed to save initial display mode! err=" << rc;
return rc;
}
sp<disp_mode> defMode = getDefaultDisplayMode();
if (defMode != nullptr) {
setDisplayMode(defMode->id, false);
}
}
mFOSSEnabled = android::base::GetBoolProperty(kFossProperty, false);
return OK;
}
status_t SDM::deinitialize() {
mController->deinit(mHandle, 0);
mHandle = 0;
mController = nullptr;
return OK;
}
uint32_t SDM::getNumSDMDisplayModes() {
uint32_t flags = 0;
int32_t count = 0;
if (mController->get_num_display_modes(mHandle, 0, 0, &count, &flags)) {
count = 0;
}
return count;
}
uint32_t SDM::getNumDisplayModes() {
int32_t count = getNumSDMDisplayModes();
if (getLocalSRGBMode() != nullptr) {
count++;
}
if (getLocalDCIP3Mode() != nullptr) {
count++;
}
return count;
}
sp<disp_mode> SDM::getDisplayModeById(int32_t id) {
std::vector<sp<disp_mode>> profiles;
status_t rc = getDisplayModes(profiles);
if (rc == OK) {
for (const auto& mode : profiles) {
if (id == mode->id) {
return mode;
}
}
}
return nullptr;
}
status_t SDM::setModeState(sp<disp_mode> mode, bool state) {
int32_t id = 0;
if (mode->privFlags == PRIV_MODE_FLAG_SYSFS) {
if (mode->id != kStandardNodeId) {
LOG(VERBOSE) << "sysfs node: " << mode->privData << " state=" << state;
return Utils::writeInt(mode->privData.c_str(), state ? 1 : 0);
} else {
// NOOP
return OK;
}
} else if (mode->privFlags == PRIV_MODE_FLAG_SDM) {
if (state) {
return mController->set_active_display_mode(mHandle, 0, mode->id, 0);
} else {
if (Utils::readInitialModeId(&id) == OK) {
LOG(VERBOSE) << "set sdm mode to default: id" << id;
return mController->set_active_display_mode(mHandle, 0, id, 0);
}
}
}
return BAD_VALUE;
}
status_t SDM::getDisplayModes(std::vector<sp<disp_mode>>& profiles) {
status_t rc = OK;
uint32_t count = getNumDisplayModes();
if (!count) return rc;
uint32_t sdm_count = getNumSDMDisplayModes();
if (sdm_count == 0) {
// sysfs only case, create a dummy standard mode
const sp<disp_mode> m = new disp_mode;
m->id = kStandardNodeId;
m->name = "standard";
m->privFlags = PRIV_MODE_FLAG_SYSFS;
m->privData = "";
profiles.push_back(m);
} else {
struct sdm_mode {
int32_t id;
int32_t type;
int32_t len;
char* name;
sdm_mode() : id(-1), type(0), len(128) {
name = new char[128];
}
~sdm_mode() {
delete name;
}
};
sdm_mode tmp[sdm_count];
uint32_t flags = 0;
rc = mController->get_display_modes(mHandle, 0, 0, tmp, sdm_count, &flags);
if (rc == 0) {
for (uint32_t i = 0; i < sdm_count; i++) {
const sp<disp_mode> m = new disp_mode;
m->id = tmp[i].id;
m->name = tmp[i].name;
m->privFlags = PRIV_MODE_FLAG_SDM;
profiles.push_back(m);
}
}
}
sp<disp_mode> srgb = getLocalSRGBMode();
sp<disp_mode> dci_p3 = getLocalDCIP3Mode();
if (srgb != nullptr) {
profiles.push_back(srgb);
}
if (dci_p3 != nullptr) {
profiles.push_back(dci_p3);
}
return rc;
}
sp<disp_mode> SDM::getCurrentDisplayMode() {
return getDisplayModeById(mActiveModeId);
}
sp<disp_mode> SDM::getDefaultDisplayMode() {
int32_t id = 0;
if (Utils::readLocalModeId(&id) == OK && id >= 0) {
return getDisplayModeById(id);
}
if (Utils::readInitialModeId(&id) == OK && id >= 0) {
return getDisplayModeById(id);
}
return nullptr;
}
sp<disp_mode> SDM::getLocalSRGBMode() {
if (access(kSrgbNode, W_OK) != 0) {
return nullptr;
}
sp<disp_mode> m = new disp_mode;
m->id = kSrgbNodeId;
m->name = "srgb";
m->privFlags = PRIV_MODE_FLAG_SYSFS;
m->privData = kSrgbNode;
return m;
}
sp<disp_mode> SDM::getLocalDCIP3Mode() {
if (access(kDciP3Node, W_OK) != 0) {
return nullptr;
}
sp<disp_mode> m = new disp_mode;
m->id = kDciP3NodeId;
m->name = "dci_p3";
m->privFlags = PRIV_MODE_FLAG_SYSFS;
m->privData = kDciP3Node;
return m;
}
status_t SDM::getPictureAdjustmentRanges(HSICRanges& ranges) {
hsic_ranges r;
memset(&r, 0, sizeof(struct hsic_ranges));
status_t rc = mController->get_global_pa_range(mHandle, 0, &r);
if (rc == OK) {
ranges.hue.min = r.hue.min;
ranges.hue.max = r.hue.max;
ranges.hue.step = r.hue.step;
ranges.saturation.min = r.saturation.min;
ranges.saturation.max = r.saturation.max;
ranges.saturation.step = r.saturation.step;
ranges.intensity.min = r.intensity.min;
ranges.intensity.max = r.intensity.max;
ranges.intensity.step = r.intensity.step;
ranges.contrast.min = r.contrast.min;
ranges.contrast.max = r.contrast.max;
ranges.contrast.step = r.contrast.step;
ranges.saturationThreshold.min = r.saturationThreshold.min;
ranges.saturationThreshold.max = r.saturationThreshold.max;
ranges.saturationThreshold.step = r.saturationThreshold.step;
}
return rc;
}
bool SDM::hasFeature(Feature feature) {
uint32_t id = 0, flags = 0;
struct version {
uint8_t x, y;
uint16_t z;
};
version v;
switch (feature) {
case Feature::DISPLAY_MODES:
id = FEATURE_VER_SW_SAVEMODES_API;
break;
case Feature::PICTURE_ADJUSTMENT:
id = FEATURE_VER_SW_PA_API;
break;
case Feature::ADAPTIVE_BACKLIGHT:
if (mFOSSEnabled) {
return true;
}
break;
default:
return false;
}
if (mController->get_feature_version(mHandle, id, &v, &flags) != OK) {
return false;
}
if (v.x <= 0 && v.y <= 0 && v.z <= 0) {
return false;
}
if (feature == Feature::DISPLAY_MODES) {
return getNumDisplayModes() > 0;
} else if (feature == Feature::PICTURE_ADJUSTMENT) {
HSICRanges r;
if (getPictureAdjustmentRanges(r) == OK && r.isValid()) {
return true;
}
}
return false;
}
status_t SDM::saveInitialDisplayMode() {
int32_t id = 0;
uint32_t flags = 0;
if (Utils::readInitialModeId(&id) != OK || id < 0) {
if (mController->get_default_display_mode(mHandle, 0, &id, &flags) == OK &&
id >= 0) {
return Utils::writeInitialModeId(id);
} else {
return Utils::writeInitialModeId(0);
}
}
return OK;
}
status_t SDM::getPictureAdjustment(HSIC& hsic) {
uint32_t enable = 0;
hsic_config config;
memset(&config, 0, sizeof(struct hsic_config));
status_t rc = mController->get_global_pa_config(mHandle, 0, &enable, &config);
if (rc == OK) {
hsic.hue = config.data.hue;
hsic.saturation = config.data.saturation;
hsic.intensity = config.data.intensity;
hsic.contrast = config.data.contrast;
hsic.saturationThreshold = config.data.saturationThreshold;
}
return rc;
}
status_t SDM::setDisplayMode(int32_t modeID, bool makeDefault) {
status_t rc = OK;
if (modeID == mActiveModeId) {
return rc;
}
sp<disp_mode> mode = getDisplayModeById(modeID);
if (mode == nullptr) {
return BAD_VALUE;
}
LOG(DEBUG) << "setDisplayMode: current mode=" << mActiveModeId;
if (mActiveModeId >= 0) {
sp<disp_mode> oldMode = getCurrentDisplayMode();
LOG(DEBUG) << "setDisplayMode: oldMode=" << oldMode->id << " flags=" << oldMode->privFlags;
if (oldMode->privFlags == PRIV_MODE_FLAG_SYSFS || mode->privFlags == PRIV_MODE_FLAG_SYSFS) {
LOG(DEBUG) << "disabling old mode";
rc = setModeState(oldMode, false);
if (rc != OK) {
LOG(ERROR) << "Failed to disable previous mode! err=" << rc;
return rc;
}
}
}
rc = setModeState(mode, true);
if (rc == OK) {
mActiveModeId = mode->id;
if (makeDefault) {
rc = Utils::writeLocalModeId(mode->id);
if (rc != OK) {
LOG(ERROR) << "failed to save mode! err=" << rc;
return rc;
}
if (mode->privFlags == PRIV_MODE_FLAG_SDM) {
rc = mController->set_default_display_mode(mHandle, 0, mode->id, 0);
if (rc != OK) {
LOG(ERROR) << "failed to save mode! err=" << rc;
return rc;
}
}
}
HSIC tmp;
rc = getPictureAdjustment(tmp);
if (rc != OK) {
LOG(ERROR) << "failed to retrieve picture adjustment after mode setting!";
} else {
LOG(DEBUG) << "new default PA: " << tmp.hue << " " << tmp.saturation << " "
<< tmp.intensity << " " << tmp.contrast << " " << tmp.saturationThreshold;
mDefaultPictureAdjustment = tmp;
}
} else {
LOG(ERROR) << "Failed to setModeState! err=" << rc;
return rc;
}
LOG(DEBUG) << "setDisplayMode: " << modeID << " default: " << makeDefault
<< " flags: " << mode->privFlags;
return OK;
}
status_t SDM::setAdaptiveBacklightEnabled(bool enabled) {
status_t rc = NO_INIT;
if (enabled == mCachedFOSSStatus) {
return OK;
}
char buf[kDppsBufSize];
sprintf(buf, "%s", enabled ? kFossOn : kFossOff);
if (Utils::sendDPPSCommand(buf, kDppsBufSize) == OK) {
if (strncmp(buf, "Success", 7) == 0) {
rc = OK;
mCachedFOSSStatus = enabled;
}
}
return rc;
}
bool SDM::isAdaptiveBacklightEnabled() {
return mCachedFOSSStatus;
}
status_t SDM::setPictureAdjustment(const HSIC& hsic) {
hsic_config config;
memset(&config, 0, sizeof(struct hsic_config));
config.data.hue = hsic.hue;
config.data.saturation = hsic.saturation;
config.data.intensity = hsic.intensity;
config.data.contrast = hsic.contrast;
config.data.saturationThreshold = hsic.saturationThreshold;
return mController->set_global_pa_config(mHandle, 0, 1, &config);
}
HSIC SDM::getDefaultPictureAdjustment() {
return mDefaultPictureAdjustment;
}
} // namespace implementation
} // namespace V1_0
} // namespace livedisplay
} // namespace lineage
} // namespace vendor