From 4130fb4065326e6fc5f31c74789f9f313315d59c Mon Sep 17 00:00:00 2001 From: tanish2k09 Date: Sun, 24 Feb 2019 19:25:29 -0800 Subject: [PATCH] Introducing KLapse - A kernel level livedisplay module v4.0 Author: @tanish2k09 (email: tanish2k09.dev@gmail.com) What is it? Kernel-based Lapse ("K-Lapse") is a linear RGB scaling module that 'shifts' RGB based on time (of the day/selected by user), or (since v2.0) brightness. This concept is inspired from LineageOS (formerly known as 'CyanogenMod') ROM's feature "livedisplay" which also changes the display settings (RGB, hue, temperature, etc) based on time. Why did you decide to make this? (Tell me a story). I (personally) am a big fan of the livedisplay feature found on LineageOS ROM. I used it every single day, since Android Lollipop. Starting from Android Nougat, a native night mode solution was added to AOSP and it felt like livedisplay was still way superior, thanks to its various options (you could say it spoiled me, sure). I also maintained a kernel (Venom kernel) for the device I was using at that time. It was all good until the OEM dropped support for the device at Android M, and XDA being XDA, was already working on N ROMs. The issue was, these ROMs weren't LineageOS or based on it, so livedisplay was... gone. I decided I'll try to bring that feature to every other ROM. How would I do that? Of course! The kernel! It worked on every single ROM, it was the key! I started to work on it ASAP and here it is, up on GitHub, licensed under GPL (check klapse.c), open to everyone :) How does it work? Think of it like a fancy night mode, but not really. Klapse is dependent on an RGB interface (like Gamma on MTK and KCAL on SD chipsets). It fetches time from the kernel, converts it to local time, and selects and RGB set based on the time. The result is really smooth shifting of RGB over time. How does it really work (dev)? Klapse mode 1 (time-based scaling) uses a method void klapse_pulse(void) that should ideally be called every minute. This can be done by injecting a pulse call inside another method that is called repeatedly naturally, like cpufreq or atomic or frame commits. It can be anything, whatever you like, even a kthread, as long as it is called repeatedly naturally. To execute every 60 seconds, use jiffies or ktime, or any similar method. The pulse function fetches the current time and makes calculations based on the current hour and the values of the tunables listed down below. Klapse mode 2 (brightness-based scaling) uses a method void set_rgb_slider( bl_lvl) where is the data type of the brightness level used in your kernel source. (OnePlus 6 uses u32 data type for bl_lvl) set_rgb_slider needs to be called/injected inside a function that sets brightness for your device. (OnePlus 6 uses dsi_panel.c for that, check out the diff for that file in /op6) What all stuff can it do? 1, Emulate night mode with the proper RGB settings 2, Smoothly scale from one set of RGB to another set of RGB in integral intervals over time. 3, Reduce perceived brightness using brightness_factor by reducing the amount of color on screen. Allows lower apparent brightness than system permits. 4, Scale RGB based on brightness of display (low brightness usually implies a dark environment, where yellowness is probably useful). 5, Automate the perceived brightness independent of whether klapse is enabled, using its own set of start and stop hours. 6, Be more efficient,faster by residing inside the kernel instead of having to use the HWC HAL like android's night mode. 7, (On older devices) Reduce stuttering or frame lags caused by native night mode. 8, An easier solution against overlay-based apps that run as service in userspace/Android and sometimes block apps asking for permissions. 9, Give you a Livedisplay alternative if it doesn't work in your ROM. 10, Impress your crush so you can get a date (Hey, don't forget to credit me if it works). Alright, so this is a replacement for night mode? NO! Not at all. One can say this is merely an alternative for LineageOS' Livedisplay, but inside a kernel. Night mode is a sub-function of both Livedisplay and KLapse. Most comparisons here were made with night mode because that's what an average user uses, and will relate to the most. There is absolutely no reason for your Android kernel to not have KLapse. Go ahead and add it or ask your kernel maintainer to. It's super-easy! What can it NOT do (yet)? 1, Calculate scaling to the level of minutes, like "Start from 5:37pm till 7:19am". --TODO 2, Make coffee for you. 3, Fly you to the moon. Without a heavy suit. 4, Get you a monthly subscription of free food, cereal included. All these following tunables are found in their respective files in /sys/klapse/ 1. enable_klapse : A switch to enable or disable klapse. Values : 0 = off, 1 = on (since v2.0, 2 = brightness-dependent mode) 2. klapse_start_hour : The hour at which klapse should start scaling the RGB values from daytime to target (see next points). Values : 0-23 3. klapse_stop_hour : The hour by which klapse should scale back the RGB values from target to daytime (see next points). Values : 0-23 4. daytime_rgb : The RGB set that must be used for all the time outside of start and stop hour range. 5. target_rgb : The RGB set that must be scaled towards for all the time inside of start and stop hour range. 6. klapse_scaling_rate : Controls how soon the RGB reaches from daytime to target inside of start and stop hour range. Once target is reached, it remains constant till 30 minutes before stop hour, where target RGB scales back to daytime RGB. 7. brightness_factor : From the name itself, this value has the ability to bend perception and make your display appear as if it is at a lesser brightness level than it actually is at. It works by reducing the RGB values by the same factor. Values : 2-10, (10 means accurate brightness, 5 means 50% of current brightness, you get it) 8. brightness_factor_auto : A switch that allows you to automatically set the brightness factor in a set time range. Value : 0 = off, 1 = on 9. brightness_factor_auto_start_hour : The hour at which brightness_factor should be applied. Works only if #8 is 1. Values : 0-23 10. brightness_factor_auto_stop_hour : The hour at which brightness_factor should be reverted to 10. Works only if #8 is 1. Values : 0-23 11. backlight_range : The brightness range within which klapse should scale from daytime to target_rgb. Works only if #1 is 2. Values : MIN_BRIGHTNESS-MAX_BRIGHTNESS Signed-off-by: engstk --- drivers/video/fbdev/msm/Kconfig | 7 + drivers/video/fbdev/msm/Makefile | 1 + drivers/video/fbdev/msm/klapse.c | 837 +++++++++++++++++++++++++++++++ drivers/video/fbdev/msm/klapse.h | 18 + 4 files changed, 863 insertions(+) create mode 100644 drivers/video/fbdev/msm/klapse.c create mode 100644 drivers/video/fbdev/msm/klapse.h diff --git a/drivers/video/fbdev/msm/Kconfig b/drivers/video/fbdev/msm/Kconfig index ee0b33d6f652..569f6bab23c2 100644 --- a/drivers/video/fbdev/msm/Kconfig +++ b/drivers/video/fbdev/msm/Kconfig @@ -179,4 +179,11 @@ config FB_MSM_MDSS_KCAL_CTRL Enable sysfs for rgb/lut control for mdss-mdp display controllers in the MDSS sub-system. +config KLAPSE + bool CONFIG_KLAPSE + depends on FB_MSM_MDSS + default n + help + A simple rgb dynamic lapsing module similar to LineageOS' livedisplay + endif diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile index abfd9c436546..a528fae905dc 100644 --- a/drivers/video/fbdev/msm/Makefile +++ b/drivers/video/fbdev/msm/Makefile @@ -69,3 +69,4 @@ obj-$(CONFIG_FB_MSM_QPIC_ILI_QVGA_PANEL) += qpic_panel_ili_qvga.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o mdss_util.o obj-$(CONFIG_COMPAT) += mdss_compat_utils.o obj-$(CONFIG_FB_MSM_MDSS_KCAL_CTRL) += mdss_mdp_kcal_ctrl.o +obj-$(CONFIG_KLAPSE) += klapse.o diff --git a/drivers/video/fbdev/msm/klapse.c b/drivers/video/fbdev/msm/klapse.c new file mode 100644 index 000000000000..847815c779c0 --- /dev/null +++ b/drivers/video/fbdev/msm/klapse.c @@ -0,0 +1,837 @@ +#include +#include +#include +#include +#include +#include +#include "klapse.h" + +//Add additional headers below here only + +/* DEFAULT_ENABLE values : + * 0 = off + * 1 = time-based scaling + * 2 = brightness-based scaling + */ +#define DEFAULT_ENABLE 0 + +// MAX_SCALE : Maximum value of RGB possible +#define MAX_SCALE 256 + +// SCALE_VAL_MIN : Minimum value of RGB recommended +#define SCALE_VAL_MIN 20 + +// MAX_BRIGHTNESS : Maximum value of the display brightness/backlight +#define MAX_BRIGHTNESS 1023 + +// MIN_BRIGHTNESS : Minimum value of the display brightness/backlight +#define MIN_BRIGHTNESS 2 + +/* UPPER_BL_LVL : Initial upper limit for brightness-dependent mode. + * Value <= MAX_BRIGHTNESS && > LOWER_BL_LVL (MUST) + */ +#define UPPER_BL_LVL 200 + +/* LOWER_BL_LVL : Initial lower limit for brightness-dependent mode. + * Value < UPPER_BL_LVL (MUST) + */ +#define LOWER_BL_LVL 2 + +#define LIC "GPLv2" +#define AUT "tanish2k09" +#define VER "4.0" + +MODULE_LICENSE(LIC); +MODULE_AUTHOR(AUT); +MODULE_DESCRIPTION("A simple rgb dynamic lapsing module similar to livedisplay"); +MODULE_VERSION(VER); + + +//Tunables : +static unsigned int daytime_r, daytime_g, daytime_b, target_r, target_g, target_b; +static unsigned int klapse_start_hour, klapse_stop_hour, enable_klapse; +static unsigned int brightness_factor_auto_start_hour, brightness_factor_auto_stop_hour; +static unsigned int klapse_scaling_rate, brightness_factor; +static unsigned int backlight_lower, backlight_upper; +static unsigned int fadeback_minutes; +static unsigned int pulse_freq; +static bool brightness_factor_auto_enable; + +/* + *Internal calculation variables : + *WARNING : DO NOT MAKE THEM TUNABLE + */ +static int target_minute; +static unsigned int b_cache; +static int current_r, current_g, current_b; +static unsigned int active_minutes, last_bl; +static unsigned long local_time; +static struct rtc_time tm; +static struct timeval time; +static struct timer_list pulse_timer; + + +//klapse related functions +static void restart_timer(void) +{ + mod_timer(&pulse_timer, jiffies + msecs_to_jiffies(pulse_freq)); + printk(KERN_INFO "KLapse pulse timer restarted!!!.\n"); +} + +static void flush_timer(void) +{ + if (timer_pending(&pulse_timer)) + mod_timer_pending(&pulse_timer, jiffies); + printk(KERN_INFO "KLapse pulse timer flushed!!!.\n"); +} + +static void calc_active_minutes(void) +{ + if(klapse_start_hour > klapse_stop_hour) + active_minutes = (24 + klapse_stop_hour - klapse_start_hour)*60; + else + active_minutes = (klapse_stop_hour - klapse_start_hour)*60; + + target_minute = (active_minutes*10)/klapse_scaling_rate; +} + +static int get_minutes_since_start(void) +{ + int hour, min; + hour = tm.tm_hour - klapse_start_hour; + + if (hour < 0) + hour += 24; + + min = ((hour*60) + tm.tm_min); + return min; +} + +static int get_minutes_before_stop(void) +{ + return (active_minutes - get_minutes_since_start()); +} + +static void set_rgb(int r, int g, int b) +{ + K_RED = r; + K_GREEN = g; + K_BLUE = b; + + current_r = r; + current_g = g; + current_b = b; +} + +static void set_rgb_brightness(int r,int g,int b) +{ + + r = ((r*brightness_factor)/10); + g = ((g*brightness_factor)/10); + b = ((b*brightness_factor)/10); + + if (r < 0) + r = SCALE_VAL_MIN; + else if (r > MAX_SCALE) + r = MAX_SCALE; + if (g < 0) + g = SCALE_VAL_MIN; + else if (g > MAX_SCALE) + g = MAX_SCALE; + if (b < 0) + b = SCALE_VAL_MIN; + else if (b > MAX_SCALE) + b = MAX_SCALE; + + set_rgb(r,g,b); +} + +static bool hour_within_range(int start, int stop, int check) +{ + // The 24-hour system is tricky because 0 comes after 23. + // Handle it here properly + + // Check whether the start hour comes before stop hour. + // if = 1, this would mean they are numerically in order + // if both extremes are same, no time is possible inside so return false. + // else, start hour is actually greater than stop, something like "From 5pm to 7am" + // which translates to "From 17 to 7". It is clear why this could be a problem if not handled. + if (start < stop) { + if ((check >= start) && (check < stop)) + return 1; + else + return 0; + } + else if (start == stop) + return 0; + else { + if ((check < stop) || (check >= start)) + return 1; + else + return 0; + } +} +//klapse calc functions end here. + +// klapse rgb update function +static void klapse_pulse(unsigned long data) +{ + int backtime; + + // Get time + do_gettimeofday(&time); + local_time = (u32)(time.tv_sec - (sys_tz.tz_minuteswest * 60)); + rtc_time_to_tm(local_time, &tm); + + // Check brightness level automation + if (brightness_factor_auto_enable == 1) + { + if (hour_within_range(brightness_factor_auto_start_hour, brightness_factor_auto_stop_hour, tm.tm_hour) == 0) //Means not in dimmer-time + brightness_factor = 10; + else + brightness_factor = b_cache; + } + else + brightness_factor = b_cache; + + // Check klapse automation, and a security measure too + if (enable_klapse == 1) + { + backtime = get_minutes_before_stop(); + + if(hour_within_range(klapse_start_hour, klapse_stop_hour, tm.tm_hour) == 0) //Means not in klapse time period. + { + set_rgb_brightness(daytime_r,daytime_g,daytime_b); + if (!timer_pending(&pulse_timer)) + restart_timer(); + return; + } + else if (backtime > fadeback_minutes) + { + backtime = get_minutes_since_start(); + + // For optimisation, this >= can be turned to an == + // But doing so will break reverse "time jumps" due to clock change + // And a wrong RGB value will be calculated. + if (backtime >= target_minute) + { + current_r = target_r; + current_g = target_g; + current_b = target_b; + } + else if (backtime < target_minute) + { + current_r = daytime_r - (((daytime_r - target_r)*backtime*klapse_scaling_rate)/(active_minutes*10)); + current_g = daytime_g - (((daytime_g - target_g)*backtime*klapse_scaling_rate)/(active_minutes*10)); + current_b = daytime_b - (((daytime_b - target_b)*backtime*klapse_scaling_rate)/(active_minutes*10)); + } + } + else + { + current_r = target_r + (((daytime_r - target_r)*(fadeback_minutes - backtime))/fadeback_minutes); + current_g = target_g + (((daytime_g - target_g)*(fadeback_minutes - backtime))/fadeback_minutes); + current_b = target_b + (((daytime_b - target_b)*(fadeback_minutes - backtime))/fadeback_minutes); + } + + set_rgb_brightness(current_r, current_g, current_b); + } + + if (!timer_pending(&pulse_timer)) + restart_timer(); +} + +// Brightness-based mode +void set_rgb_slider(u32 bl_lvl) +{ + if (bl_lvl >= MIN_BRIGHTNESS) + { + if ((enable_klapse == 2) && (bl_lvl <= MAX_BRIGHTNESS)) + { + if (bl_lvl > backlight_upper) + set_rgb_brightness(daytime_r, daytime_g, daytime_b); + else if (bl_lvl <= backlight_lower) + set_rgb_brightness(target_r, target_g, target_b); + else { + current_r = daytime_r - ((daytime_r - target_r)*(backlight_upper - bl_lvl)/(backlight_upper - backlight_lower)); + current_g = daytime_g - ((daytime_g - target_g)*(backlight_upper - bl_lvl)/(backlight_upper - backlight_lower)); + current_b = daytime_b - ((daytime_b - target_b)*(backlight_upper - bl_lvl)/(backlight_upper - backlight_lower)); + set_rgb_brightness(current_r, current_g, current_b); + } + } + + last_bl = bl_lvl; + } +} + +static void set_enable_klapse(int val) +{ + if ((val <= 2) && (val >= 0)) + { + if ((val == 1) && (enable_klapse != 1)) + { + if (brightness_factor_auto_enable == 0) + { + klapse_pulse(0); + } + } + else if (val == 0) + { + set_rgb_brightness(daytime_r, daytime_g, daytime_b); + current_r = daytime_r; + current_g = daytime_g; + current_b = daytime_b; + + if (brightness_factor_auto_enable == 0) + flush_timer(); + } + else if (enable_klapse == 2) + set_rgb_slider(last_bl); + + enable_klapse = val; + } +} + +//SYSFS node for details : +static ssize_t info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t count = 0; + + count += sprintf(buf, "Author : %s\nVersion : %s\nLicense : %s\n", AUT, VER, LIC); + + return count; +} + +static ssize_t info_dump(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return count; +} + +//SYSFS tunables : +static ssize_t enable_klapse_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t count = 0; + + count += sprintf(buf, "%d\n", enable_klapse); + + return count; +} + +static ssize_t enable_klapse_dump(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int tmpval = 0; + + if (!sscanf(buf, "%d", &tmpval)) + return -EINVAL; + + set_enable_klapse(tmpval); + + return count; +} + +static ssize_t daytime_rgb_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t count = 0; + + count += sprintf(buf, "%u %u %u\n", daytime_r, daytime_g, daytime_b); + + return count; +} + +static ssize_t daytime_rgb_dump(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int tmp_r = 0, tmp_g = 0, tmp_b = 0; + + if (sscanf(buf, "%u %u %u", &tmp_r, &tmp_g, &tmp_b) != 3) + return -EINVAL; + + if ((tmp_r >= (SCALE_VAL_MIN)) && (tmp_r <= MAX_SCALE) && + (tmp_g >= (SCALE_VAL_MIN)) && (tmp_g <= MAX_SCALE) && + (tmp_b >= (SCALE_VAL_MIN)) && (tmp_b <= MAX_SCALE)) + { + daytime_r = tmp_r; + daytime_g = tmp_g; + daytime_b = tmp_b; + + if ((enable_klapse == 0) || ((enable_klapse == 1) && !hour_within_range(klapse_start_hour, klapse_stop_hour, tm.tm_hour))) + set_rgb_brightness(daytime_r, daytime_g, daytime_b); + else if (enable_klapse == 2) + set_rgb_slider(last_bl); + + return count; + } + + return -EINVAL; +} + +static ssize_t target_rgb_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t count = 0; + + count += sprintf(buf, "%u %u %u\n", target_r, target_g, target_b); + + return count; +} + +static ssize_t target_rgb_dump(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int tmp_r = 0, tmp_g = 0, tmp_b = 0; + + if (sscanf(buf, "%u %u %u", &tmp_r, &tmp_g, &tmp_b) != 3) + return -EINVAL; + + if ((tmp_r >= (SCALE_VAL_MIN)) && (tmp_r <= MAX_SCALE) && + (tmp_g >= (SCALE_VAL_MIN)) && (tmp_g <= MAX_SCALE) && + (tmp_b >= (SCALE_VAL_MIN)) && (tmp_b <= MAX_SCALE)) + { + target_r = tmp_r; + target_g = tmp_g; + target_b = tmp_b; + + if (enable_klapse == 2) + set_rgb_slider(last_bl); + + return count; + } + + return -EINVAL; +} + +static ssize_t klapse_start_hour_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t count = 0; + + count += sprintf(buf, "%u\n", klapse_start_hour); + + return count; +} + +static ssize_t klapse_start_hour_dump(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int tmpval = 0; + + if (!sscanf(buf, "%u", &tmpval)) + return -EINVAL; + + if ((tmpval >= 0) && (tmpval < 24) && (tmpval != klapse_stop_hour)) + { + klapse_start_hour = tmpval; + calc_active_minutes(); + } + + return count; +} + +static ssize_t klapse_stop_hour_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t count = 0; + + count += sprintf(buf, "%u\n", klapse_stop_hour); + + return count; +} + +static ssize_t klapse_stop_hour_dump(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int tmpval = 0; + + if (!sscanf(buf, "%u", &tmpval)) + return -EINVAL; + + if ((tmpval >= 0) && (tmpval < 24) && (tmpval != klapse_start_hour)) + { + klapse_stop_hour = tmpval; + calc_active_minutes(); + } + + return count; +} + +static ssize_t klapse_scaling_rate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t count = 0; + + count += sprintf(buf, "%u\n", klapse_scaling_rate); + + return count; +} + +static ssize_t klapse_scaling_rate_dump(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int tmpval = 0; + + if (!sscanf(buf, "%u", &tmpval)) + return -EINVAL; + + if ((tmpval > 0) && (tmpval < (MAX_SCALE*10))) + { + klapse_scaling_rate = tmpval; + target_minute = (active_minutes*10)/klapse_scaling_rate; + } + + return count; +} + +static ssize_t brightness_factor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t count = 0; + + count += sprintf(buf, "%u\n", brightness_factor); + + return count; +} + +static ssize_t brightness_factor_dump(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int tmpval = 0; + + if (!sscanf(buf, "%u", &tmpval)) + return -EINVAL; + + if ((tmpval >= 2) && (tmpval <= 10)) + { + b_cache = tmpval; + if (brightness_factor_auto_enable == 0) + set_rgb_brightness(K_RED, K_GREEN, K_BLUE); + } + + return count; +} + + +static ssize_t brightness_factor_auto_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t count = 0; + + count += sprintf(buf, "%u\n", brightness_factor_auto_enable); + + return count; +} + +static ssize_t brightness_factor_auto_enable_dump(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int tmpval = 0; + + if (!sscanf(buf, "%u", &tmpval)) + return -EINVAL; + + if ((tmpval == 0) || (tmpval == 1)) + { + if ((tmpval == 1) && hour_within_range(brightness_factor_auto_start_hour, brightness_factor_auto_stop_hour, tm.tm_hour)) + set_rgb_brightness(K_RED, K_GREEN, K_BLUE); + if ((tmpval == 1) && (enable_klapse != 1) && (brightness_factor_auto_enable != 1)) + { + klapse_pulse(0); + } + + if ((tmpval == 0) && (enable_klapse == 0)) + { + flush_timer(); + } + brightness_factor_auto_enable = tmpval; + } + + return count; +} + +static ssize_t brightness_factor_auto_start_hour_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t count = 0; + + count += sprintf(buf, "%u\n", brightness_factor_auto_start_hour); + + return count; +} + +static ssize_t brightness_factor_auto_start_hour_dump(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int tmpval = 0; + + if (!sscanf(buf, "%u", &tmpval)) + return -EINVAL; + + if ((tmpval >= 0) && (tmpval < 24) && (tmpval != brightness_factor_auto_stop_hour)) + { + brightness_factor_auto_start_hour = tmpval; + if ((brightness_factor_auto_enable == 1) && hour_within_range(brightness_factor_auto_start_hour, brightness_factor_auto_stop_hour, tm.tm_hour)) + set_rgb_brightness(K_RED, K_GREEN, K_BLUE); + } + + return count; +} + +static ssize_t brightness_factor_auto_stop_hour_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t count = 0; + + count += sprintf(buf, "%u\n", brightness_factor_auto_stop_hour); + + return count; +} + +static ssize_t brightness_factor_auto_stop_hour_dump(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int tmpval = 0; + + if (!sscanf(buf, "%u", &tmpval)) + return -EINVAL; + + if ((tmpval >= 0) && (tmpval < 24) && (tmpval != brightness_factor_auto_start_hour)) + { + brightness_factor_auto_stop_hour = tmpval; + if ((brightness_factor_auto_enable == 1) && hour_within_range(brightness_factor_auto_start_hour, brightness_factor_auto_stop_hour, tm.tm_hour)) + set_rgb_brightness(K_RED, K_GREEN, K_BLUE); + } + + return count; +} + +static ssize_t backlight_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t count = 0; + + count += sprintf(buf, "%u %u\n", backlight_lower, backlight_upper); + + return count; +} + +static ssize_t backlight_range_dump(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int tmp_l = 0, tmp_u = 0, tmp = 0; + + if (sscanf(buf, "%u %u", &tmp_l, &tmp_u) != 2) + return -EINVAL; + + if ((tmp_l >= MIN_BRIGHTNESS) && (tmp_l <= MAX_BRIGHTNESS) && + (tmp_u >= MIN_BRIGHTNESS) && (tmp_u <= MAX_BRIGHTNESS)) + { + // Swap min and max correct + if (tmp_u < tmp_l) + { + tmp = tmp_u; + tmp_u = tmp_l; + tmp_l = tmp; + } + + backlight_lower = tmp_l; + backlight_upper = tmp_u; + + if (enable_klapse == 2) + set_rgb_slider(last_bl); + } + + return count; +} + +static ssize_t pulse_freq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t count = 0; + + count += sprintf(buf, "%u\n", pulse_freq); + + return count; +} + +static ssize_t pulse_freq_dump(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int tmp = 0; + + if (!sscanf(buf, "%u", &tmp)) + return -EINVAL; + + if ((tmp >= 1000) && (tmp <= 10*60000)) + { + pulse_freq = tmp; + } + + return count; +} + +static ssize_t fadeback_minutes_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t count = 0; + + count += sprintf(buf, "%u\n", fadeback_minutes); + + return count; +} + +static ssize_t fadeback_minutes_dump(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int tmp = 0; + + if (!sscanf(buf, "%u", &tmp)) + return -EINVAL; + + if ((tmp >= 2) && (tmp <= active_minutes)) + { + fadeback_minutes = tmp; + } + + return count; +} + + +static DEVICE_ATTR(enable_klapse, 0644, enable_klapse_show, enable_klapse_dump); +static DEVICE_ATTR(daytime_rgb, 0644, daytime_rgb_show, daytime_rgb_dump); +static DEVICE_ATTR(target_rgb, 0644, target_rgb_show, target_rgb_dump); +static DEVICE_ATTR(klapse_start_hour, 0644, klapse_start_hour_show, klapse_start_hour_dump); +static DEVICE_ATTR(klapse_stop_hour, 0644, klapse_stop_hour_show, klapse_stop_hour_dump); +static DEVICE_ATTR(klapse_scaling_rate, 0644, klapse_scaling_rate_show, klapse_scaling_rate_dump); +static DEVICE_ATTR(brightness_factor, 0644, brightness_factor_show, brightness_factor_dump); +static DEVICE_ATTR(brightness_factor_auto, 0644, brightness_factor_auto_enable_show, brightness_factor_auto_enable_dump); +static DEVICE_ATTR(brightness_factor_auto_start_hour, 0644, brightness_factor_auto_start_hour_show, brightness_factor_auto_start_hour_dump); +static DEVICE_ATTR(brightness_factor_auto_stop_hour, 0644, brightness_factor_auto_stop_hour_show, brightness_factor_auto_stop_hour_dump); +static DEVICE_ATTR(backlight_range, 0644, backlight_range_show, backlight_range_dump); +static DEVICE_ATTR(pulse_freq, 0644, pulse_freq_show, pulse_freq_dump); +static DEVICE_ATTR(fadeback_minutes, 0644, fadeback_minutes_show, fadeback_minutes_dump); +static DEVICE_ATTR(info, 0444, info_show, info_dump); + +//INIT +static void values_setup(void) +{ + daytime_r = MAX_SCALE; + daytime_g = MAX_SCALE; + daytime_b = MAX_SCALE; + current_r = MAX_SCALE; + current_g = MAX_SCALE; + current_b = MAX_SCALE; + target_r = MAX_SCALE; + target_g = (MAX_SCALE*79)/100; + target_b = (MAX_SCALE*59)/100; + brightness_factor = 10; + b_cache = brightness_factor; + klapse_scaling_rate = 30; + klapse_start_hour = 17; + klapse_stop_hour = 7; + brightness_factor_auto_start_hour = 23; + brightness_factor_auto_stop_hour = 6; + enable_klapse = DEFAULT_ENABLE; + brightness_factor_auto_enable = 0; + backlight_lower = LOWER_BL_LVL; + backlight_upper = UPPER_BL_LVL; + last_bl = 1023; + pulse_freq = 60000; + fadeback_minutes = 30; + calc_active_minutes(); + + do_gettimeofday(&time); + local_time = (u32)(time.tv_sec - (sys_tz.tz_minuteswest * 60)); + rtc_time_to_tm(local_time, &tm); +} + +struct kobject *klapse_kobj; +EXPORT_SYMBOL_GPL(klapse_kobj); + +static int __init klapse_init(void) +{ + int rc; + printk(KERN_INFO "KLapse init entered!!!.\n"); + + values_setup(); + + klapse_kobj = kobject_create_and_add("klapse", NULL) ; + if (klapse_kobj == NULL) { + pr_warn("%s: klapse_kobj create_and_add failed\n", __func__); + } + + rc = sysfs_create_file(klapse_kobj, &dev_attr_info.attr); + if (rc) { + pr_warn("%s: sysfs_create_file failed for info\n", __func__); + } + rc = sysfs_create_file(klapse_kobj, &dev_attr_enable_klapse.attr); + if (rc) { + pr_warn("%s: sysfs_create_file failed for enable_klapse\n", __func__); + } + rc = sysfs_create_file(klapse_kobj, &dev_attr_daytime_rgb.attr); + if (rc) { + pr_warn("%s: sysfs_create_file failed for daytime_rgb\n", __func__); + } + rc = sysfs_create_file(klapse_kobj, &dev_attr_target_rgb.attr); + if (rc) { + pr_warn("%s: sysfs_create_file failed for target_rgb\n", __func__); + } + rc = sysfs_create_file(klapse_kobj, &dev_attr_klapse_start_hour.attr); + if (rc) { + pr_warn("%s: sysfs_create_file failed for klapse_start_hour\n", __func__); + } + rc = sysfs_create_file(klapse_kobj, &dev_attr_klapse_stop_hour.attr); + if (rc) { + pr_warn("%s: sysfs_create_file failed for klapse_stop_hour\n", __func__); + } + rc = sysfs_create_file(klapse_kobj, &dev_attr_klapse_scaling_rate.attr); + if (rc) { + pr_warn("%s: sysfs_create_file failed for klapse_scaling_rate\n", __func__); + } + rc = sysfs_create_file(klapse_kobj, &dev_attr_brightness_factor.attr); + if (rc) { + pr_warn("%s: sysfs_create_file failed for brightness_factor\n", __func__); + } + rc = sysfs_create_file(klapse_kobj, &dev_attr_brightness_factor_auto.attr); + if (rc) { + pr_warn("%s: sysfs_create_file failed for brightness_factor_auto\n", __func__); + } + rc = sysfs_create_file(klapse_kobj, &dev_attr_brightness_factor_auto_start_hour.attr); + if (rc) { + pr_warn("%s: sysfs_create_file failed for brightness_factor_auto_start_hour\n", __func__); + } + rc = sysfs_create_file(klapse_kobj, &dev_attr_brightness_factor_auto_stop_hour.attr); + if (rc) { + pr_warn("%s: sysfs_create_file failed for brightness_factor_auto_stop_hour\n", __func__); + } + rc = sysfs_create_file(klapse_kobj, &dev_attr_backlight_range.attr); + if (rc) { + pr_warn("%s: sysfs_create_file failed for backlight_range\n", __func__); + } + rc = sysfs_create_file(klapse_kobj, &dev_attr_pulse_freq.attr); + if (rc) { + pr_warn("%s: sysfs_create_file failed for pulse_freq\n", __func__); + } + rc = sysfs_create_file(klapse_kobj, &dev_attr_fadeback_minutes.attr); + if (rc) { + pr_warn("%s: sysfs_create_file failed for fadeback_minutes\n", __func__); + } + + setup_timer(&pulse_timer, klapse_pulse, 0); + + printk(KERN_INFO "KLapse init returning!!!.\n"); + + return 0; +} + +static void __exit klapse_exit(void){ + printk(KERN_INFO "KLapse exit entered!!!.\n"); + kobject_del(klapse_kobj); + del_timer(&pulse_timer); + printk(KERN_INFO "KLapse exit finished!!!.\n"); +} + +module_init(klapse_init); +module_exit(klapse_exit); diff --git a/drivers/video/fbdev/msm/klapse.h b/drivers/video/fbdev/msm/klapse.h new file mode 100644 index 000000000000..2da625c97b43 --- /dev/null +++ b/drivers/video/fbdev/msm/klapse.h @@ -0,0 +1,18 @@ +#ifndef _LINUX_KLAPSE_H +#define _LINUX_KLAPSE_H + +/* Required variables for external access. Change as per use */ +extern void set_rgb_slider(u32 bl_lvl); + +// This file uses generalised K_### defines +// The interpretation (right argument) should be the respective color's var that you +// include as extern via the CUSTOM_HEADER above +#define K_RED kcal_red +#define K_GREEN kcal_green +#define K_BLUE kcal_blue + +#define K_TYPE unsigned short + +extern K_TYPE K_RED, K_GREEN, K_BLUE; + +#endif /* _LINUX_KLAPSE_H */