Initial Contribution
msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142
Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index b84e46b..feb345d 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -182,6 +182,18 @@
defined as platform devices and/or OpenFirmware platform devices.
The code to use these bindings can be selected below.
+config LEDS_MSM_PDM
+ tristate "LED Support through PDM"
+ depends on LEDS_CLASS
+ help
+ This option enables support for the LEDs operated through Pulse
+ Denisty Modulation.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called leds-msm-pdm.
+
config LEDS_GPIO_PLATFORM
bool "Platform device bindings for GPIO LEDs"
depends on LEDS_GPIO
@@ -211,6 +223,12 @@
To compile this driver as a module, choose M here: the
module will be called leds-lp3944.
+config LEDS_CPLD
+ tristate "LED Support for CPLD connected LEDs"
+ depends on LEDS_CLASS
+ help
+ This option enables support for the LEDs connected to CPLD
+
config LEDS_LP5521
tristate "LED Support for N.S. LP5521 LED driver chip"
depends on LEDS_CLASS && I2C
@@ -269,6 +287,17 @@
LED driver chips accessed via the I2C bus. Supported
devices include PCA9550, PCA9551, PCA9552, and PCA9553.
+config LEDS_PM8XXX
+ tristate "LED Support for Qualcomm PMIC8XXX"
+ depends on MFD_PM8XXX
+ help
+ This option enables support for LEDs connected over PMIC8XXX
+ (Power Management IC) chip on Qualcomm reference boards,
+ for example SURF and FFAs.
+
+ To compile this driver as a module, choose M here: the module will
+ be called leds-pmic8xxx.
+
config LEDS_WM831X_STATUS
tristate "LED support for status LEDs on WM831x PMICs"
depends on LEDS_CLASS
@@ -323,6 +352,24 @@
This option enables support for BD2802GU RGB LED driver chips
accessed via the I2C bus.
+config LEDS_MSM_PMIC
+ tristate "LED Support for Qualcomm PMIC connected LEDs"
+ default y
+ depends on ARCH_MSM
+ help
+ This option enables support for LEDs connected over PMIC
+ (Power Management IC) chip on Qualcomm reference boards,
+ for example SURF and FFAs.
+
+config LEDS_PMIC8058
+ tristate "LED Support for Qualcomm PMIC8058"
+ default n
+ depends on PMIC8058
+ help
+ This option enables support for LEDs connected over PMIC8058
+ (Power Management IC) chip on Qualcomm reference boards,
+ for example SURF and FFAs.
+
config LEDS_INTEL_SS4200
tristate "LED driver for Intel NAS SS4200 series"
depends on LEDS_CLASS
@@ -369,6 +416,14 @@
This option enable support for on-chip LED drivers found
on Freescale Semiconductor MC13783 PMIC.
+config LEDS_QCIBL
+ tristate "LED Support for Quanta LCD backlight"
+ depends on SENSORS_WPCE775X && ARCH_MSM_SCORPION
+ default n
+ help
+ Say Y here if you want to use the Quanta backlight driver for ST15
+ platform.
+
config LEDS_NS2
tristate "LED support for Network Space v2 GPIO LEDs"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index cb77b9b..819abc0 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -21,15 +21,18 @@
obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o
obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o
obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o
+obj-$(CONFIG_LEDS_PM8XXX) += leds-pm8xxx.o
obj-$(CONFIG_LEDS_GPIO_REGISTER) += leds-gpio-register.o
obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o
obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o
obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o
obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
+obj-$(CONFIG_LEDS_CPLD) += leds-cpld.o
obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
obj-$(CONFIG_LEDS_FSG) += leds-fsg.o
obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o
+obj-$(CONFIG_LEDS_MSM_PMIC) += leds-msm-pmic.o
obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o
obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o
obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o
@@ -43,6 +46,9 @@
obj-$(CONFIG_LEDS_NS2) += leds-ns2.o
obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o
obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o
+obj-$(CONFIG_LEDS_PMIC8058) += leds-pmic8058.o
+obj-$(CONFIG_LEDS_QCIBL) += leds-qci-backlight.o
+obj-$(CONFIG_LEDS_MSM_PDM) += leds-msm-pdm.o
# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index dc3d3d8..43273fe 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -20,10 +20,51 @@
#include <linux/err.h>
#include <linux/ctype.h>
#include <linux/leds.h>
+#include <linux/slab.h>
#include "leds.h"
static struct class *leds_class;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+
+static void change_brightness(struct work_struct *brightness_change_data)
+{
+ struct deferred_brightness_change *brightness_change = container_of(
+ brightness_change_data,
+ struct deferred_brightness_change,
+ brightness_change_work);
+ struct led_classdev *led_cdev = brightness_change->led_cdev;
+ enum led_brightness value = brightness_change->value;
+
+ led_cdev->brightness_set(led_cdev, value);
+
+ /* Free up memory for the brightness_change structure. */
+ kfree(brightness_change);
+}
+
+int queue_brightness_change(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ /* Initialize the brightness_change_work and its super-struct. */
+ struct deferred_brightness_change *brightness_change =
+ kzalloc(sizeof(struct deferred_brightness_change), GFP_KERNEL);
+
+ if (!brightness_change)
+ return -ENOMEM;
+
+ brightness_change->led_cdev = led_cdev;
+ brightness_change->value = value;
+
+ INIT_WORK(&(brightness_change->brightness_change_work),
+ change_brightness);
+ queue_work(suspend_work_queue,
+ &(brightness_change->brightness_change_work));
+
+ return 0;
+}
+
+#endif
+
static void led_update_brightness(struct led_classdev *led_cdev)
{
if (led_cdev->brightness_get)
@@ -64,6 +105,25 @@
return ret;
}
+static ssize_t led_max_brightness_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ ssize_t ret = -EINVAL;
+ unsigned long state = 0;
+
+ ret = strict_strtoul(buf, 10, &state);
+ if (!ret) {
+ ret = size;
+ if (state > LED_FULL)
+ state = LED_FULL;
+ led_cdev->max_brightness = state;
+ led_set_brightness(led_cdev, led_cdev->brightness);
+ }
+
+ return ret;
+}
+
static ssize_t led_max_brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -74,7 +134,8 @@
static struct device_attribute led_class_attrs[] = {
__ATTR(brightness, 0644, led_brightness_show, led_brightness_store),
- __ATTR(max_brightness, 0444, led_max_brightness_show, NULL),
+ __ATTR(max_brightness, 0644, led_max_brightness_show,
+ led_max_brightness_store),
#ifdef CONFIG_LEDS_TRIGGERS
__ATTR(trigger, 0644, led_trigger_show, led_trigger_store),
#endif
diff --git a/drivers/leds/leds-cpld.c b/drivers/leds/leds-cpld.c
new file mode 100644
index 0000000..eab004c
--- /dev/null
+++ b/drivers/leds/leds-cpld.c
@@ -0,0 +1,405 @@
+/* include/asm/mach-msm/leds-cpld.c
+ *
+ * Copyright (C) 2008 HTC Corporation.
+ *
+ * Author: Farmer Tseng
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/leds.h>
+#include <linux/spinlock.h>
+#include <linux/ctype.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <asm/mach-types.h>
+
+#define DEBUG_LED_CHANGE 0
+
+static int _g_cpld_led_addr;
+
+struct CPLD_LED_data {
+ spinlock_t data_lock;
+ struct led_classdev leds[4]; /* blue, green, red */
+};
+
+static ssize_t led_blink_solid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct CPLD_LED_data *CPLD_LED;
+ int idx = 2;
+ uint8_t reg_val;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ ssize_t ret = 0;
+
+ if (!strcmp(led_cdev->name, "red"))
+ idx = 0;
+ else if (!strcmp(led_cdev->name, "green"))
+ idx = 1;
+ else
+ idx = 2;
+
+ CPLD_LED = container_of(led_cdev, struct CPLD_LED_data, leds[idx]);
+
+ spin_lock(&CPLD_LED->data_lock);
+ reg_val = readb(_g_cpld_led_addr);
+ reg_val = reg_val >> (2 * idx + 1);
+ reg_val &= 0x1;
+ spin_unlock(&CPLD_LED->data_lock);
+
+ /* no lock needed for this */
+ sprintf(buf, "%u\n", reg_val);
+ ret = strlen(buf) + 1;
+
+ return ret;
+}
+
+static ssize_t led_blink_solid_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct CPLD_LED_data *CPLD_LED;
+ int idx = 2;
+ uint8_t reg_val;
+ char *after;
+ unsigned long state;
+ ssize_t ret = -EINVAL;
+ size_t count;
+
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+ if (!strcmp(led_cdev->name, "red"))
+ idx = 0;
+ else if (!strcmp(led_cdev->name, "green"))
+ idx = 1;
+ else
+ idx = 2;
+
+ CPLD_LED = container_of(led_cdev, struct CPLD_LED_data, leds[idx]);
+
+ state = simple_strtoul(buf, &after, 10);
+
+ count = after - buf;
+
+ if (*after && isspace(*after))
+ count++;
+
+ if (count == size) {
+ ret = count;
+ spin_lock(&CPLD_LED->data_lock);
+ reg_val = readb(_g_cpld_led_addr);
+ if (state)
+ reg_val |= 1 << (2 * idx + 1);
+ else
+ reg_val &= ~(1 << (2 * idx + 1));
+
+ writeb(reg_val, _g_cpld_led_addr);
+ spin_unlock(&CPLD_LED->data_lock);
+ }
+
+ return ret;
+}
+
+static DEVICE_ATTR(blink, 0644, led_blink_solid_show, led_blink_solid_store);
+
+static ssize_t cpldled_blink_all_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ uint8_t reg_val;
+ struct CPLD_LED_data *CPLD_LED = dev_get_drvdata(dev);
+ ssize_t ret = 0;
+
+ spin_lock(&CPLD_LED->data_lock);
+ reg_val = readb(_g_cpld_led_addr);
+ reg_val &= 0x2A;
+ if (reg_val == 0x2A)
+ reg_val = 1;
+ else
+ reg_val = 0;
+ spin_unlock(&CPLD_LED->data_lock);
+
+ /* no lock needed for this */
+ sprintf(buf, "%u\n", reg_val);
+ ret = strlen(buf) + 1;
+
+ return ret;
+}
+
+static ssize_t cpldled_blink_all_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ uint8_t reg_val;
+ char *after;
+ unsigned long state;
+ ssize_t ret = -EINVAL;
+ size_t count;
+ struct CPLD_LED_data *CPLD_LED = dev_get_drvdata(dev);
+
+ state = simple_strtoul(buf, &after, 10);
+
+ count = after - buf;
+
+ if (*after && isspace(*after))
+ count++;
+
+ if (count == size) {
+ ret = count;
+ spin_lock(&CPLD_LED->data_lock);
+ reg_val = readb(_g_cpld_led_addr);
+ if (state)
+ reg_val |= 0x2A;
+ else
+ reg_val &= ~0x2A;
+
+ writeb(reg_val, _g_cpld_led_addr);
+ spin_unlock(&CPLD_LED->data_lock);
+ }
+
+ return ret;
+}
+
+static struct device_attribute dev_attr_blink_all = {
+ .attr = {
+ .name = "blink",
+ .mode = 0644,
+ },
+ .show = cpldled_blink_all_show,
+ .store = cpldled_blink_all_store,
+};
+
+static void led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct CPLD_LED_data *CPLD_LED;
+ int idx = 2;
+ struct led_classdev *led;
+ uint8_t reg_val;
+
+ if (!strcmp(led_cdev->name, "jogball-backlight")) {
+ if (brightness > 7)
+ reg_val = 1;
+ else
+ reg_val = brightness;
+ writeb(0, _g_cpld_led_addr + 0x8);
+ writeb(reg_val, _g_cpld_led_addr + 0x8);
+#if DEBUG_LED_CHANGE
+ printk(KERN_INFO "LED change: jogball backlight = %d \n",
+ reg_val);
+#endif
+ return;
+ } else if (!strcmp(led_cdev->name, "red")) {
+ idx = 0;
+ } else if (!strcmp(led_cdev->name, "green")) {
+ idx = 1;
+ } else {
+ idx = 2;
+ }
+
+ CPLD_LED = container_of(led_cdev, struct CPLD_LED_data, leds[idx]);
+ spin_lock(&CPLD_LED->data_lock);
+ reg_val = readb(_g_cpld_led_addr);
+ led = &CPLD_LED->leds[idx];
+
+ if (led->brightness > LED_OFF)
+ reg_val |= 1 << (2 * idx);
+ else
+ reg_val &= ~(1 << (2 * idx));
+
+ writeb(reg_val, _g_cpld_led_addr);
+#if DEBUG_LED_CHANGE
+ printk(KERN_INFO "LED change: %s = %d \n", led_cdev->name, led->brightness);
+#endif
+ spin_unlock(&CPLD_LED->data_lock);
+}
+
+static ssize_t cpldled_grpfreq_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", 0);
+}
+
+static ssize_t cpldled_grpfreq_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return 0;
+}
+
+static DEVICE_ATTR(grpfreq, 0644, cpldled_grpfreq_show, cpldled_grpfreq_store);
+
+static ssize_t cpldled_grppwm_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", 0);
+}
+
+static ssize_t cpldled_grppwm_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return 0;
+}
+
+static DEVICE_ATTR(grppwm, 0644, cpldled_grppwm_show, cpldled_grppwm_store);
+
+static int CPLD_LED_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ int i, j;
+ struct resource *res;
+ struct CPLD_LED_data *CPLD_LED;
+
+ CPLD_LED = kzalloc(sizeof(struct CPLD_LED_data), GFP_KERNEL);
+ if (CPLD_LED == NULL) {
+ printk(KERN_ERR "CPLD_LED_probe: no memory for device\n");
+ ret = -ENOMEM;
+ goto err_alloc_failed;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENOMEM;
+ goto err_alloc_failed;
+ }
+
+ _g_cpld_led_addr = res->start;
+ if (!_g_cpld_led_addr) {
+ ret = -ENOMEM;
+ goto err_alloc_failed;
+ }
+
+ memset(CPLD_LED, 0, sizeof(struct CPLD_LED_data));
+ writeb(0x00, _g_cpld_led_addr);
+
+ CPLD_LED->leds[0].name = "red";
+ CPLD_LED->leds[0].brightness_set = led_brightness_set;
+
+ CPLD_LED->leds[1].name = "green";
+ CPLD_LED->leds[1].brightness_set = led_brightness_set;
+
+ CPLD_LED->leds[2].name = "blue";
+ CPLD_LED->leds[2].brightness_set = led_brightness_set;
+
+ CPLD_LED->leds[3].name = "jogball-backlight";
+ CPLD_LED->leds[3].brightness_set = led_brightness_set;
+
+ spin_lock_init(&CPLD_LED->data_lock);
+
+ for (i = 0; i < 4; i++) { /* red, green, blue jogball */
+ ret = led_classdev_register(&pdev->dev, &CPLD_LED->leds[i]);
+ if (ret) {
+ printk(KERN_ERR
+ "CPLD_LED: led_classdev_register failed\n");
+ goto err_led_classdev_register_failed;
+ }
+ }
+
+ for (i = 0; i < 3; i++) {
+ ret =
+ device_create_file(CPLD_LED->leds[i].dev, &dev_attr_blink);
+ if (ret) {
+ printk(KERN_ERR
+ "CPLD_LED: device_create_file failed\n");
+ goto err_out_attr_blink;
+ }
+ }
+
+ dev_set_drvdata(&pdev->dev, CPLD_LED);
+ ret = device_create_file(&pdev->dev, &dev_attr_blink_all);
+ if (ret) {
+ printk(KERN_ERR
+ "CPLD_LED: create dev_attr_blink_all failed\n");
+ goto err_out_attr_blink;
+ }
+ ret = device_create_file(&pdev->dev, &dev_attr_grppwm);
+ if (ret) {
+ printk(KERN_ERR
+ "CPLD_LED: create dev_attr_grppwm failed\n");
+ goto err_out_attr_grppwm;
+ }
+ ret = device_create_file(&pdev->dev, &dev_attr_grpfreq);
+ if (ret) {
+ printk(KERN_ERR
+ "CPLD_LED: create dev_attr_grpfreq failed\n");
+ goto err_out_attr_grpfreq;
+ }
+
+ return 0;
+
+err_out_attr_grpfreq:
+ device_remove_file(&pdev->dev, &dev_attr_grppwm);
+err_out_attr_grppwm:
+ device_remove_file(&pdev->dev, &dev_attr_blink_all);
+err_out_attr_blink:
+ for (j = 0; j < i; j++)
+ device_remove_file(CPLD_LED->leds[j].dev, &dev_attr_blink);
+ i = 3;
+
+err_led_classdev_register_failed:
+ for (j = 0; j < i; j++)
+ led_classdev_unregister(&CPLD_LED->leds[j]);
+
+err_alloc_failed:
+ kfree(CPLD_LED);
+
+ return ret;
+}
+
+static int __devexit CPLD_LED_remove(struct platform_device *pdev)
+{
+ struct CPLD_LED_data *CPLD_LED;
+ int i;
+
+ CPLD_LED = platform_get_drvdata(pdev);
+
+ for (i = 0; i < 3; i++) {
+ device_remove_file(CPLD_LED->leds[i].dev, &dev_attr_blink);
+ led_classdev_unregister(&CPLD_LED->leds[i]);
+ }
+
+ device_remove_file(&pdev->dev, &dev_attr_blink_all);
+ device_remove_file(&pdev->dev, &dev_attr_grppwm);
+ device_remove_file(&pdev->dev, &dev_attr_grpfreq);
+
+ kfree(CPLD_LED);
+ return 0;
+}
+
+static struct platform_driver CPLD_LED_driver = {
+ .probe = CPLD_LED_probe,
+ .remove = __devexit_p(CPLD_LED_remove),
+ .driver = {
+ .name = "leds-cpld",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init CPLD_LED_init(void)
+{
+ return platform_driver_register(&CPLD_LED_driver);
+}
+
+static void __exit CPLD_LED_exit(void)
+{
+ platform_driver_unregister(&CPLD_LED_driver);
+}
+
+MODULE_AUTHOR("Farmer Tseng");
+MODULE_DESCRIPTION("CPLD_LED driver");
+MODULE_LICENSE("GPL");
+
+module_init(CPLD_LED_init);
+module_exit(CPLD_LED_exit);
diff --git a/drivers/leds/leds-msm-pdm.c b/drivers/leds/leds-msm-pdm.c
new file mode 100644
index 0000000..9509514
--- /dev/null
+++ b/drivers/leds/leds-msm-pdm.c
@@ -0,0 +1,233 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+
+/* Early-suspend level */
+#define LED_SUSPEND_LEVEL 1
+#endif
+
+#define PDM_DUTY_MAXVAL BIT(16)
+#define PDM_DUTY_REFVAL BIT(15)
+
+struct pdm_led_data {
+ struct led_classdev cdev;
+ void __iomem *perph_base;
+ int pdm_offset;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+};
+
+static void msm_led_brightness_set_percent(struct pdm_led_data *led,
+ int duty_per)
+{
+ u16 duty_val;
+
+ duty_val = PDM_DUTY_REFVAL - ((PDM_DUTY_MAXVAL * duty_per) / 100);
+
+ if (!duty_per)
+ duty_val--;
+
+ writel_relaxed(duty_val, led->perph_base + led->pdm_offset);
+}
+
+static void msm_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct pdm_led_data *led =
+ container_of(led_cdev, struct pdm_led_data, cdev);
+
+ msm_led_brightness_set_percent(led, (value * 100) / LED_FULL);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int msm_led_pdm_suspend(struct device *dev)
+{
+ struct pdm_led_data *led = dev_get_drvdata(dev);
+
+ msm_led_brightness_set_percent(led, 0);
+
+ return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void msm_led_pdm_early_suspend(struct early_suspend *h)
+{
+ struct pdm_led_data *led = container_of(h,
+ struct pdm_led_data, early_suspend);
+
+ msm_led_pdm_suspend(led->cdev.dev->parent);
+}
+
+#endif
+
+static const struct dev_pm_ops msm_led_pdm_pm_ops = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = msm_led_pdm_suspend,
+#endif
+};
+#endif
+
+static int __devinit msm_pdm_led_probe(struct platform_device *pdev)
+{
+ const struct led_info *pdata = pdev->dev.platform_data;
+ struct pdm_led_data *led;
+ struct resource *res, *ioregion;
+ u32 tcxo_pdm_ctl;
+ int rc;
+
+ if (!pdata) {
+ pr_err("platform data is invalid\n");
+ return -EINVAL;
+ }
+
+ if (pdev->id > 2) {
+ pr_err("pdm id is invalid\n");
+ return -EINVAL;
+ }
+
+ led = kzalloc(sizeof(struct pdm_led_data), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ /* Enable runtime PM ops, start in ACTIVE mode */
+ rc = pm_runtime_set_active(&pdev->dev);
+ if (rc < 0)
+ dev_dbg(&pdev->dev, "unable to set runtime pm state\n");
+ pm_runtime_enable(&pdev->dev);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ pr_err("get resource failed\n");
+ rc = -EINVAL;
+ goto err_get_res;
+ }
+
+ ioregion = request_mem_region(res->start, resource_size(res),
+ pdev->name);
+ if (!ioregion) {
+ pr_err("request for mem region failed\n");
+ rc = -ENOMEM;
+ goto err_get_res;
+ }
+
+ led->perph_base = ioremap(res->start, resource_size(res));
+ if (!led->perph_base) {
+ pr_err("ioremap failed\n");
+ rc = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ /* Pulse Density Modulation(PDM) ids start with 0 and
+ * every PDM register takes 4 bytes
+ */
+ led->pdm_offset = ((pdev->id) + 1) * 4;
+
+ /* program tcxo_pdm_ctl register to enable pdm*/
+ tcxo_pdm_ctl = readl_relaxed(led->perph_base);
+ tcxo_pdm_ctl |= (1 << pdev->id);
+ writel_relaxed(tcxo_pdm_ctl, led->perph_base);
+
+ /* Start with LED in off state */
+ msm_led_brightness_set_percent(led, 0);
+
+ led->cdev.brightness_set = msm_led_brightness_set;
+ led->cdev.name = pdata->name ? : "leds-msm-pdm";
+
+ rc = led_classdev_register(&pdev->dev, &led->cdev);
+ if (rc) {
+ pr_err("led class registration failed\n");
+ goto err_led_reg;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ led->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
+ LED_SUSPEND_LEVEL;
+ led->early_suspend.suspend = msm_led_pdm_early_suspend;
+ register_early_suspend(&led->early_suspend);
+#endif
+
+ platform_set_drvdata(pdev, led);
+ return 0;
+
+err_led_reg:
+ iounmap(led->perph_base);
+err_ioremap:
+ release_mem_region(res->start, resource_size(res));
+err_get_res:
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ kfree(led);
+ return rc;
+}
+
+static int __devexit msm_pdm_led_remove(struct platform_device *pdev)
+{
+ struct pdm_led_data *led = platform_get_drvdata(pdev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&led->early_suspend);
+#endif
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ led_classdev_unregister(&led->cdev);
+ msm_led_brightness_set_percent(led, 0);
+ iounmap(led->perph_base);
+ release_mem_region(res->start, resource_size(res));
+ kfree(led);
+
+ return 0;
+}
+
+static struct platform_driver msm_pdm_led_driver = {
+ .probe = msm_pdm_led_probe,
+ .remove = __devexit_p(msm_pdm_led_remove),
+ .driver = {
+ .name = "leds-msm-pdm",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM_SLEEP
+ .pm = &msm_led_pdm_pm_ops,
+#endif
+ },
+};
+
+static int __init msm_pdm_led_init(void)
+{
+ return platform_driver_register(&msm_pdm_led_driver);
+}
+module_init(msm_pdm_led_init);
+
+static void __exit msm_pdm_led_exit(void)
+{
+ platform_driver_unregister(&msm_pdm_led_driver);
+}
+module_exit(msm_pdm_led_exit);
+
+MODULE_DESCRIPTION("MSM PDM LEDs driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:leds-msm-pdm");
diff --git a/drivers/leds/leds-msm-pmic.c b/drivers/leds/leds-msm-pmic.c
new file mode 100644
index 0000000..b9c6a53
--- /dev/null
+++ b/drivers/leds/leds-msm-pmic.c
@@ -0,0 +1,105 @@
+/*
+ * leds-msm-pmic.c - MSM PMIC LEDs driver.
+ *
+ * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+
+#include <mach/pmic.h>
+
+#define MAX_KEYPAD_BL_LEVEL 16
+
+static void msm_keypad_bl_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ int ret;
+
+ ret = pmic_set_led_intensity(LED_KEYPAD, value / MAX_KEYPAD_BL_LEVEL);
+ if (ret)
+ dev_err(led_cdev->dev, "can't set keypad backlight\n");
+}
+
+static struct led_classdev msm_kp_bl_led = {
+ .name = "keyboard-backlight",
+ .brightness_set = msm_keypad_bl_led_set,
+ .brightness = LED_OFF,
+};
+
+static int msm_pmic_led_probe(struct platform_device *pdev)
+{
+ int rc;
+
+ rc = led_classdev_register(&pdev->dev, &msm_kp_bl_led);
+ if (rc) {
+ dev_err(&pdev->dev, "unable to register led class driver\n");
+ return rc;
+ }
+ msm_keypad_bl_led_set(&msm_kp_bl_led, LED_OFF);
+ return rc;
+}
+
+static int __devexit msm_pmic_led_remove(struct platform_device *pdev)
+{
+ led_classdev_unregister(&msm_kp_bl_led);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int msm_pmic_led_suspend(struct platform_device *dev,
+ pm_message_t state)
+{
+ led_classdev_suspend(&msm_kp_bl_led);
+
+ return 0;
+}
+
+static int msm_pmic_led_resume(struct platform_device *dev)
+{
+ led_classdev_resume(&msm_kp_bl_led);
+
+ return 0;
+}
+#else
+#define msm_pmic_led_suspend NULL
+#define msm_pmic_led_resume NULL
+#endif
+
+static struct platform_driver msm_pmic_led_driver = {
+ .probe = msm_pmic_led_probe,
+ .remove = __devexit_p(msm_pmic_led_remove),
+ .suspend = msm_pmic_led_suspend,
+ .resume = msm_pmic_led_resume,
+ .driver = {
+ .name = "pmic-leds",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_pmic_led_init(void)
+{
+ return platform_driver_register(&msm_pmic_led_driver);
+}
+module_init(msm_pmic_led_init);
+
+static void __exit msm_pmic_led_exit(void)
+{
+ platform_driver_unregister(&msm_pmic_led_driver);
+}
+module_exit(msm_pmic_led_exit);
+
+MODULE_DESCRIPTION("MSM PMIC LEDs driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:pmic-leds");
diff --git a/drivers/leds/leds-pm8xxx.c b/drivers/leds/leds-pm8xxx.c
new file mode 100644
index 0000000..c34bf30
--- /dev/null
+++ b/drivers/leds/leds-pm8xxx.c
@@ -0,0 +1,339 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/workqueue.h>
+
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/leds-pm8xxx.h>
+
+#define SSBI_REG_ADDR_DRV_KEYPAD 0x48
+#define PM8XXX_DRV_KEYPAD_BL_MASK 0xf0
+#define PM8XXX_DRV_KEYPAD_BL_SHIFT 0x04
+
+#define SSBI_REG_ADDR_FLASH_DRV0 0x49
+#define PM8XXX_DRV_FLASH_MASK 0xf0
+#define PM8XXX_DRV_FLASH_SHIFT 0x04
+
+#define SSBI_REG_ADDR_FLASH_DRV1 0xFB
+
+#define SSBI_REG_ADDR_LED_CTRL_BASE 0x131
+#define SSBI_REG_ADDR_LED_CTRL(n) (SSBI_REG_ADDR_LED_CTRL_BASE + (n))
+#define PM8XXX_DRV_LED_CTRL_MASK 0xf8
+#define PM8XXX_DRV_LED_CTRL_SHIFT 0x03
+
+#define MAX_FLASH_LED_CURRENT 300
+#define MAX_LC_LED_CURRENT 40
+#define MAX_KP_BL_LED_CURRENT 300
+
+#define MAX_KEYPAD_BL_LEVEL (1 << 4)
+#define MAX_LED_DRV_LEVEL 20 /* 2 * 20 mA */
+
+#define PM8XXX_LED_OFFSET(id) ((id) - PM8XXX_ID_LED_0)
+
+#define MAX_KB_LED_BRIGHTNESS 15
+#define MAX_LC_LED_BRIGHTNESS 20
+#define MAX_FLASH_LED_BRIGHTNESS 15
+
+/**
+ * struct pm8xxx_led_data - internal led data structure
+ * @led_classdev - led class device
+ * @id - led index
+ * @led_brightness - led brightness levels
+ * @work - workqueue for led
+ * @lock - to protect the transactions
+ * @reg - cached value of led register
+ */
+struct pm8xxx_led_data {
+ struct led_classdev cdev;
+ int id;
+ u8 reg;
+ enum led_brightness brightness;
+ struct device *dev;
+ struct work_struct work;
+ struct mutex lock;
+};
+
+static void led_kp_set(struct pm8xxx_led_data *led, enum led_brightness value)
+{
+ int rc;
+ u8 level;
+
+ level = (value << PM8XXX_DRV_KEYPAD_BL_SHIFT) &
+ PM8XXX_DRV_KEYPAD_BL_MASK;
+
+ led->reg &= ~PM8XXX_DRV_KEYPAD_BL_MASK;
+ led->reg |= level;
+
+ rc = pm8xxx_writeb(led->dev->parent, SSBI_REG_ADDR_DRV_KEYPAD,
+ led->reg);
+ if (rc < 0)
+ dev_err(led->cdev.dev,
+ "can't set keypad backlight level rc=%d\n", rc);
+}
+
+static void led_lc_set(struct pm8xxx_led_data *led, enum led_brightness value)
+{
+ int rc, offset;
+ u8 level;
+
+ level = (value << PM8XXX_DRV_LED_CTRL_SHIFT) &
+ PM8XXX_DRV_LED_CTRL_MASK;
+
+ offset = PM8XXX_LED_OFFSET(led->id);
+
+ led->reg &= ~PM8XXX_DRV_LED_CTRL_MASK;
+ led->reg |= level;
+
+ rc = pm8xxx_writeb(led->dev->parent, SSBI_REG_ADDR_LED_CTRL(offset),
+ led->reg);
+ if (rc)
+ dev_err(led->cdev.dev, "can't set (%d) led value rc=%d\n",
+ led->id, rc);
+}
+
+static void
+led_flash_set(struct pm8xxx_led_data *led, enum led_brightness value)
+{
+ int rc;
+ u8 level;
+ u16 reg_addr;
+
+ level = (value << PM8XXX_DRV_FLASH_SHIFT) &
+ PM8XXX_DRV_FLASH_MASK;
+
+ led->reg &= ~PM8XXX_DRV_FLASH_MASK;
+ led->reg |= level;
+
+ if (led->id == PM8XXX_ID_FLASH_LED_0)
+ reg_addr = SSBI_REG_ADDR_FLASH_DRV0;
+ else
+ reg_addr = SSBI_REG_ADDR_FLASH_DRV1;
+
+ rc = pm8xxx_writeb(led->dev->parent, reg_addr, led->reg);
+ if (rc < 0)
+ dev_err(led->cdev.dev, "can't set flash led%d level rc=%d\n",
+ led->id, rc);
+}
+
+static void pm8xxx_led_work(struct work_struct *work)
+{
+ struct pm8xxx_led_data *led = container_of(work,
+ struct pm8xxx_led_data, work);
+
+ mutex_lock(&led->lock);
+
+ switch (led->id) {
+ case PM8XXX_ID_LED_KB_LIGHT:
+ led_kp_set(led, led->brightness);
+ break;
+ case PM8XXX_ID_LED_0:
+ case PM8XXX_ID_LED_1:
+ case PM8XXX_ID_LED_2:
+ led_lc_set(led, led->brightness);
+ break;
+ case PM8XXX_ID_FLASH_LED_0:
+ case PM8XXX_ID_FLASH_LED_1:
+ led_flash_set(led, led->brightness);
+ break;
+ }
+
+ mutex_unlock(&led->lock);
+}
+
+static void pm8xxx_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct pm8xxx_led_data *led;
+
+ led = container_of(led_cdev, struct pm8xxx_led_data, cdev);
+
+ led->brightness = value;
+ schedule_work(&led->work);
+}
+
+static enum led_brightness pm8xxx_led_get(struct led_classdev *led_cdev)
+{
+ struct pm8xxx_led_data *led;
+
+ led = container_of(led_cdev, struct pm8xxx_led_data, cdev);
+
+ return led->brightness;
+}
+
+static int __devinit get_max_brightness(enum pm8xxx_leds id)
+{
+ switch (id) {
+ case PM8XXX_ID_LED_KB_LIGHT:
+ return MAX_KB_LED_BRIGHTNESS;
+ case PM8XXX_ID_LED_0:
+ case PM8XXX_ID_LED_1:
+ case PM8XXX_ID_LED_2:
+ return MAX_LC_LED_BRIGHTNESS;
+ case PM8XXX_ID_FLASH_LED_0:
+ case PM8XXX_ID_FLASH_LED_1:
+ return MAX_FLASH_LED_CURRENT;
+ default:
+ return 0;
+ }
+}
+
+static int __devinit get_init_value(struct pm8xxx_led_data *led, u8 *val)
+{
+ int rc, offset;
+ u16 addr;
+
+ switch (led->id) {
+ case PM8XXX_ID_LED_KB_LIGHT:
+ addr = SSBI_REG_ADDR_DRV_KEYPAD;
+ break;
+ case PM8XXX_ID_LED_0:
+ case PM8XXX_ID_LED_1:
+ case PM8XXX_ID_LED_2:
+ offset = PM8XXX_LED_OFFSET(led->id);
+ addr = SSBI_REG_ADDR_LED_CTRL(offset);
+ break;
+ case PM8XXX_ID_FLASH_LED_0:
+ addr = SSBI_REG_ADDR_FLASH_DRV0;
+ break;
+ case PM8XXX_ID_FLASH_LED_1:
+ addr = SSBI_REG_ADDR_FLASH_DRV1;
+ break;
+ }
+
+ rc = pm8xxx_readb(led->dev->parent, addr, val);
+ if (rc)
+ dev_err(led->cdev.dev, "can't get led(%d) level rc=%d\n",
+ led->id, rc);
+
+ return rc;
+}
+
+static int __devinit pm8xxx_led_probe(struct platform_device *pdev)
+{
+ const struct led_platform_data *pdata = pdev->dev.platform_data;
+ struct led_info *curr_led;
+ struct pm8xxx_led_data *led, *led_dat;
+ int rc, i;
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "platform data not supplied\n");
+ return -EINVAL;
+ }
+
+ led = kcalloc(pdata->num_leds, sizeof(*led), GFP_KERNEL);
+ if (led == NULL) {
+ dev_err(&pdev->dev, "failed to alloc memory\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < pdata->num_leds; i++) {
+ curr_led = &pdata->leds[i];
+ led_dat = &led[i];
+ /* the flags variable is used for led-id */
+ led_dat->id = curr_led->flags;
+
+ if (!((led_dat->id >= PM8XXX_ID_LED_KB_LIGHT) &&
+ (led_dat->id <= PM8XXX_ID_FLASH_LED_1))) {
+ dev_err(&pdev->dev, "invalid LED ID (%d) specified\n",
+ led_dat->id);
+ rc = -EINVAL;
+ goto fail_id_check;
+ }
+
+ led_dat->cdev.name = curr_led->name;
+ led_dat->cdev.default_trigger = curr_led->default_trigger;
+ led_dat->cdev.brightness_set = pm8xxx_led_set;
+ led_dat->cdev.brightness_get = pm8xxx_led_get;
+ led_dat->cdev.brightness = LED_OFF;
+ led_dat->cdev.flags = LED_CORE_SUSPENDRESUME;
+
+ led_dat->cdev.max_brightness = get_max_brightness(led_dat->id);
+ led_dat->dev = &pdev->dev;
+
+ rc = get_init_value(led_dat, &led_dat->reg);
+ if (rc < 0)
+ goto fail_id_check;
+
+ mutex_init(&led_dat->lock);
+ INIT_WORK(&led_dat->work, pm8xxx_led_work);
+
+ rc = led_classdev_register(&pdev->dev, &led_dat->cdev);
+ if (rc) {
+ dev_err(&pdev->dev, "unable to register led %d,rc=%d\n",
+ led_dat->id, rc);
+ goto fail_id_check;
+ }
+ }
+
+ platform_set_drvdata(pdev, led);
+
+ return 0;
+
+fail_id_check:
+ if (i > 0) {
+ for (i = i - 1; i >= 0; i--) {
+ mutex_destroy(&led[i].lock);
+ led_classdev_unregister(&led[i].cdev);
+ }
+ }
+ kfree(led);
+ return rc;
+}
+
+static int __devexit pm8xxx_led_remove(struct platform_device *pdev)
+{
+ int i;
+ const struct led_platform_data *pdata =
+ pdev->dev.platform_data;
+ struct pm8xxx_led_data *led = platform_get_drvdata(pdev);
+
+ for (i = 0; i < pdata->num_leds; i++) {
+ cancel_work_sync(&led[i].work);
+ mutex_destroy(&led[i].lock);
+ led_classdev_unregister(&led[i].cdev);
+ }
+
+ kfree(led);
+
+ return 0;
+}
+
+static struct platform_driver pm8xxx_led_driver = {
+ .probe = pm8xxx_led_probe,
+ .remove = __devexit_p(pm8xxx_led_remove),
+ .driver = {
+ .name = PM8XXX_LEDS_DEV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pm8xxx_led_init(void)
+{
+ return platform_driver_register(&pm8xxx_led_driver);
+}
+module_init(pm8xxx_led_init);
+
+static void __exit pm8xxx_led_exit(void)
+{
+ platform_driver_unregister(&pm8xxx_led_driver);
+}
+module_exit(pm8xxx_led_exit);
+
+MODULE_DESCRIPTION("PM8XXX LEDs driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pm8xxx-led");
diff --git a/drivers/leds/leds-pmic8058.c b/drivers/leds/leds-pmic8058.c
new file mode 100644
index 0000000..d1aed3f
--- /dev/null
+++ b/drivers/leds/leds-pmic8058.c
@@ -0,0 +1,434 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/leds-pmic8058.h>
+
+#define SSBI_REG_ADDR_DRV_KEYPAD 0x48
+#define PM8058_DRV_KEYPAD_BL_MASK 0xf0
+#define PM8058_DRV_KEYPAD_BL_SHIFT 0x04
+
+#define SSBI_REG_ADDR_FLASH_DRV0 0x49
+#define PM8058_DRV_FLASH_MASK 0xf0
+#define PM8058_DRV_FLASH_SHIFT 0x04
+
+#define SSBI_REG_ADDR_FLASH_DRV1 0xFB
+
+#define SSBI_REG_ADDR_LED_CTRL_BASE 0x131
+#define SSBI_REG_ADDR_LED_CTRL(n) (SSBI_REG_ADDR_LED_CTRL_BASE + (n))
+#define PM8058_DRV_LED_CTRL_MASK 0xf8
+#define PM8058_DRV_LED_CTRL_SHIFT 0x03
+
+#define MAX_FLASH_CURRENT 300
+#define MAX_KEYPAD_CURRENT 300
+#define MAX_KEYPAD_BL_LEVEL (1 << 4)
+#define MAX_LED_DRV_LEVEL 20 /* 2 * 20 mA */
+
+#define PMIC8058_LED_OFFSET(id) ((id) - PMIC8058_ID_LED_0)
+
+struct pmic8058_led_data {
+ struct led_classdev cdev;
+ int id;
+ enum led_brightness brightness;
+ u8 flags;
+ struct pm8058_chip *pm_chip;
+ struct work_struct work;
+ struct mutex lock;
+ spinlock_t value_lock;
+ u8 reg_kp;
+ u8 reg_led_ctrl[3];
+ u8 reg_flash_led0;
+ u8 reg_flash_led1;
+};
+
+#define PM8058_MAX_LEDS 7
+static struct pmic8058_led_data led_data[PM8058_MAX_LEDS];
+
+static void kp_bl_set(struct pmic8058_led_data *led, enum led_brightness value)
+{
+ int rc;
+ u8 level;
+ unsigned long flags;
+
+ spin_lock_irqsave(&led->value_lock, flags);
+ level = (value << PM8058_DRV_KEYPAD_BL_SHIFT) &
+ PM8058_DRV_KEYPAD_BL_MASK;
+
+ led->reg_kp &= ~PM8058_DRV_KEYPAD_BL_MASK;
+ led->reg_kp |= level;
+ spin_unlock_irqrestore(&led->value_lock, flags);
+
+ rc = pm8058_write(led->pm_chip, SSBI_REG_ADDR_DRV_KEYPAD,
+ &led->reg_kp, 1);
+ if (rc)
+ pr_err("%s: can't set keypad backlight level\n", __func__);
+}
+
+static enum led_brightness kp_bl_get(struct pmic8058_led_data *led)
+{
+ if ((led->reg_kp & PM8058_DRV_KEYPAD_BL_MASK) >>
+ PM8058_DRV_KEYPAD_BL_SHIFT)
+ return LED_FULL;
+ else
+ return LED_OFF;
+}
+
+static void led_lc_set(struct pmic8058_led_data *led, enum led_brightness value)
+{
+ unsigned long flags;
+ int rc, offset;
+ u8 level, tmp;
+
+ spin_lock_irqsave(&led->value_lock, flags);
+
+ level = (led->brightness << PM8058_DRV_LED_CTRL_SHIFT) &
+ PM8058_DRV_LED_CTRL_MASK;
+
+ offset = PMIC8058_LED_OFFSET(led->id);
+ tmp = led->reg_led_ctrl[offset];
+
+ tmp &= ~PM8058_DRV_LED_CTRL_MASK;
+ tmp |= level;
+ spin_unlock_irqrestore(&led->value_lock, flags);
+
+ rc = pm8058_write(led->pm_chip, SSBI_REG_ADDR_LED_CTRL(offset),
+ &tmp, 1);
+ if (rc) {
+ dev_err(led->cdev.dev, "can't set (%d) led value\n",
+ led->id);
+ return;
+ }
+
+ spin_lock_irqsave(&led->value_lock, flags);
+ led->reg_led_ctrl[offset] = tmp;
+ spin_unlock_irqrestore(&led->value_lock, flags);
+}
+
+static enum led_brightness led_lc_get(struct pmic8058_led_data *led)
+{
+ int offset;
+ u8 value;
+
+ offset = PMIC8058_LED_OFFSET(led->id);
+ value = led->reg_led_ctrl[offset];
+
+ if ((value & PM8058_DRV_LED_CTRL_MASK) >>
+ PM8058_DRV_LED_CTRL_SHIFT)
+ return LED_FULL;
+ else
+ return LED_OFF;
+}
+
+static void
+led_flash_set(struct pmic8058_led_data *led, enum led_brightness value)
+{
+ int rc;
+ u8 level;
+ unsigned long flags;
+ u8 reg_flash_led;
+ u16 reg_addr;
+
+ spin_lock_irqsave(&led->value_lock, flags);
+ level = (value << PM8058_DRV_FLASH_SHIFT) &
+ PM8058_DRV_FLASH_MASK;
+
+ if (led->id == PMIC8058_ID_FLASH_LED_0) {
+ led->reg_flash_led0 &= ~PM8058_DRV_FLASH_MASK;
+ led->reg_flash_led0 |= level;
+ reg_flash_led = led->reg_flash_led0;
+ reg_addr = SSBI_REG_ADDR_FLASH_DRV0;
+ } else {
+ led->reg_flash_led1 &= ~PM8058_DRV_FLASH_MASK;
+ led->reg_flash_led1 |= level;
+ reg_flash_led = led->reg_flash_led1;
+ reg_addr = SSBI_REG_ADDR_FLASH_DRV1;
+ }
+ spin_unlock_irqrestore(&led->value_lock, flags);
+
+ rc = pm8058_write(led->pm_chip, reg_addr, ®_flash_led, 1);
+ if (rc)
+ pr_err("%s: can't set flash led%d level %d\n", __func__,
+ led->id, rc);
+}
+
+int pm8058_set_flash_led_current(enum pmic8058_leds id, unsigned mA)
+{
+ struct pmic8058_led_data *led;
+
+ if ((id < PMIC8058_ID_FLASH_LED_0) || (id > PMIC8058_ID_FLASH_LED_1)) {
+ pr_err("%s: invalid LED ID (%d) specified\n", __func__, id);
+ return -EINVAL;
+ }
+
+ led = &led_data[id];
+ if (!led) {
+ pr_err("%s: flash led not available\n", __func__);
+ return -EINVAL;
+ }
+
+ if (mA > MAX_FLASH_CURRENT)
+ return -EINVAL;
+
+ led_flash_set(led, mA / 20);
+
+ return 0;
+}
+EXPORT_SYMBOL(pm8058_set_flash_led_current);
+
+int pm8058_set_led_current(enum pmic8058_leds id, unsigned mA)
+{
+ struct pmic8058_led_data *led;
+ int brightness = 0;
+
+ if ((id < PMIC8058_ID_LED_KB_LIGHT) || (id > PMIC8058_ID_FLASH_LED_1)) {
+ pr_err("%s: invalid LED ID (%d) specified\n", __func__, id);
+ return -EINVAL;
+ }
+
+ led = &led_data[id];
+ if (!led) {
+ pr_err("%s: flash led not available\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (id) {
+ case PMIC8058_ID_LED_0:
+ case PMIC8058_ID_LED_1:
+ case PMIC8058_ID_LED_2:
+ brightness = mA / 2;
+ if (brightness > led->cdev.max_brightness)
+ return -EINVAL;
+ led_lc_set(led, brightness);
+ break;
+
+ case PMIC8058_ID_LED_KB_LIGHT:
+ case PMIC8058_ID_FLASH_LED_0:
+ case PMIC8058_ID_FLASH_LED_1:
+ brightness = mA / 20;
+ if (brightness > led->cdev.max_brightness)
+ return -EINVAL;
+ if (id == PMIC8058_ID_LED_KB_LIGHT)
+ kp_bl_set(led, brightness);
+ else
+ led_flash_set(led, brightness);
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(pm8058_set_led_current);
+
+static void pmic8058_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct pmic8058_led_data *led;
+ unsigned long flags;
+
+ led = container_of(led_cdev, struct pmic8058_led_data, cdev);
+
+ spin_lock_irqsave(&led->value_lock, flags);
+ led->brightness = value;
+ schedule_work(&led->work);
+ spin_unlock_irqrestore(&led->value_lock, flags);
+}
+
+static void pmic8058_led_work(struct work_struct *work)
+{
+ struct pmic8058_led_data *led = container_of(work,
+ struct pmic8058_led_data, work);
+
+ mutex_lock(&led->lock);
+
+ switch (led->id) {
+ case PMIC8058_ID_LED_KB_LIGHT:
+ kp_bl_set(led, led->brightness);
+ break;
+ case PMIC8058_ID_LED_0:
+ case PMIC8058_ID_LED_1:
+ case PMIC8058_ID_LED_2:
+ led_lc_set(led, led->brightness);
+ break;
+ case PMIC8058_ID_FLASH_LED_0:
+ case PMIC8058_ID_FLASH_LED_1:
+ led_flash_set(led, led->brightness);
+ break;
+ }
+
+ mutex_unlock(&led->lock);
+}
+
+static enum led_brightness pmic8058_led_get(struct led_classdev *led_cdev)
+{
+ struct pmic8058_led_data *led;
+
+ led = container_of(led_cdev, struct pmic8058_led_data, cdev);
+
+ switch (led->id) {
+ case PMIC8058_ID_LED_KB_LIGHT:
+ return kp_bl_get(led);
+ case PMIC8058_ID_LED_0:
+ case PMIC8058_ID_LED_1:
+ case PMIC8058_ID_LED_2:
+ return led_lc_get(led);
+ }
+ return LED_OFF;
+}
+
+static int pmic8058_led_probe(struct platform_device *pdev)
+{
+ struct pmic8058_leds_platform_data *pdata = pdev->dev.platform_data;
+ struct pmic8058_led_data *led_dat;
+ struct pmic8058_led *curr_led;
+ int rc, i = 0;
+ struct pm8058_chip *pm_chip;
+ u8 reg_kp;
+ u8 reg_led_ctrl[3];
+ u8 reg_flash_led0;
+ u8 reg_flash_led1;
+
+ pm_chip = dev_get_drvdata(pdev->dev.parent);
+ if (pm_chip == NULL) {
+ dev_err(&pdev->dev, "no parent data passed in\n");
+ return -EFAULT;
+ }
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "platform data not supplied\n");
+ return -EINVAL;
+ }
+
+ rc = pm8058_read(pm_chip, SSBI_REG_ADDR_DRV_KEYPAD, ®_kp,
+ 1);
+ if (rc) {
+ dev_err(&pdev->dev, "can't get keypad backlight level\n");
+ goto err_reg_read;
+ }
+
+ rc = pm8058_read(pm_chip, SSBI_REG_ADDR_LED_CTRL_BASE,
+ reg_led_ctrl, 3);
+ if (rc) {
+ dev_err(&pdev->dev, "can't get led levels\n");
+ goto err_reg_read;
+ }
+
+ rc = pm8058_read(pm_chip, SSBI_REG_ADDR_FLASH_DRV0,
+ ®_flash_led0, 1);
+ if (rc) {
+ dev_err(&pdev->dev, "can't read flash led0\n");
+ goto err_reg_read;
+ }
+
+ rc = pm8058_read(pm_chip, SSBI_REG_ADDR_FLASH_DRV1,
+ ®_flash_led1, 1);
+ if (rc) {
+ dev_err(&pdev->dev, "can't get flash led1\n");
+ goto err_reg_read;
+ }
+
+ for (i = 0; i < pdata->num_leds; i++) {
+ curr_led = &pdata->leds[i];
+ led_dat = &led_data[curr_led->id];
+
+ led_dat->cdev.name = curr_led->name;
+ led_dat->cdev.default_trigger = curr_led->default_trigger;
+ led_dat->cdev.brightness_set = pmic8058_led_set;
+ led_dat->cdev.brightness_get = pmic8058_led_get;
+ led_dat->cdev.brightness = LED_OFF;
+ led_dat->cdev.max_brightness = curr_led->max_brightness;
+ led_dat->cdev.flags = LED_CORE_SUSPENDRESUME;
+
+ led_dat->id = curr_led->id;
+ led_dat->reg_kp = reg_kp;
+ memcpy(led_data->reg_led_ctrl, reg_led_ctrl,
+ sizeof(reg_led_ctrl));
+ led_dat->reg_flash_led0 = reg_flash_led0;
+ led_dat->reg_flash_led1 = reg_flash_led1;
+
+ if (!((led_dat->id >= PMIC8058_ID_LED_KB_LIGHT) &&
+ (led_dat->id <= PMIC8058_ID_FLASH_LED_1))) {
+ dev_err(&pdev->dev, "invalid LED ID (%d) specified\n",
+ led_dat->id);
+ rc = -EINVAL;
+ goto fail_id_check;
+ }
+
+ led_dat->pm_chip = pm_chip;
+
+ mutex_init(&led_dat->lock);
+ spin_lock_init(&led_dat->value_lock);
+ INIT_WORK(&led_dat->work, pmic8058_led_work);
+
+ rc = led_classdev_register(&pdev->dev, &led_dat->cdev);
+ if (rc) {
+ dev_err(&pdev->dev, "unable to register led %d\n",
+ led_dat->id);
+ goto fail_id_check;
+ }
+ }
+
+ platform_set_drvdata(pdev, led_data);
+
+ return 0;
+
+err_reg_read:
+fail_id_check:
+ if (i > 0) {
+ for (i = i - 1; i >= 0; i--)
+ led_classdev_unregister(&led_data[i].cdev);
+ }
+ return rc;
+}
+
+static int __devexit pmic8058_led_remove(struct platform_device *pdev)
+{
+ int i;
+ struct pmic8058_leds_platform_data *pdata = pdev->dev.platform_data;
+ struct pmic8058_led_data *led = platform_get_drvdata(pdev);
+
+ for (i = 0; i < pdata->num_leds; i++) {
+ led_classdev_unregister(&led[led->id].cdev);
+ cancel_work_sync(&led[led->id].work);
+ }
+
+ return 0;
+}
+
+static struct platform_driver pmic8058_led_driver = {
+ .probe = pmic8058_led_probe,
+ .remove = __devexit_p(pmic8058_led_remove),
+ .driver = {
+ .name = "pm8058-led",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pmic8058_led_init(void)
+{
+ return platform_driver_register(&pmic8058_led_driver);
+}
+module_init(pmic8058_led_init);
+
+static void __exit pmic8058_led_exit(void)
+{
+ platform_driver_unregister(&pmic8058_led_driver);
+}
+module_exit(pmic8058_led_exit);
+
+MODULE_DESCRIPTION("PMIC8058 LEDs driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pmic8058-led");
diff --git a/drivers/leds/leds-qci-backlight.c b/drivers/leds/leds-qci-backlight.c
new file mode 100644
index 0000000..67502e8
--- /dev/null
+++ b/drivers/leds/leds-qci-backlight.c
@@ -0,0 +1,71 @@
+/* Quanta I2C Backlight Driver
+ *
+ * Copyright (C) 2009 Quanta Computer Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*
+ *
+ * The Driver with I/O communications via the I2C Interface for ST15 platform.
+ * And it is only working on the nuvoTon WPCE775x Embedded Controller.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/leds.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/wpce775x.h>
+
+#define EC_CMD_SET_BACKLIGHT 0xB1
+
+static void qci_backlight_store(struct led_classdev *led_cdev,
+ enum led_brightness val);
+
+static struct platform_device *bl_pdev;
+static struct led_classdev lcd_backlight = {
+ .name = "lcd-backlight",
+ .brightness = 147,
+ .brightness_set = qci_backlight_store,
+};
+
+static void qci_backlight_store(struct led_classdev *led_cdev,
+ enum led_brightness val)
+{
+ u16 value = val;
+ wpce_smbus_write_word_data(EC_CMD_SET_BACKLIGHT, value);
+ msleep(10);
+
+ dev_dbg(&bl_pdev->dev, "[backlight_store] : value = %d\n", value);
+}
+
+static int __init qci_backlight_init(void)
+{
+ int err = 0;
+ bl_pdev = platform_device_register_simple("backlight", 0, NULL, 0);
+ err = led_classdev_register(&bl_pdev->dev, &lcd_backlight);
+ return err;
+}
+
+static void __exit qci_backlight_exit(void)
+{
+ led_classdev_unregister(&lcd_backlight);
+ platform_device_unregister(bl_pdev);
+}
+
+module_init(qci_backlight_init);
+module_exit(qci_backlight_exit);
+
+MODULE_AUTHOR("Quanta Computer Inc.");
+MODULE_DESCRIPTION("Quanta Embedded Controller I2C Backlight Driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h
index e77c7f8..593a63c 100644
--- a/drivers/leds/leds.h
+++ b/drivers/leds/leds.h
@@ -16,6 +16,21 @@
#include <linux/device.h>
#include <linux/rwsem.h>
#include <linux/leds.h>
+#include <linux/workqueue.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+
+extern struct workqueue_struct *suspend_work_queue;
+extern int queue_brightness_change(struct led_classdev *led_cdev,
+ enum led_brightness value);
+
+struct deferred_brightness_change {
+ struct work_struct brightness_change_work;
+ struct led_classdev *led_cdev;
+ enum led_brightness value;
+};
+
+#endif
static inline void led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness value)
@@ -23,8 +38,12 @@
if (value > led_cdev->max_brightness)
value = led_cdev->max_brightness;
led_cdev->brightness = value;
- if (!(led_cdev->flags & LED_SUSPENDED))
- led_cdev->brightness_set(led_cdev, value);
+ if (!(led_cdev->flags & LED_SUSPENDED)) {
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ if (queue_brightness_change(led_cdev, value) != 0)
+#endif
+ led_cdev->brightness_set(led_cdev, value);
+ }
}
static inline int led_get_brightness(struct led_classdev *led_cdev)