diff options
author | Dorian Snyder <dastin1015@gmail.com> | 2013-06-12 02:24:45 -0700 |
---|---|---|
committer | Dorian Snyder <dastin1015@gmail.com> | 2013-06-20 00:06:04 -0700 |
commit | 4b2308ce699b9c599dd6e6acf57ac11f483381d9 (patch) | |
tree | 4c31179b06d094887b1c8ca70264cf8f184a5981 /drivers/leds | |
parent | 855d6a6c1f7c54ef073caac3f6c5f9b1ed72eb4d (diff) | |
download | kernel_samsung_smdk4412-4b2308ce699b9c599dd6e6acf57ac11f483381d9.zip kernel_samsung_smdk4412-4b2308ce699b9c599dd6e6acf57ac11f483381d9.tar.gz kernel_samsung_smdk4412-4b2308ce699b9c599dd6e6acf57ac11f483381d9.tar.bz2 |
d710: initial support for the Epic 4G Touch (SPH-D710)
Change-Id: Iafbd9fb45253b02d539ac0ba114f57b3bf9eeed4
Diffstat (limited to 'drivers/leds')
-rw-r--r-- | drivers/leds/Kconfig | 6 | ||||
-rw-r--r-- | drivers/leds/Makefile | 1 | ||||
-rw-r--r-- | drivers/leds/ledtrig-notification.c | 364 | ||||
-rw-r--r-- | drivers/leds/ledtrig-timer.c | 79 |
4 files changed, 450 insertions, 0 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 7cdcd46..ea556d6 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -528,6 +528,12 @@ config LEDS_TRIGGER_SLEEP help This turns LEDs on when the screen is off but the cpu still running. +config LEDS_TRIGGER_NOTIFICATION + tristate "Notification LED Trigger" + help + This allows LEDS to be controlled as a notification device. + If unsure, say Y. + comment "iptables trigger is under Netfilter config (LED target)" depends on LEDS_TRIGGERS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index e538739..3e3945d 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -60,3 +60,4 @@ obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o obj-$(CONFIG_LEDS_TRIGGER_SLEEP) += ledtrig-sleep.o +obj-$(CONFIG_LEDS_TRIGGER_NOTIFICATION) += ledtrig-notification.o diff --git a/drivers/leds/ledtrig-notification.c b/drivers/leds/ledtrig-notification.c new file mode 100644 index 0000000..25cf3f5 --- /dev/null +++ b/drivers/leds/ledtrig-notification.c @@ -0,0 +1,364 @@ +/* + * LED notification trigger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include <linux/sysdev.h> +#include <linux/timer.h> +#include <linux/ctype.h> +#include <linux/leds.h> +#include <linux/slab.h> +#include "leds.h" +#include <linux/android_alarm.h> +#include <linux/wakelock.h> +#include <asm/mach/time.h> +#include <linux/hrtimer.h> +#include <linux/hrtimer.h> + +static struct wake_lock ledtrig_rtc_timer_wakelock; + +struct notification_trig_data { + int brightness_on; /* LED brightness during "on" period. + * (LED_OFF < brightness_on <= LED_FULL) + */ + unsigned long delay_on; /* milliseconds on */ + unsigned long delay_off; /* milliseconds off */ + unsigned long blink_cnt; /* number of blink times */ + unsigned long off_duration; /* blink stop duration */ + + unsigned long current_blink_cnt; /* current blink count */ + + struct timer_list timer; + struct alarm alarm; + struct wake_lock wakelock; +}; + +static int led_rtc_set_alarm(struct led_classdev *led_cdev, unsigned long msec) +{ + struct notification_trig_data *timer_data = led_cdev->trigger_data; + ktime_t expire; + ktime_t now; + + now = ktime_get_real(); + expire = ktime_add(now, ns_to_ktime((u64)msec*1000*1000)); + + alarm_start_range(&timer_data->alarm, expire, expire); + if(msec < 1500) { + /* If expire time is less than 1.5s keep a wake lock to prevent constant + * suspend fail. RTC alarm fails to suspend if the earliest expiration + * time is less than a second. Keep the wakelock just a jiffy more than + * the expire time to prevent wake lock timeout. */ + wake_lock_timeout(&timer_data->wakelock, (msec*HZ/1000)+1); + } + return 0; +} + +static void led_rtc_timer_function(struct alarm *alarm) +{ + struct notification_trig_data *timer_data = container_of(alarm, struct notification_trig_data, alarm); + + /* let led_timer_function do the actual work */ + mod_timer(&timer_data->timer, jiffies + 1); +} + +static void led_timer_function(unsigned long data) +{ + struct led_classdev *led_cdev = (struct led_classdev *) data; + struct notification_trig_data *timer_data = led_cdev->trigger_data; + unsigned long brightness; + unsigned long delay; + + if (!timer_data->delay_on || !timer_data->delay_off || !timer_data->blink_cnt) { + led_set_brightness(led_cdev, LED_OFF); + return; + } + + brightness = led_get_brightness(led_cdev); + if (!brightness) { + /* Time to switch the LED on. */ + brightness = timer_data->brightness_on; + delay = timer_data->delay_on; + } else { + /* Store the current brightness value to be able + * to restore it when the delay_off period is over. + */ + timer_data->brightness_on = brightness; + brightness = LED_OFF; + delay = timer_data->delay_off; + + if(timer_data->blink_cnt <= ++timer_data->current_blink_cnt) { + timer_data->current_blink_cnt = 0; + delay+=timer_data->off_duration; + } + } + + led_set_brightness(led_cdev, brightness); + + led_rtc_set_alarm(led_cdev, delay); +} + +static ssize_t led_delay_on_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct notification_trig_data *timer_data = led_cdev->trigger_data; + + return sprintf(buf, "%lu\n", timer_data->delay_on); +} + +static ssize_t led_delay_on_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct notification_trig_data *timer_data = led_cdev->trigger_data; + int ret = -EINVAL; + char *after; + unsigned long state = simple_strtoul(buf, &after, 10); + size_t count = after - buf; + + if (isspace(*after)) + count++; + + if (count == size) { + if (timer_data->delay_on != state) { + /* the new value differs from the previous */ + timer_data->delay_on = state; + + /* deactivate previous settings */ + del_timer_sync(&timer_data->timer); + + mod_timer(&timer_data->timer, jiffies + 1); + } + ret = count; + } + + return ret; +} + +static ssize_t led_delay_off_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct notification_trig_data *timer_data = led_cdev->trigger_data; + + return sprintf(buf, "%lu\n", timer_data->delay_off); +} + +static ssize_t led_delay_off_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct notification_trig_data *timer_data = led_cdev->trigger_data; + int ret = -EINVAL; + char *after; + unsigned long state = simple_strtoul(buf, &after, 10); + size_t count = after - buf; + + if (isspace(*after)) + count++; + + if (count == size) { + if (timer_data->delay_off != state) { + /* the new value differs from the previous */ + timer_data->delay_off = state; + + /* deactivate previous settings */ + del_timer_sync(&timer_data->timer); + + mod_timer(&timer_data->timer, jiffies + 1); + } + ret = count; + } + + return ret; +} + +static ssize_t led_blink_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct notification_trig_data *timer_data = led_cdev->trigger_data; + + return sprintf(buf, "%lu\n", timer_data->blink_cnt); +} + +static ssize_t led_blink_count_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct notification_trig_data *timer_data = led_cdev->trigger_data; + int ret = -EINVAL; + char *after; + unsigned long state = simple_strtoul(buf, &after, 10); + size_t count = after - buf; + + if (isspace(*after)) + count++; + + if (count == size) { + if (timer_data->blink_cnt != state) { + /* the new value differs from the previous */ + timer_data->blink_cnt = state; + + /* deactivate previous settings */ + del_timer_sync(&timer_data->timer); + + mod_timer(&timer_data->timer, jiffies + 1); + } + ret = count; + } + + return ret; +} + +static ssize_t led_off_duration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct notification_trig_data *timer_data = led_cdev->trigger_data; + + return sprintf(buf, "%lu\n", timer_data->off_duration); +} + +static ssize_t led_off_duration_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct notification_trig_data *timer_data = led_cdev->trigger_data; + int ret = -EINVAL; + char *after; + unsigned long state = simple_strtoul(buf, &after, 10); + size_t count = after - buf; + + if (isspace(*after)) + count++; + + if (count == size) { + if (timer_data->off_duration != state) { + /* the new value differs from the previous */ + timer_data->off_duration = state; + + /* deactivate previous settings */ + del_timer_sync(&timer_data->timer); + + mod_timer(&timer_data->timer, jiffies + 1); + } + ret = count; + } + + return ret; +} + + +static DEVICE_ATTR(delay_on, 0777, led_delay_on_show, led_delay_on_store); +static DEVICE_ATTR(delay_off, 0777, led_delay_off_show, led_delay_off_store); +static DEVICE_ATTR(blink_count, 0777, led_blink_count_show, led_blink_count_store); +static DEVICE_ATTR(off_duration, 0777, led_off_duration_show, led_off_duration_store); + +static void notification_trig_activate(struct led_classdev *led_cdev) +{ + struct notification_trig_data *timer_data; + int rc; + + timer_data = kzalloc(sizeof(struct notification_trig_data), GFP_KERNEL); + if (!timer_data) + return; + + timer_data->brightness_on = led_get_brightness(led_cdev); + if (timer_data->brightness_on == LED_OFF) + timer_data->brightness_on = led_cdev->max_brightness; + led_cdev->trigger_data = timer_data; + + init_timer(&timer_data->timer); + timer_data->timer.function = led_timer_function; + timer_data->timer.data = (unsigned long) led_cdev; + + alarm_init(&timer_data->alarm, ANDROID_ALARM_RTC_WAKEUP, + led_rtc_timer_function); + wake_lock_init(&timer_data->wakelock, WAKE_LOCK_SUSPEND, "ledtrig_rtc_timer"); + + rc = device_create_file(led_cdev->dev, &dev_attr_delay_on); + if (rc) + goto err_out; + rc = device_create_file(led_cdev->dev, &dev_attr_delay_off); + if (rc) + goto err_out_delayon; + rc = device_create_file(led_cdev->dev, &dev_attr_blink_count); + if (rc) + goto err_attr_delay_off; + rc = device_create_file(led_cdev->dev, &dev_attr_off_duration); + if (rc) + goto err_attr_blink_count; + + /* If there is hardware support for blinking, start one + * user friendly blink rate chosen by the driver. + */ + if (led_cdev->blink_set) + led_cdev->blink_set(led_cdev, + &timer_data->delay_on, &timer_data->delay_off); + + return; +err_attr_blink_count: + device_remove_file(led_cdev->dev, &dev_attr_blink_count); +err_attr_delay_off: + device_remove_file(led_cdev->dev, &dev_attr_delay_off); +err_out_delayon: + device_remove_file(led_cdev->dev, &dev_attr_delay_on); +err_out: + led_cdev->trigger_data = NULL; + kfree(timer_data); +} + +static void notification_trig_deactivate(struct led_classdev *led_cdev) +{ + struct notification_trig_data *timer_data = led_cdev->trigger_data; + unsigned long on = 0, off = 0; + + if (timer_data) { + device_remove_file(led_cdev->dev, &dev_attr_delay_on); + device_remove_file(led_cdev->dev, &dev_attr_delay_off); + device_remove_file(led_cdev->dev, &dev_attr_blink_count); + device_remove_file(led_cdev->dev, &dev_attr_off_duration); + del_timer_sync(&timer_data->timer); + alarm_cancel(&timer_data->alarm); + wake_lock_destroy(&timer_data->wakelock); + kfree(timer_data); + } + + /* If there is hardware support for blinking, stop it */ + if (led_cdev->blink_set) + led_cdev->blink_set(led_cdev, &on, &off); +} + +static struct led_trigger notification_led_trigger = { + .name = "notification", + .activate = notification_trig_activate, + .deactivate = notification_trig_deactivate, +}; + +static int __init notification_trig_init(void) +{ + return led_trigger_register(¬ification_led_trigger); +} + +static void __exit notification_trig_exit(void) +{ + led_trigger_unregister(¬ification_led_trigger); +} + +module_init(notification_trig_init); +module_exit(notification_trig_exit); + +MODULE_DESCRIPTION("Notification LED trigger"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c index 328c64c..713c59c 100644 --- a/drivers/leds/ledtrig-timer.c +++ b/drivers/leds/ledtrig-timer.c @@ -11,6 +11,7 @@ * */ + #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> @@ -19,6 +20,84 @@ #include <linux/leds.h> #include "leds.h" +#ifdef CONFIG_TARGET_LOCALE_NA +#define CONFIG_RTC_LEDTRIG_TIMER 1//chief.rtc.ledtrig +#endif /* CONFIG_TARGET_LOCALE_NA */ + + #ifdef CONFIG_TARGET_LOCALE_NA +#if defined(CONFIG_RTC_LEDTRIG_TIMER) +#include <linux/android_alarm.h> +#include <linux/wakelock.h> +#include <asm/mach/time.h> +#include <linux/hrtimer.h> +#include <linux/hrtimer.h> +#endif + +#if defined(CONFIG_RTC_LEDTRIG_TIMER) +static struct wake_lock ledtrig_rtc_timer_wakelock; +#endif +#endif /* CONFIG_TARGET_LOCALE_NA */ + +struct timer_trig_data { + int brightness_on; /* LED brightness during "on" period. + * (LED_OFF < brightness_on <= LED_FULL) + */ + unsigned long delay_on; /* milliseconds on */ + unsigned long delay_off; /* milliseconds off */ + struct timer_list timer; +#ifdef CONFIG_TARGET_LOCALE_NA +#if (CONFIG_RTC_LEDTRIG_TIMER==1) + struct alarm alarm; + struct wake_lock wakelock; +#elif (CONFIG_RTC_LEDTRIG_TIMER==2) + struct hrtimer hrtimer; +#endif +#endif /* CONFIG_TARGET_LOCALE_NA */ +}; + + +#ifdef CONFIG_TARGET_LOCALE_NA +#if (CONFIG_RTC_LEDTRIG_TIMER==1) +static int led_rtc_set_alarm(struct led_classdev *led_cdev, unsigned long msec) +{ + struct timer_trig_data *timer_data = led_cdev->trigger_data; + ktime_t expire; + ktime_t now; + + now = ktime_get_real(); + expire = ktime_add(now, ns_to_ktime((u64)msec*1000*1000)); + + alarm_start_range(&timer_data->alarm, expire, expire); + if(msec < 1500) { + /* If expire time is less than 1.5s keep a wake lock to prevent constant + * suspend fail. RTC alarm fails to suspend if the earliest expiration + * time is less than a second. Keep the wakelock just a jiffy more than + * the expire time to prevent wake lock timeout. */ + wake_lock_timeout(&timer_data->wakelock, (msec*HZ/1000)+1); + } + return 0; +} + +static void led_rtc_timer_function(struct alarm *alarm) +{ + struct timer_trig_data *timer_data = container_of(alarm, struct timer_trig_data, alarm); + + /* let led_timer_function do the actual work */ + mod_timer(&timer_data->timer, jiffies + 1); +} +#elif (CONFIG_RTC_LEDTRIG_TIMER==2) +extern int msm_pm_request_wakeup(struct hrtimer *timer); +static enum hrtimer_restart ledtrig_hrtimer_function(struct hrtimer *timer) +{ + struct timer_trig_data *timer_data = container_of(timer, struct timer_trig_data, hrtimer); + + /* let led_timer_function do the actual work */ + mod_timer(&timer_data->timer, jiffies + 1); +} +#endif +#endif /* CONFIG_TARGET_LOCALE_NA */ + + static ssize_t led_delay_on_show(struct device *dev, struct device_attribute *attr, char *buf) { |