/* * Copyright (C) 2021 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 "AlsCorrection" #include "AlsCorrection.h" #include #include #include #include #include #include #include #include #include #include #include #define PROC_OPLUSALS "/proc/oplusAls/" #define OPLUS_DISPLAY "/sys/kernel/oplus_display/" namespace android { namespace hardware { namespace sensors { namespace V2_1 { namespace implementation { static const std::string rgbw_max_lux_paths[4] = { PROC_OPLUSALS "red_max_lux", PROC_OPLUSALS "green_max_lux", PROC_OPLUSALS "blue_max_lux", PROC_OPLUSALS "white_max_lux", }; bool DEBUG = false; struct als_config { bool hbr; float rgbw_max_lux[4]; float rgbw_max_lux_div[4]; float rgbw_lux_postmul[4]; float rgbw_poly[4][4]; float grayscale_weights[3]; float sensor_inverse_gain[4]; float agc_threshold; float calib_gain; float bias; float max_brightness; }; //Add mt6893 data static als_config conf = { .hbr = true, .rgbw_max_lux = { 1210.0, 2074.0, 838.0, 3541.0 }, .rgbw_max_lux_div = { 1210.0, 2074.0, 838.0, 531.0 }, .rgbw_poly = { { 0.0000463,0.00508,0.38457 }, { 0.0000643,0.01464,0.17515,0 }, { 0.0000111819,0.00859,-0.08779,0 }, { 0.0000644,-0.00975,0.32079,0 }, }, .grayscale_weights = { 0.232, 0.415, 0.353 }, .sensor_inverse_gain = { 0.0615, 0.0466, 0.0361, 0.0288 }, }; static struct { float middle; float min, max; } hysteresis_ranges[] = { { 0, 0, 4 }, { 7, 1, 12 }, { 15, 5, 30 }, { 30, 10, 50 }, { 360, 25, 700 }, { 1200, 300, 1600 }, { 2250, 1000, 2940 }, { 4600, 2000, 5900 }, { 10000, 4000, 80000 }, { HUGE_VALF, 8000, HUGE_VALF }, }; static struct { nsecs_t last_update, last_forced_update; bool force_update; float hyst_min, hyst_max; float last_corrected_value; float last_agc_gain; } state = { .last_update = 0, .force_update = true, .hyst_min = -1.0, .hyst_max = -1.0, .last_agc_gain = 0.0, }; template static T get(const std::string& path, const T& def) { std::ifstream file(path); T result; file >> result; return file.fail() ? def : result; } void AlsCorrection::init() { static bool initialized = false; if (initialized) { return; } initialized = true; // TODO: Constantly update and persist this float screen_on_time = get(PROC_OPLUSALS "screenontimebyhours", 0.0); float screen_aging_factor = 1.0 - screen_on_time / 87600.0; ALOGI("Screen on time: %.2fh (aging factor: %.2f%%)", screen_on_time, screen_aging_factor * 100.0); float rgbw_acc = 0.0; for (int i = 0; i < 4; i++) { float max_lux = get(rgbw_max_lux_paths[i], 0.0); if (max_lux != 0.0) { conf.rgbw_max_lux[i] = max_lux; } conf.rgbw_max_lux[i] *= screen_aging_factor; if (i < 3) { rgbw_acc += conf.rgbw_max_lux[i]; conf.rgbw_lux_postmul[i] = conf.rgbw_max_lux[i] / conf.rgbw_max_lux_div[i]; } else { rgbw_acc -= conf.rgbw_max_lux[i]; conf.rgbw_lux_postmul[i] = rgbw_acc / conf.rgbw_max_lux_div[i]; } } if(DEBUG) ALOGI("Display maximums: R=%.0f G=%.0f B=%.0f W=%.0f", conf.rgbw_max_lux[0], conf.rgbw_max_lux[1], conf.rgbw_max_lux[2], conf.rgbw_max_lux[3]); float row_coe = get(PROC_OPLUSALS "row_coe", 0.0); if (row_coe != 0.0) { conf.sensor_inverse_gain[0] = row_coe / 1000.0; } conf.agc_threshold = 800.0 / conf.sensor_inverse_gain[0]; float cali_coe = get(PROC_OPLUSALS "cali_coe", 0.0); conf.calib_gain = cali_coe > 0.0 ? cali_coe / 1000.0 : 1.0; if(DEBUG) ALOGI("Calibrated sensor gain: %.2fx", 1.0 / (conf.calib_gain * conf.sensor_inverse_gain[0])); float als_bias = get(PROC_OPLUSALS "als_bias", 0.0); conf.bias = als_bias <= 4.0 ? als_bias : 0.0; if(DEBUG) ALOGI("Sensor bias: %.2f", conf.bias); float max_brightness = get(OPLUS_DISPLAY "oplus_max_brightness", 0.0); conf.max_brightness = max_brightness > 0.0 ? max_brightness : 1023.0; for (auto& range : hysteresis_ranges) { range.min /= conf.calib_gain * conf.sensor_inverse_gain[0]; range.max /= conf.calib_gain * conf.sensor_inverse_gain[0]; } hysteresis_ranges[0].min = -1.0; } void AlsCorrection::process(Event& event) { /* ALOGV("%f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f", event.u.data[0], event.u.data[1], event.u.data[2], event.u.data[3], event.u.data[4], event.u.data[5], event.u.data[6], event.u.data[7], event.u.data[8], event.u.data[9], event.u.data[10], event.u.data[11], event.u.data[12], event.u.data[13], event.u.data[14], event.u.data[15] ); */ if(DEBUG) ALOGV("Raw sensor reading: %.0f", event.u.scalar); if (conf.rgbw_max_lux[0] == 0.0 && conf.rgbw_max_lux[1] == 0.0 && conf.rgbw_max_lux[2] == 0.0 && conf.rgbw_max_lux[3] == 0.0) { ALOGE("Trying to init AlsCorrection again"); AlsCorrection::init(); event.sensorHandle = 0; return; } if (event.u.scalar > conf.bias) { event.u.scalar -= conf.bias; } if (conf.hbr && event.u.data[8] != 2.0) { state.force_update = true; event.u.scalar *= conf.calib_gain * conf.sensor_inverse_gain[0]; if(DEBUG) ALOGV("Skipping correction, calibrated ambient light: %.0f lux", event.u.scalar); return; } nsecs_t now = systemTime(SYSTEM_TIME_BOOTTIME); float brightness = get(OPLUS_DISPLAY "oplus_brightness", 0.0); if (state.last_update == 0) { state.last_update = now; state.last_forced_update = now; } else { if (brightness > 0.0 && (now - state.last_forced_update) > s2ns(3)) { if(DEBUG) ALOGV("Forcing screenshot"); state.last_forced_update = now; state.force_update = true; } if ((now - state.last_update) < ms2ns(100)) { if(DEBUG) ALOGV("Events coming too fast, dropping"); // TODO figure out a better way to drop events event.sensorHandle = 0; return; } state.last_update = now; } float sensor_raw_calibrated = event.u.scalar * conf.calib_gain * state.last_agc_gain; if (state.force_update || ((event.u.scalar < state.hyst_min || event.u.scalar > state.hyst_max) && (event.u.data[6] > 2.0 || sensor_raw_calibrated < 10.0 || sensor_raw_calibrated > (5.0 / .07)))) { android::base::unique_fd fd(socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)); if (fd.get() < 0) { if(DEBUG) ALOGV("Failed to open als correction socket: %s", strerror(errno)); // TODO figure out a better way to drop events event.sensorHandle = 0; return; } sockaddr_un addr{ AF_UNIX, "/dev/socket/als_correction" }; if (connect(fd.get(), reinterpret_cast(&addr), sizeof(addr)) == -1) { if(DEBUG) ALOGV("Failed to connect to als correction socket: %s", strerror(errno)); // TODO figure out a better way to drop events event.sensorHandle = 0; return; } const char *cmd = "take_screenshot"; if (send(fd.get(), cmd, strlen(cmd) + 1, 0) == -1) { if(DEBUG) ALOGV("Failed to send command to als correction socket: %s", strerror(errno)); // TODO figure out a better way to drop events event.sensorHandle = 0; return; } pollfd fds{ fd.get(), POLLIN, 0 }; if (poll(&fds, 1, -1) != 1) { if(DEBUG) ALOGV("Invalid poll als correction socket fd"); // TODO figure out a better way to drop events event.sensorHandle = 0; return; } struct screenshot_t { uint32_t r, g, b; nsecs_t timestamp; } screenshot; if (read(fd.get(), &screenshot, sizeof(screenshot_t)) != sizeof(screenshot_t)) { if(DEBUG) ALOGV("Invalid reply from als correction socket"); // TODO figure out a better way to drop events event.sensorHandle = 0; return; } if ((now - screenshot.timestamp) > ms2ns(1000)) { if(DEBUG) ALOGV("Screenshot too old, dropping event"); // TODO figure out a better way to drop events event.sensorHandle = 0; return; } if(DEBUG) ALOGV("Screen color above sensor: %d %d %d", screenshot.r, screenshot.g, screenshot.b); float rgbw[4] = { static_cast(screenshot.r), static_cast(screenshot.g), static_cast(screenshot.b), screenshot.r * conf.grayscale_weights[0] + screenshot.g * conf.grayscale_weights[1] + screenshot.b * conf.grayscale_weights[2] }; float cumulative_correction = 0.0; for (int i = 0; i < 4; i++) { float corr = 0.0; for (float coef : conf.rgbw_poly[i]) { corr *= rgbw[i]; corr += coef; } corr *= conf.rgbw_lux_postmul[i]; if (i < 3) { cumulative_correction += std::max(corr, 0.0f); } else { cumulative_correction -= corr; } } cumulative_correction *= brightness / conf.max_brightness; float brightness_fullwhite = conf.rgbw_max_lux[3] * brightness / conf.max_brightness; float brightness_grayscale_gamma = std::pow(rgbw[3] / 255.0, 2.2) * brightness_fullwhite; cumulative_correction = std::min(cumulative_correction, brightness_fullwhite); cumulative_correction = std::max(cumulative_correction, brightness_grayscale_gamma); if(DEBUG) ALOGV("Estimated screen brightness: %.0f", cumulative_correction); float sensor_raw_corrected = std::max(event.u.scalar - cumulative_correction, 0.0f); float agc_gain = conf.sensor_inverse_gain[0]; if (sensor_raw_corrected > conf.agc_threshold) { if (!conf.hbr) { float gain_estimate = sensor_raw_corrected / event.u.data[8]; if (gain_estimate > 85.0) { agc_gain = conf.sensor_inverse_gain[0]; } else if (gain_estimate >= 39.0) { agc_gain = conf.sensor_inverse_gain[1]; } else if (gain_estimate >= 29.0) { agc_gain = conf.sensor_inverse_gain[2]; } else { agc_gain = conf.sensor_inverse_gain[3]; } } else { float gain_estimate = event.u.data[8] * 1000.0 / event.u.scalar; if (gain_estimate > 1050.0) { agc_gain = conf.sensor_inverse_gain[3]; } else if (gain_estimate > 800.0) { agc_gain = conf.sensor_inverse_gain[2]; } else if (gain_estimate > 450.0) { agc_gain = conf.sensor_inverse_gain[1]; } else { agc_gain = conf.sensor_inverse_gain[0]; } } } if(DEBUG) ALOGV("AGC gain: %f", agc_gain); if (cumulative_correction <= event.u.scalar * 1.35 || event.u.scalar * conf.calib_gain * agc_gain < 10000.0 || state.force_update) { float sensor_corrected = sensor_raw_corrected * conf.calib_gain * agc_gain; state.last_agc_gain = agc_gain; for (auto& range : hysteresis_ranges) { if (sensor_corrected <= range.middle) { state.hyst_min = range.min; state.hyst_max = range.max + brightness_fullwhite; break; } } sensor_corrected = std::max(sensor_corrected - 14.0, 0.0); event.u.scalar = sensor_corrected; state.last_corrected_value = sensor_corrected; if(DEBUG) ALOGV("Fully corrected sensor value: %.0f lux", sensor_corrected); } else { event.u.scalar = state.last_corrected_value; if(DEBUG) ALOGV("Reusing cached value: %.0f lux", event.u.scalar); } state.force_update = false; } else { event.u.scalar = state.last_corrected_value; if(DEBUG) ALOGV("Reusing cached value: %.0f lux", event.u.scalar); } } } // namespace implementation } // namespace V2_1 } // namespace sensors } // namespace hardware } // namespace android