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, &reg_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, &reg_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,
+			&reg_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,
+			&reg_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)