/* ** Copyright 2016, The CyanogenMod Project ** 2017, 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. */ #include #include #include #include #include #include //#define LOG_NDEBUG 0 #define LOG_TAG "LiveDisplay-SDM" #include #include "SDM.h" #include "Utils.h" namespace android { status_t SDM::loadVendorLibrary() { if (mLibHandle != NULL) { return OK; } mLibHandle = dlopen(SDM_DISP_LIB, RTLD_NOW); if (mLibHandle == NULL) { ALOGE("DLOPEN failed for %s (%s)", SDM_DISP_LIB, dlerror()); return NO_INIT; } disp_api_init = (int32_t(*)(int64_t*, uint32_t))dlsym(mLibHandle, "disp_api_init"); if (disp_api_init == NULL) { ALOGE("dlsym failed for disp_api_init"); goto fail; } disp_api_deinit = (int32_t(*)(int64_t, uint32_t))dlsym(mLibHandle, "disp_api_deinit"); if (disp_api_deinit == NULL) { ALOGE("dlsym failed for disp_api_deinit"); goto fail; } disp_api_get_global_color_balance_range = (int32_t(*)(int64_t, uint32_t, void*))dlsym( mLibHandle, "disp_api_get_global_color_balance_range"); if (disp_api_get_global_color_balance_range == NULL) { ALOGE("dlsym failed for disp_api_get_global_color_balance_range"); goto fail; } disp_api_set_global_color_balance = (int32_t(*)(int64_t, uint32_t, int32_t, uint32_t))dlsym( mLibHandle, "disp_api_set_global_color_balance"); if (disp_api_set_global_color_balance == NULL) { ALOGE("dlsym failed for disp_api_set_global_color_balance"); goto fail; } disp_api_get_global_color_balance = (int32_t(*)(int64_t, uint32_t, int32_t*, uint32_t*))dlsym( mLibHandle, "disp_api_get_global_color_balance"); if (disp_api_get_global_color_balance == NULL) { ALOGE("dlsym failed for disp_api_get_global_color_balance"); goto fail; } disp_api_get_num_display_modes = (int32_t(*)(int64_t, uint32_t, int32_t, int32_t*, uint32_t*))dlsym( mLibHandle, "disp_api_get_num_display_modes"); if (disp_api_get_num_display_modes == NULL) { ALOGE("dlsym failed for disp_api_get_num_display_modes"); goto fail; } disp_api_get_display_modes = (int32_t(*)(int64_t, uint32_t, int32_t, void*, int32_t, uint32_t*))dlsym( mLibHandle, "disp_api_get_display_modes"); if (disp_api_get_display_modes == NULL) { ALOGE("dlsym failed for disp_api_get_display_modes"); goto fail; } disp_api_get_active_display_mode = (int32_t(*)(int64_t, uint32_t, int32_t*, uint32_t*, uint32_t*))dlsym( mLibHandle, "disp_api_get_active_display_mode"); if (disp_api_get_active_display_mode == NULL) { ALOGE("dlsym failed for disp_api_get_active_display_mode"); goto fail; } disp_api_set_active_display_mode = (int32_t(*)(int64_t, uint32_t, int32_t, uint32_t))dlsym( mLibHandle, "disp_api_set_active_display_mode"); if (disp_api_set_active_display_mode == NULL) { ALOGE("dlsym failed for disp_api_set_active_display_mode"); goto fail; } disp_api_set_default_display_mode = (int32_t(*)(int64_t, uint32_t, int32_t, uint32_t))dlsym( mLibHandle, "disp_api_set_default_display_mode"); if (disp_api_set_default_display_mode == NULL) { ALOGE("dlsym failed for disp_api_set_default_display_mode"); goto fail; } disp_api_get_default_display_mode = (int32_t(*)(int64_t, uint32_t, int32_t*, uint32_t*))dlsym( mLibHandle, "disp_api_get_default_display_mode"); if (disp_api_get_default_display_mode == NULL) { ALOGE("dlsym failed for disp_api_get_default_display_mode"); goto fail; } disp_api_get_global_pa_range = (int32_t(*)(int64_t, uint32_t, void*))dlsym(mLibHandle, "disp_api_get_global_pa_range"); if (disp_api_get_global_pa_range == NULL) { ALOGE("dlsym failed for disp_api_get_global_pa_range"); goto fail; } disp_api_get_global_pa_config = (int32_t(*)(int64_t, uint32_t, uint32_t*, void*))dlsym( mLibHandle, "disp_api_get_global_pa_config"); if (disp_api_get_global_pa_config == NULL) { ALOGE("dlsym failed for disp_api_get_global_pa_config"); goto fail; } disp_api_set_global_pa_config = (int32_t(*)(int64_t, uint32_t, uint32_t, void*))dlsym( mLibHandle, "disp_api_set_global_pa_config"); if (disp_api_set_global_pa_config == NULL) { ALOGE("dlsym failed for disp_api_set_global_pa_config"); goto fail; } disp_api_get_feature_version = (int32_t(*)(int64_t, uint32_t, void*, uint32_t*))dlsym( mLibHandle, "disp_api_get_feature_version"); if (disp_api_get_feature_version == NULL) { ALOGE("dlsym failed for disp_api_get_feature_version"); goto fail; } return OK; fail: ALOGE("Failed to link vendor library: %s", dlerror()); dlclose(mLibHandle); mLibHandle = NULL; return NO_INIT; } status_t SDM::initialize() { status_t rc = loadVendorLibrary(); if (rc != OK) { return rc; } rc = disp_api_init(&mHandle, 0); if (rc != OK) { return rc; } mActiveModeId = -1; if (hasFeature(Feature::DISPLAY_MODES)) { rc = saveInitialDisplayMode(); if (rc != OK) { ALOGE("Failed to save initial display mode! err=%d", rc); return rc; } sp defMode = getDefaultDisplayMode(); if (defMode != nullptr) { setDisplayMode(defMode->id, false); } } return OK; } SDM::~SDM() { if (mLibHandle != NULL) { dlclose(mLibHandle); } } status_t SDM::deinitialize() { if (mLibHandle != NULL) { disp_api_deinit(mHandle, 0); mHandle = -1; } return OK; } status_t SDM::setAdaptiveBacklightEnabled(bool enabled) { status_t rc = NO_INIT; if (enabled == mCachedFOSSStatus) { return OK; } char* buf = new char[DPPS_BUF_SIZE]; sprintf(buf, "%s", enabled ? FOSS_ON : FOSS_OFF); if (Utils::sendDPPSCommand(buf, DPPS_BUF_SIZE) == OK) { if (strncmp(buf, "Success", 7) == 0) { rc = OK; mCachedFOSSStatus = enabled; } } delete[] buf; return rc; } bool SDM::isAdaptiveBacklightEnabled() { return mCachedFOSSStatus; } status_t SDM::getColorBalanceRange(Range& range) { status_t rc = disp_api_get_global_color_balance_range(mHandle, 0, &range); ALOGV("getColorBalanceRange: min=%d max=%d step=%d", range.min, range.max, range.step); return rc; } status_t SDM::setColorBalance(int32_t balance) { return disp_api_set_global_color_balance(mHandle, 0, balance, 0); } int32_t SDM::getColorBalance() { int32_t value = -1; uint32_t flags = 0; if (disp_api_get_global_color_balance(mHandle, 0, &value, &flags) != 0) { value = 0; } return value; } uint32_t SDM::getNumDisplayModes() { uint32_t flags = 0; int32_t count = 0; if (disp_api_get_num_display_modes(mHandle, 0, 0, &count, &flags)) { count = 0; } if (getLocalSRGBMode() != nullptr) { count++; } if (getLocalDCIP3Mode() != nullptr) { count++; } return count; } status_t SDM::getDisplayModes(List>& profiles) { status_t rc = OK; uint32_t flags = 0, i = 0; uint32_t count = getNumDisplayModes(); if (!count) return rc; sp srgb = getLocalSRGBMode(); sp dci_p3 = getLocalDCIP3Mode(); uint32_t sdm_count = count; if (srgb != nullptr) { sdm_count--; } if (dci_p3 != nullptr) { sdm_count--; } struct sdm_mode { int32_t id; int32_t type; int32_t len; char* name; }; sdm_mode* tmp = new sdm_mode[sdm_count]; memset(tmp, 0, sizeof(sdm_mode) * sdm_count); for (i = 0; i < sdm_count; i++) { tmp[i].id = -1; tmp[i].name = new char[128]; tmp[i].len = 128; } rc = disp_api_get_display_modes(mHandle, 0, 0, tmp, sdm_count, &flags); if (rc == 0) { for (i = 0; i < sdm_count; i++) { const sp m = new DisplayMode(tmp[i].id, tmp[i].name, tmp[i].len); m->privFlags = PRIV_MODE_FLAG_SDM; profiles.push_back(m); delete tmp[i].name; } } delete[] tmp; if (srgb != nullptr) { profiles.push_back(srgb); } if (dci_p3 != nullptr) { profiles.push_back(dci_p3); } return rc; } status_t SDM::setDisplayMode(int32_t modeID, bool makeDefault) { status_t rc = OK; if (modeID == mActiveModeId) { return OK; } sp mode = getDisplayModeById(modeID); if (mode == nullptr) { return BAD_VALUE; } ALOGV("setDisplayMode: current mode=%d", mActiveModeId); if (mActiveModeId >= 0) { sp oldMode = getCurrentDisplayMode(); ALOGV("setDisplayMode: oldMode=%d flags=%d", oldMode->id, oldMode->privFlags); if (oldMode->privFlags == PRIV_MODE_FLAG_SYSFS || mode->privFlags == PRIV_MODE_FLAG_SYSFS) { ALOGV("disabling old mode"); rc = setModeState(oldMode, false); if (rc != OK) { ALOGE("Failed to disable previous mode! err=%d", rc); return rc; } } } rc = setModeState(mode, true); if (rc == OK) { mActiveModeId = mode->id; if (makeDefault) { rc = Utils::writeLocalModeId(mode->id); if (rc != OK) { ALOGE("failed to save mode! %d", rc); return rc; } if (mode->privFlags == PRIV_MODE_FLAG_SDM) { rc = disp_api_set_default_display_mode(mHandle, 0, mode->id, 0); if (rc != OK) { ALOGE("failed to save mode! %d", rc); return rc; } } } HSIC tmp; rc = getPictureAdjustment(tmp); if (rc != OK) { ALOGE("failed to retrieve picture adjustment after mode setting!"); } else { ALOGV("new default PA: %d %f %f %f %f", tmp.hue, tmp.saturation, tmp.intensity, tmp.contrast, tmp.saturationThreshold); mDefaultPictureAdjustment.setTo(tmp); } } else { ALOGE("Failed to setModeState! err=%d", rc); return rc; } ALOGV("setDisplayMode: %d default: %d flags: %d", modeID, makeDefault, mode->privFlags); return OK; } sp SDM::getDisplayModeById(int32_t id) { List> profiles; status_t rc = getDisplayModes(profiles); if (rc == OK) { for (List>::iterator it = profiles.begin(); it != profiles.end(); ++it) { const sp mode = *it; if (id == mode->id) { return mode; } } } return nullptr; } sp SDM::getCurrentDisplayMode() { return getDisplayModeById(mActiveModeId); } sp 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; } status_t SDM::setModeState(sp mode, bool state) { int32_t id = 0; if (mode->privFlags == PRIV_MODE_FLAG_SYSFS) { ALOGV("sysfs node: %s state=%d", mode->privData.string(), state); return Utils::writeInt(mode->privData.string(), state ? 1 : 0); } else if (mode->privFlags == PRIV_MODE_FLAG_SDM) { if (state) { return disp_api_set_active_display_mode(mHandle, 0, mode->id, 0); } else { if (Utils::readInitialModeId(&id) == OK) { ALOGV("set sdm mode to default: id=%d", id); return disp_api_set_active_display_mode(mHandle, 0, id, 0); } } } return BAD_VALUE; } sp SDM::getLocalSRGBMode() { char path[PATH_MAX]; sprintf(path, "%s", SRGB_NODE); if (access(path, W_OK) != 0) { return nullptr; } sp m = new DisplayMode(SRGB_NODE_ID, "srgb", 4); m->privFlags = PRIV_MODE_FLAG_SYSFS; m->privData.setTo(path); return m; } sp SDM::getLocalDCIP3Mode() { char path[PATH_MAX]; sprintf(path, "%s", DCI_P3_NODE); if (access(path, W_OK) != 0) { return nullptr; } sp m = new DisplayMode(DCI_P3_NODE_ID, "dci_p3", 6); m->privFlags = PRIV_MODE_FLAG_SYSFS; m->privData.setTo(path); return m; } status_t SDM::getPictureAdjustmentRanges(HSICRanges& ranges) { hsic_ranges r; memset(&r, 0, sizeof(struct hsic_ranges)); status_t rc = disp_api_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; } status_t SDM::getPictureAdjustment(HSIC& hsic) { uint32_t enable = 0; hsic_config config; memset(&config, 0, sizeof(struct hsic_config)); status_t rc = disp_api_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::getDefaultPictureAdjustment(HSIC& hsic) { hsic.setTo(mDefaultPictureAdjustment); return OK; } status_t SDM::setPictureAdjustment(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 disp_api_set_global_pa_config(mHandle, 0, 1, &config); } 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 = 4; break; case Feature::COLOR_TEMPERATURE: id = 3; break; case Feature::PICTURE_ADJUSTMENT: id = 1; case Feature::ADAPTIVE_BACKLIGHT: if (property_get_int32("ro.qualcomm.foss", 0) > 0) { return true; } break; default: return false; } if (disp_api_get_feature_version(mHandle, id, &v, &flags) == 0) { if (v.x > 0 || v.y > 0 || v.z > 0) { // Color balance depends on calibration data in SDM if (feature == Feature::DISPLAY_MODES || feature == Feature::COLOR_TEMPERATURE) { if (getNumDisplayModes() > 0) { // make sure the range isn't zero if (feature == Feature::COLOR_TEMPERATURE) { Range r; if (getColorBalanceRange(r) == OK && r.isNonZero()) { return true; } return false; } return true; } } 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 (disp_api_get_default_display_mode(mHandle, 0, &id, &flags) == OK && id >= 0) { return Utils::writeInitialModeId(id); } else { return Utils::writeInitialModeId(id = 0); } } return OK; } };