From bf714b907a14a93625124d3e64ccdaf39b082e41 Mon Sep 17 00:00:00 2001 From: galaxyfreak Date: Sun, 18 May 2014 11:53:30 -0700 Subject: [PATCH] Add backlight notification support for Pico --- arch/arm/configs/pico_defconfig | 3 + drivers/leds/led-class.c | 44 ++++ drivers/misc/Kconfig | 22 ++ drivers/misc/Makefile | 1 + drivers/misc/bln.c | 496 ++++++++++++++++++++++++++++++++++++++++ include/linux/bln.h | 38 +++ 6 files changed, 604 insertions(+) create mode 100644 drivers/misc/bln.c create mode 100644 include/linux/bln.h diff --git a/arch/arm/configs/pico_defconfig b/arch/arm/configs/pico_defconfig index 2a730b5..7748945 100644 --- a/arch/arm/configs/pico_defconfig +++ b/arch/arm/configs/pico_defconfig @@ -2325,6 +2325,9 @@ CONFIG_LEDS_MSM_PMIC=y # CONFIG_LEDS_TRIGGERS is not set # CONFIG_LEDS_PM8029 is not set CONFIG_LEDS_PMIC8029=y +CONFIG_GENERIC_BLN=y +# CONFIG_GENERIC_BLN_EMULATE_BUTTONS_LED is not set +CONFIG_GENERIC_BLN_USE_WAKELOCK=y # # LED Triggers diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index b444059..09a4db2 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -25,6 +25,43 @@ #define LED_BUFF_SIZE 50 +#ifdef CONFIG_GENERIC_BLN +#include + +struct led_classdev *bln_led_cdev; + +static int led_bln_enable(int led_mask) +{ + led_set_brightness(bln_led_cdev, bln_led_cdev->max_brightness); + return 0; +} + +static int led_bln_disable(int led_mask) +{ + led_set_brightness(bln_led_cdev, LED_OFF); + return 0; +} + +static int led_bln_power_on(void) +{ + return 0; +} + +static int led_bln_power_off(void) +{ + return 0; +} + +static struct bln_implementation led_bln = { + .enable = led_bln_enable, + .disable = led_bln_disable, + .power_on = led_bln_power_on, + .power_off = led_bln_power_off, + .led_count = 1 +}; + +#endif + static struct class *leds_class; static struct workqueue_struct *leds_workqueue; @@ -346,6 +383,13 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) printk(KERN_DEBUG "Registered led device: %s\n", led_cdev->name); +#ifdef CONFIG_GENERIC_BLN + if (strcmp(led_cdev, "button-backlight")) + { + bln_led_cdev = led_cdev; + register_bln_implementation(&led_bln); + } +#endif return 0; } diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 4e2496f..a24ba67 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -703,6 +703,28 @@ config FSYNC_CONTROL help Google it. +config GENERIC_BLN + bool "Generic BLN support for backlight notification" + depends on SYSFS && EXPERIMENTAL + default y + help + Say Y here to enable the backlight notification + for android led-notification (modified liblight needed) + +config GENERIC_BLN_EMULATE_BUTTONS_LED + bool "Emulate buttons led" + depends on GENERIC_BLN + default n + help + Say Y here to simulate buttons led + +config GENERIC_BLN_USE_WAKELOCK + bool "Use a kernel wakelock to retain the leds enabled in suspend." + depends on GENERIC_BLN + default n + help + Say Y here use the wakelock implementation of the bln driver + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 364ee37..dd22b50 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -74,3 +74,4 @@ obj-$(CONFIG_CABLE_DETECT_8X60) += cable_detect.o obj-$(CONFIG_CABLE_DETECT_8960) += cable_detect_8960.o obj-$(CONFIG_CABLE_DETECT_8X55) += cable_detect_8x55.o obj-$(CONFIG_FSYNC_CONTROL) += fsync_control.o +obj-$(CONFIG_GENERIC_BLN) += bln.o diff --git a/drivers/misc/bln.c b/drivers/misc/bln.c new file mode 100644 index 0000000..339ec8a --- /dev/null +++ b/drivers/misc/bln.c @@ -0,0 +1,496 @@ +/* drivers/misc/bln.c + * + * Copyright 2011 Michael Richter (alias neldar) + * Copyright 2011 Adam Kent + * Copyright 2014 Jonathan Jason Dennis [Meticulus] + theonejohnnyd@gmail.com + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_GENERIC_BLN_USE_WAKELOCK +#include +#endif + +static bool bln_enabled = true; +static bool bln_ongoing = false; /* ongoing LED Notification */ +static int bln_blink_state = 0; +static int bln_blink_mode = 1; /* blink by default */ +static bool bln_suspended = false; /* is system suspended */ +static struct bln_implementation *bln_imp = NULL; + +static long unsigned int notification_led_mask = 0x0; + +#ifdef CONFIG_GENERIC_BLN_USE_WAKELOCK +static bool use_wakelock = true; +static struct wake_lock bln_wake_lock; +#endif + +#ifdef CONFIG_GENERIC_BLN_EMULATE_BUTTONS_LED +static bool buttons_led_enabled = false; +#endif + +#define BACKLIGHTNOTIFICATION_VERSION 9 + +static int gen_all_leds_mask(void) +{ + int i = 0; + int mask = 0x0; + + for(; i < bln_imp->led_count; i++) + mask |= 1 << i; + + return mask; +} + +static int get_led_mask(void){ + return (notification_led_mask != 0) ? notification_led_mask : gen_all_leds_mask(); +} + +static void reset_bln_states(void) +{ + bln_blink_state = 0; + bln_ongoing = false; +} + +static void bln_enable_backlights(int mask) +{ + if (likely(bln_imp && bln_imp->enable)) + bln_imp->enable(mask); +} + +static void bln_disable_backlights(int mask) +{ + if (likely(bln_imp && bln_imp->disable)) + bln_imp->disable(mask); +} + +static void bln_power_on(void) +{ + if (likely(bln_imp && bln_imp->power_on)) { +#ifdef CONFIG_GENERIC_BLN_USE_WAKELOCK + if(use_wakelock && !wake_lock_active(&bln_wake_lock)){ + wake_lock(&bln_wake_lock); + } +#endif + bln_imp->power_on(); + } +} + +static void bln_power_off(void) +{ + if (likely(bln_imp && bln_imp->power_off)) { + bln_imp->power_off(); +#ifdef CONFIG_GENERIC_BLN_USE_WAKELOCK + if(wake_lock_active(&bln_wake_lock)){ + wake_unlock(&bln_wake_lock); + } +#endif + } +} + +static void bln_early_suspend(struct early_suspend *h) +{ + bln_suspended = true; +} + +static void bln_late_resume(struct early_suspend *h) +{ + bln_suspended = false; + + reset_bln_states(); +} + +static struct early_suspend bln_suspend_data = { + .level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1, + .suspend = bln_early_suspend, + .resume = bln_late_resume, +}; + +static void blink_thread(void) +{ + while(bln_suspended) + { + bln_enable_backlights(get_led_mask()); + msleep(1000); + bln_disable_backlights(get_led_mask()); + msleep(1000); + } +} + +static void enable_led_notification(void) +{ + if (!bln_enabled) + return; + + /* + * dont allow led notifications while the screen is on, + * avoid to interfere with the normal buttons led + */ + if (!bln_suspended) + return; + + /* + * If we already have a blink thread going + * don't start another one. + */ + if(bln_ongoing & bln_blink_mode) + return; + + bln_ongoing = true; + + bln_power_on(); + if(!bln_blink_mode) + bln_enable_backlights(get_led_mask()); + else + kthread_run(&blink_thread, NULL,"bln_blink_thread"); + + pr_info("%s: notification led enabled\n", __FUNCTION__); +} + +static void disable_led_notification(void) +{ + if (bln_suspended && bln_ongoing) { + bln_disable_backlights(gen_all_leds_mask()); + bln_power_off(); + } + + reset_bln_states(); + + pr_info("%s: notification led disabled\n", __FUNCTION__); +} + +static ssize_t backlightnotification_status_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + + if(unlikely(!bln_imp)) + ret = -1; + + if(bln_enabled) + ret = 1; + else + ret = 0; + + return sprintf(buf, "%u\n", ret); +} + +static ssize_t backlightnotification_status_write(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + unsigned int data; + + if(unlikely(!bln_imp)) { + pr_err("%s: no BLN implementation registered!\n", __FUNCTION__); + return size; + } + + if (sscanf(buf, "%u\n", &data) != 1) { + pr_info("%s: input error\n", __FUNCTION__); + return size; + } + + pr_devel("%s: %u \n", __FUNCTION__, data); + + if (data == 1) { + pr_info("%s: BLN function enabled\n", __FUNCTION__); + bln_enabled = true; + } else if (data == 0) { + pr_info("%s: BLN function disabled\n", __FUNCTION__); + bln_enabled = false; + if (bln_ongoing) + disable_led_notification(); + } else { + pr_info("%s: invalid input range %u\n", __FUNCTION__, + data); + } + + return size; +} + +static ssize_t notification_led_status_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf,"%u\n", (bln_ongoing ? 1 : 0)); +} + +static ssize_t notification_led_status_write(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + unsigned int data; + + if (sscanf(buf, "%u\n", &data) != 1) { + pr_info("%s: input error\n", __FUNCTION__); + return size; + } + + if (data == 1) + enable_led_notification(); + else if (data == 0) + disable_led_notification(); + else + pr_info("%s: wrong input %u\n", __FUNCTION__, data); + + return size; +} + +static ssize_t notification_led_mask_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf,"%lu\n", notification_led_mask); +} + +static ssize_t notification_led_mask_write(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + unsigned int data; + + if (sscanf(buf, "%u\n", &data) != 1) { + pr_info("%s: input error\n", __FUNCTION__); + return size; + } + + if(data & gen_all_leds_mask()){ + notification_led_mask = data; + } else { + //TODO: correct error code + return -1; + } + + return size; +} + +static ssize_t blink_control_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", bln_blink_state); +} + +static ssize_t blink_control_write(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + unsigned int data; + + if (!bln_ongoing) + return size; + + if (sscanf(buf, "%u\n", &data) != 1) { + pr_info("%s: input error\n", __FUNCTION__); + return size; + } + + /* reversed logic: + * 1 = leds off + * 0 = leds on + */ + if (data == 1) { + bln_blink_state = 1; + bln_disable_backlights(get_led_mask()); + } else if (data == 0) { + bln_blink_state = 0; + bln_enable_backlights(get_led_mask()); + } else { + pr_info("%s: wrong input %u\n", __FUNCTION__, data); + } + + return size; +} + +static ssize_t backlightnotification_version(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", BACKLIGHTNOTIFICATION_VERSION); +} + +static ssize_t led_count_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int ret = 0x0; + + if (bln_imp) + ret = bln_imp->led_count; + + return sprintf(buf,"%u\n", ret); +} + +#ifdef CONFIG_GENERIC_BLN_USE_WAKELOCK +static ssize_t wakelock_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf,"%u\n", (use_wakelock ? 1 : 0)); +} + +static ssize_t wakelock_write(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + unsigned int data; + + if (sscanf(buf, "%u\n", &data) != 1) { + pr_info("%s: input error\n", __FUNCTION__); + return size; + } + + if (data == 1) { + use_wakelock = true; + } else if (data == 0) { + use_wakelock = false; + } else { + pr_info("%s: wrong input %u\n", __FUNCTION__, data); + } + + return size; +} +#endif + +#ifdef CONFIG_GENERIC_BLN_EMULATE_BUTTONS_LED +static ssize_t buttons_led_status_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf,"%u\n", (buttons_led_enabled ? 1 : 0)); +} + +static ssize_t buttons_led_status_write(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + unsigned int data; + + if (sscanf(buf, "%u\n", &data) != 1) { + pr_info("%s: input error\n", __FUNCTION__); + return size; + } + + if (data == 1) { + if(!bln_suspended){ + buttons_led_enabled = true; + bln_power_on(); + bln_enable_backlights(gen_all_leds_mask()); + } + } else if (data == 0) { + if(!bln_suspended){ + buttons_led_enabled = false; + bln_disable_backlights(gen_all_leds_mask()); + } + } else { + pr_info("%s: wrong input %u\n", __FUNCTION__, data); + } + + return size; +} + +static DEVICE_ATTR(buttons_led, S_IRUGO | S_IWUGO, + buttons_led_status_read, + buttons_led_status_write); +#endif + +static DEVICE_ATTR(blink_control, S_IRUGO | S_IWUGO, blink_control_read, + blink_control_write); +static DEVICE_ATTR(enabled, S_IRUGO | S_IWUGO, + backlightnotification_status_read, + backlightnotification_status_write); +static DEVICE_ATTR(led_count, S_IRUGO , led_count_read, NULL); +static DEVICE_ATTR(notification_led, S_IRUGO | S_IWUGO, + notification_led_status_read, + notification_led_status_write); +static DEVICE_ATTR(notification_led_mask, S_IRUGO | S_IWUGO, + notification_led_mask_read, + notification_led_mask_write); +static DEVICE_ATTR(version, S_IRUGO , backlightnotification_version, NULL); + +#ifdef CONFIG_GENERIC_BLN_USE_WAKELOCK +static DEVICE_ATTR(wakelock, S_IRUGO | S_IWUGO, wakelock_read, wakelock_write); +#endif + + +static struct attribute *bln_notification_attributes[] = { + &dev_attr_blink_control.attr, + &dev_attr_enabled.attr, + &dev_attr_led_count.attr, + &dev_attr_notification_led.attr, + &dev_attr_notification_led_mask.attr, +#ifdef CONFIG_GENERIC_BLN_EMULATE_BUTTONS_LED + &dev_attr_buttons_led.attr, +#endif +#ifdef CONFIG_GENERIC_BLN_USE_WAKELOCK + &dev_attr_wakelock.attr, +#endif + &dev_attr_version.attr, + NULL +}; + +static struct attribute_group bln_notification_group = { + .attrs = bln_notification_attributes, +}; + +static struct miscdevice bln_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "backlightnotification", +}; + +/** + * register_bln_implementation - register a bln implementation of a touchkey device device + * @imp: bln implementation structure + * + * Register a bln implementation with the bln kernel module. + */ +void register_bln_implementation(struct bln_implementation *imp) +{ + //TODO: more checks + if(imp){ + bln_imp = imp; + } +} +EXPORT_SYMBOL(register_bln_implementation); + +/** + * bln_is_ongoing - check if a bln (led) notification is ongoing + */ +bool bln_is_ongoing() +{ + return bln_ongoing; +} +EXPORT_SYMBOL(bln_is_ongoing); + +static int __init bln_control_init(void) +{ + int ret; + + pr_info("%s misc_register(%s)\n", __FUNCTION__, bln_device.name); + ret = misc_register(&bln_device); + if (ret) { + pr_err("%s misc_register(%s) fail\n", __FUNCTION__, + bln_device.name); + return 1; + } + + /* add the bln attributes */ + if (sysfs_create_group(&bln_device.this_device->kobj, + &bln_notification_group) < 0) { + pr_err("%s sysfs_create_group fail\n", __FUNCTION__); + pr_err("Failed to create sysfs group for device (%s)!\n", + bln_device.name); + return 1; + } + +#ifdef CONFIG_GENERIC_BLN_USE_WAKELOCK + wake_lock_init(&bln_wake_lock, WAKE_LOCK_SUSPEND, "bln_kernel_wake_lock"); +#endif + + register_early_suspend(&bln_suspend_data); + + return 0; +} + +device_initcall(bln_control_init); diff --git a/include/linux/bln.h b/include/linux/bln.h new file mode 100644 index 0000000..fb7b37a --- /dev/null +++ b/include/linux/bln.h @@ -0,0 +1,38 @@ +/* include/linux/bln.h */ + +#ifndef _LINUX_BLN_H +#define _LINUX_BLN_H + +/** + * struct bln_implementation - a structure containing BLN controls + * @enable: enables the leds given by ledmask + * @disable: disables the leds given by ledmask + * @power_on: powers on the components to enable the leds + * @power_off: powers off the components + * @led_count: number of leds in this bln implementation (see ledmask) + * + * The BLN implementation structure contains all LED control functions of an + * touchkey device. + * + * Enable/Disable functions should only affect the leds given by the ledmask. + * It should not configure components necessary for powering the leds (e.g. + * regulators, GPIOs). + * + * Ledmask: the least significant bit is the first led, e.g. 0x1 is the left + * led, on the Nexus S is that the back button. + * + * Power On/Off functions take care of the necessary components that needs to + * be (re-)configured so that enable/disable of leds could operate. + */ +struct bln_implementation { + int (*enable)(int led_mask); + int (*disable)(int led_mask); + int (*power_on)(void); + int (*power_off)(void); + unsigned int led_count; +}; + +void register_bln_implementation(struct bln_implementation *imp); +bool bln_is_ongoing(void); +#endif + -- 1.8.5.2