leds: tps61310_flashlight: Add TI TPS61310 flashlight driver

HTC kernel version: villeu-jb-crc-3.4.10-ae8b65e

Change-Id: I53b57e105643a7ace9f73d36261e2b7b5f9dcd7a
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 26e8496..19b18ac 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -585,4 +585,11 @@
 comment "iptables trigger is under Netfilter config (LED target)"
 	depends on LEDS_TRIGGERS
 
+comment "LED Flashlights"
+
+config FLASHLIGHT_TPS61310
+	tristate "TI TPS61310 flashlight"
+	help
+	  Say Y here to enable the TI TPS61310 flashlight.
+
 endif # NEW_LEDS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 3fe422e..430e0b0 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -70,3 +70,6 @@
 obj-$(CONFIG_LEDS_TRIGGER_GPIO)		+= ledtrig-gpio.o
 obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON)	+= ledtrig-default-on.o
 obj-$(CONFIG_LEDS_TRIGGER_SLEEP)	+= ledtrig-sleep.o
+
+# Flashlight Drivers
+obj-$(CONFIG_FLASHLIGHT_TPS61310)	+= tps61310_flashlight.o
diff --git a/drivers/leds/tps61310_flashlight.c b/drivers/leds/tps61310_flashlight.c
new file mode 100644
index 0000000..4e6b40a
--- /dev/null
+++ b/drivers/leds/tps61310_flashlight.c
@@ -0,0 +1,604 @@
+/* drivers/i2c/chips/tps61310_flashlight.c
+ *
+ * Copyright (C) 2008-2009 HTC Corporation.
+ *
+ * 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 <mach/msm_iomap.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/earlysuspend.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/leds.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/htc_flashlight.h>
+#include <linux/module.h>
+
+#define FLT_DBG_LOG(fmt, ...) \
+		printk(KERN_DEBUG "[FLT]TPS " fmt, ##__VA_ARGS__)
+#define FLT_INFO_LOG(fmt, ...) \
+		printk(KERN_INFO "[FLT]TPS " fmt, ##__VA_ARGS__)
+#define FLT_ERR_LOG(fmt, ...) \
+		printk(KERN_ERR "[FLT][ERR]TPS " fmt, ##__VA_ARGS__)
+
+#define TPS61310_RETRY_COUNT 10
+
+struct tps61310_data {
+	struct led_classdev 		fl_lcdev;
+	struct early_suspend		fl_early_suspend;
+	enum flashlight_mode_flags 	mode_status;
+	uint32_t			flash_sw_timeout;
+	struct mutex 			tps61310_data_mutex;
+	uint32_t			strb0;
+	uint32_t			strb1;
+	uint8_t 			led_count;
+	uint8_t 			mode_pin_suspend_state_low;
+};
+
+static struct i2c_client *this_client;
+static struct tps61310_data *this_tps61310;
+struct delayed_work tps61310_delayed_work;
+static struct workqueue_struct *tps61310_work_queue;
+static struct mutex tps61310_mutex;
+
+static int switch_state = 1;
+
+static ssize_t switch_show(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	return sprintf(buf, "switch status:%d \n", switch_state);
+}
+
+static ssize_t switch_store(
+		struct device *dev, struct device_attribute *attr,
+		const char *buf, size_t size)
+{
+	int switch_status;
+	switch_status = -1;
+	sscanf(buf, "%d ",&switch_status);
+	FLT_INFO_LOG("%s: %d\n",__func__,switch_status);
+	switch_state = switch_status;
+	return size;
+}
+
+static DEVICE_ATTR(function_switch, S_IRUGO | S_IWUSR, switch_show, switch_store);
+
+static int TPS61310_I2C_TxData(char *txData, int length)
+{
+	uint8_t loop_i;
+	struct i2c_msg msg[] = {
+		{
+		 .addr = this_client->addr,
+		 .flags = 0,
+		 .len = length,
+		 .buf = txData,
+		 },
+	};
+
+	for (loop_i = 0; loop_i < TPS61310_RETRY_COUNT; loop_i++) {
+		if (i2c_transfer(this_client->adapter, msg, 1) > 0)
+			break;
+
+		mdelay(10);
+	}
+
+	if (loop_i >= TPS61310_RETRY_COUNT) {
+		FLT_ERR_LOG("%s retry over %d\n", __func__,
+							TPS61310_RETRY_COUNT);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int tps61310_i2c_command(uint8_t address, uint8_t data)
+{
+	uint8_t buffer[2];
+	int ret;
+
+	buffer[0] = address;
+	buffer[1] = data;
+	ret = TPS61310_I2C_TxData(buffer, 2);
+	if (ret < 0) {
+		FLT_ERR_LOG("%s error\n", __func__);
+		return ret;
+	}
+	return 0;
+}
+
+static int flashlight_turn_off(void)
+{
+	FLT_INFO_LOG("%s\n", __func__);
+	gpio_set_value_cansleep(this_tps61310->strb0, 0);
+	gpio_set_value_cansleep(this_tps61310->strb1, 1);
+	tps61310_i2c_command(0x01, 0x00);
+	this_tps61310->mode_status = FL_MODE_OFF;
+	return 0;
+}
+
+int tps61310_flashlight_control(int mode)
+{
+	int ret = 0;
+
+	mutex_lock(&tps61310_mutex);
+	if (this_tps61310->led_count == 1) {
+	switch (mode) {
+	case FL_MODE_OFF:
+		flashlight_turn_off();
+	break;
+	case FL_MODE_FLASH:
+		tps61310_i2c_command(0x05, 0x6A);
+		tps61310_i2c_command(0x00, 0x00);
+		tps61310_i2c_command(0x01, 0x9E);
+		gpio_set_value_cansleep(this_tps61310->strb1, 0);
+		gpio_set_value_cansleep(this_tps61310->strb0, 1);
+		queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work,
+				   msecs_to_jiffies(this_tps61310->flash_sw_timeout));
+	break;
+	case FL_MODE_FLASH_LEVEL1:
+			tps61310_i2c_command(0x05, 0x6A);
+			tps61310_i2c_command(0x00, 0x00);
+			tps61310_i2c_command(0x01, 0x86);
+			gpio_set_value_cansleep(this_tps61310->strb1, 0);
+			gpio_set_value_cansleep(this_tps61310->strb0, 1);
+			queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work,
+					   msecs_to_jiffies(this_tps61310->flash_sw_timeout));
+	break;
+	case FL_MODE_FLASH_LEVEL2:
+			tps61310_i2c_command(0x05, 0x6A);
+			tps61310_i2c_command(0x00, 0x00);
+			tps61310_i2c_command(0x01, 0x88);
+			gpio_set_value_cansleep(this_tps61310->strb1, 0);
+			gpio_set_value_cansleep(this_tps61310->strb0, 1);
+			queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work,
+					   msecs_to_jiffies(this_tps61310->flash_sw_timeout));
+	break;
+	case FL_MODE_FLASH_LEVEL3:
+			tps61310_i2c_command(0x05, 0x6A);
+			tps61310_i2c_command(0x00, 0x00);
+			tps61310_i2c_command(0x01, 0x8C);
+			gpio_set_value_cansleep(this_tps61310->strb1, 0);
+			gpio_set_value_cansleep(this_tps61310->strb0, 1);
+			queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work,
+					   msecs_to_jiffies(this_tps61310->flash_sw_timeout));
+	break;
+	case FL_MODE_FLASH_LEVEL4:
+			tps61310_i2c_command(0x05, 0x6A);
+			tps61310_i2c_command(0x00, 0x00);
+			tps61310_i2c_command(0x01, 0x90);
+			gpio_set_value_cansleep(this_tps61310->strb1, 0);
+			gpio_set_value_cansleep(this_tps61310->strb0, 1);
+			queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work,
+					   msecs_to_jiffies(this_tps61310->flash_sw_timeout));
+	break;
+	case FL_MODE_FLASH_LEVEL5:
+			tps61310_i2c_command(0x05, 0x6A);
+			tps61310_i2c_command(0x00, 0x00);
+			tps61310_i2c_command(0x01, 0x94);
+			gpio_set_value_cansleep(this_tps61310->strb1, 0);
+			gpio_set_value_cansleep(this_tps61310->strb0, 1);
+			queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work,
+					   msecs_to_jiffies(this_tps61310->flash_sw_timeout));
+	break;
+	case FL_MODE_FLASH_LEVEL6:
+			tps61310_i2c_command(0x05, 0x6A);
+			tps61310_i2c_command(0x00, 0x00);
+			tps61310_i2c_command(0x01, 0x98);
+			gpio_set_value_cansleep(this_tps61310->strb1, 0);
+			gpio_set_value_cansleep(this_tps61310->strb0, 1);
+			queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work,
+					   msecs_to_jiffies(this_tps61310->flash_sw_timeout));
+	break;
+	case FL_MODE_FLASH_LEVEL7:
+			tps61310_i2c_command(0x05, 0x6A);
+			tps61310_i2c_command(0x00, 0x00);
+			tps61310_i2c_command(0x01, 0x9C);
+			gpio_set_value_cansleep(this_tps61310->strb1, 0);
+			gpio_set_value_cansleep(this_tps61310->strb0, 1);
+			queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work,
+					   msecs_to_jiffies(this_tps61310->flash_sw_timeout));
+	break;
+	case FL_MODE_PRE_FLASH:
+		tps61310_i2c_command(0x05, 0x6A);
+		tps61310_i2c_command(0x00, 0x04);
+		gpio_set_value_cansleep(this_tps61310->strb0, 0);
+		gpio_set_value_cansleep(this_tps61310->strb1, 1);
+		tps61310_i2c_command(0x01, 0x40);
+	break;
+	case FL_MODE_TORCH:
+		tps61310_i2c_command(0x05, 0x6A);
+		tps61310_i2c_command(0x00, 0x05);
+		gpio_set_value_cansleep(this_tps61310->strb0, 0);
+		gpio_set_value_cansleep(this_tps61310->strb1, 1);
+		tps61310_i2c_command(0x01, 0x40);
+	break;
+	case FL_MODE_TORCH_LEVEL_1:
+		tps61310_i2c_command(0x05, 0x6A);
+		tps61310_i2c_command(0x00, 0x01);
+		gpio_set_value_cansleep(this_tps61310->strb0, 0);
+		gpio_set_value_cansleep(this_tps61310->strb1, 1);
+		tps61310_i2c_command(0x01, 0x40);
+	break;
+	case FL_MODE_TORCH_LEVEL_2:
+		tps61310_i2c_command(0x05, 0x6A);
+		tps61310_i2c_command(0x00, 0x03);
+		gpio_set_value_cansleep(this_tps61310->strb0, 0);
+		gpio_set_value_cansleep(this_tps61310->strb1, 1);
+		tps61310_i2c_command(0x01, 0x40);
+	break;
+	default:
+		FLT_ERR_LOG("%s: unknown flash_light flags: %d\n",
+							__func__, mode);
+		ret = -EINVAL;
+	break;
+	}
+		} else if (this_tps61310->led_count == 2) {
+	switch (mode) {
+	case FL_MODE_OFF:
+		flashlight_turn_off();
+	break;
+	case FL_MODE_FLASH:
+		tps61310_i2c_command(0x05, 0x6B);
+		tps61310_i2c_command(0x00, 0x00);
+		tps61310_i2c_command(0x02, 0x90);
+		tps61310_i2c_command(0x01, 0x90);
+		gpio_set_value_cansleep(this_tps61310->strb1, 0);
+		gpio_set_value_cansleep(this_tps61310->strb0, 1);
+		queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work,
+				   msecs_to_jiffies(this_tps61310->flash_sw_timeout));
+	break;
+	case FL_MODE_FLASH_LEVEL1:
+			tps61310_i2c_command(0x05, 0x6B);
+			tps61310_i2c_command(0x00, 0x00);
+			tps61310_i2c_command(0x02, 0x83);
+			tps61310_i2c_command(0x01, 0x83);
+			gpio_set_value_cansleep(this_tps61310->strb1, 0);
+			gpio_set_value_cansleep(this_tps61310->strb0, 1);
+			queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work,
+					   msecs_to_jiffies(this_tps61310->flash_sw_timeout));
+	break;
+	case FL_MODE_FLASH_LEVEL2:
+			tps61310_i2c_command(0x05, 0x6B);
+			tps61310_i2c_command(0x00, 0x00);
+			tps61310_i2c_command(0x02, 0x84);
+			tps61310_i2c_command(0x01, 0x84);
+			gpio_set_value_cansleep(this_tps61310->strb1, 0);
+			gpio_set_value_cansleep(this_tps61310->strb0, 1);
+			queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work,
+					   msecs_to_jiffies(this_tps61310->flash_sw_timeout));
+	break;
+	case FL_MODE_FLASH_LEVEL3:
+			tps61310_i2c_command(0x05, 0x6B);
+			tps61310_i2c_command(0x00, 0x00);
+			tps61310_i2c_command(0x02, 0x86);
+			tps61310_i2c_command(0x01, 0x86);
+			gpio_set_value_cansleep(this_tps61310->strb1, 0);
+			gpio_set_value_cansleep(this_tps61310->strb0, 1);
+			queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work,
+					   msecs_to_jiffies(this_tps61310->flash_sw_timeout));
+	break;
+	case FL_MODE_FLASH_LEVEL4:
+			tps61310_i2c_command(0x05, 0x6B);
+			tps61310_i2c_command(0x00, 0x00);
+			tps61310_i2c_command(0x02, 0x88);
+			tps61310_i2c_command(0x01, 0x88);
+			gpio_set_value_cansleep(this_tps61310->strb1, 0);
+			gpio_set_value_cansleep(this_tps61310->strb0, 1);
+			queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work,
+					   msecs_to_jiffies(this_tps61310->flash_sw_timeout));
+	break;
+	case FL_MODE_FLASH_LEVEL5:
+			tps61310_i2c_command(0x05, 0x6B);
+			tps61310_i2c_command(0x00, 0x00);
+			tps61310_i2c_command(0x02, 0x8A);
+			tps61310_i2c_command(0x01, 0x8A);
+			gpio_set_value_cansleep(this_tps61310->strb1, 0);
+			gpio_set_value_cansleep(this_tps61310->strb0, 1);
+			queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work,
+					   msecs_to_jiffies(this_tps61310->flash_sw_timeout));
+	break;
+	case FL_MODE_FLASH_LEVEL6:
+			tps61310_i2c_command(0x05, 0x6B);
+			tps61310_i2c_command(0x00, 0x00);
+			tps61310_i2c_command(0x02, 0x8C);
+			tps61310_i2c_command(0x01, 0x8C);
+			gpio_set_value_cansleep(this_tps61310->strb1, 0);
+			gpio_set_value_cansleep(this_tps61310->strb0, 1);
+			queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work,
+					   msecs_to_jiffies(this_tps61310->flash_sw_timeout));
+	break;
+	case FL_MODE_FLASH_LEVEL7:
+			tps61310_i2c_command(0x05, 0x6B);
+			tps61310_i2c_command(0x00, 0x00);
+			tps61310_i2c_command(0x02, 0x8E);
+			tps61310_i2c_command(0x01, 0x8E);
+			gpio_set_value_cansleep(this_tps61310->strb1, 0);
+			gpio_set_value_cansleep(this_tps61310->strb0, 1);
+			queue_delayed_work(tps61310_work_queue, &tps61310_delayed_work,
+					   msecs_to_jiffies(this_tps61310->flash_sw_timeout));
+	break;
+	case FL_MODE_PRE_FLASH:
+		tps61310_i2c_command(0x05, 0x6B);
+		tps61310_i2c_command(0x00, 0x12);
+		gpio_set_value_cansleep(this_tps61310->strb0, 0);
+		gpio_set_value_cansleep(this_tps61310->strb1, 1);
+		tps61310_i2c_command(0x01, 0x40);
+	break;
+	case FL_MODE_TORCH:
+		tps61310_i2c_command(0x05, 0x6B);
+		tps61310_i2c_command(0x00, 0x1B);
+		gpio_set_value_cansleep(this_tps61310->strb0, 0);
+		gpio_set_value_cansleep(this_tps61310->strb1, 1);
+		tps61310_i2c_command(0x01, 0x40);
+	break;
+	case FL_MODE_TORCH_LEVEL_1:
+		tps61310_i2c_command(0x05, 0x6B);
+		tps61310_i2c_command(0x00, 0x09);
+		gpio_set_value_cansleep(this_tps61310->strb0, 0);
+		gpio_set_value_cansleep(this_tps61310->strb1, 1);
+		tps61310_i2c_command(0x01, 0x40);
+	break;
+	case FL_MODE_TORCH_LEVEL_2:
+		tps61310_i2c_command(0x05, 0x6B);
+		tps61310_i2c_command(0x00, 0x12);
+		gpio_set_value_cansleep(this_tps61310->strb0, 0);
+		gpio_set_value_cansleep(this_tps61310->strb1, 1);
+		tps61310_i2c_command(0x01, 0x40);
+	break;
+	case FL_MODE_TORCH_LED_A:
+		tps61310_i2c_command(0x05, 0x69);
+		tps61310_i2c_command(0x00, 0x09);
+		gpio_set_value_cansleep(this_tps61310->strb0, 0);
+		gpio_set_value_cansleep(this_tps61310->strb1, 1);
+		tps61310_i2c_command(0x01, 0x40);
+	break;
+	case FL_MODE_TORCH_LED_B:
+		tps61310_i2c_command(0x05, 0x6A);
+		tps61310_i2c_command(0x00, 0x09);
+		gpio_set_value_cansleep(this_tps61310->strb0, 0);
+		gpio_set_value_cansleep(this_tps61310->strb1, 1);
+		tps61310_i2c_command(0x01, 0x40);
+	break;
+
+	default:
+		FLT_ERR_LOG("%s: unknown flash_light flags: %d\n",
+							__func__, mode);
+		ret = -EINVAL;
+	break;
+	}
+		}
+	FLT_INFO_LOG("%s: mode: %d\n", __func__, mode);
+	this_tps61310->mode_status = mode;
+	mutex_unlock(&tps61310_mutex);
+
+	return ret;
+}
+
+static void fl_lcdev_brightness_set(struct led_classdev *led_cdev,
+						enum led_brightness brightness)
+{
+	enum flashlight_mode_flags mode;
+	int ret = -1;
+
+
+	if (brightness > 0 && brightness <= LED_HALF) {
+		if (brightness == (LED_HALF - 2))
+			mode = FL_MODE_TORCH_LEVEL_1;
+		else if (brightness == (LED_HALF - 1))
+			mode = FL_MODE_TORCH_LEVEL_2;
+		else if (brightness == 1 && this_tps61310->led_count ==2)
+			mode = FL_MODE_TORCH_LED_A;
+		else if (brightness == 2 && this_tps61310->led_count ==2)
+			mode = FL_MODE_TORCH_LED_B;
+		else
+			mode = FL_MODE_TORCH;
+	} else if (brightness > LED_HALF && brightness <= LED_FULL) {
+		if (brightness == (LED_HALF + 1))
+			mode = FL_MODE_PRE_FLASH; 
+		else if (brightness == (LED_HALF + 3))
+			mode = FL_MODE_FLASH_LEVEL1; 
+		else if (brightness == (LED_HALF + 4))
+			mode = FL_MODE_FLASH_LEVEL2; 
+		else if (brightness == (LED_HALF + 5))
+			mode = FL_MODE_FLASH_LEVEL3; 
+		else if (brightness == (LED_HALF + 6))
+			mode = FL_MODE_FLASH_LEVEL4; 
+		else if (brightness == (LED_HALF + 7))
+			mode = FL_MODE_FLASH_LEVEL5; 
+		else if (brightness == (LED_HALF + 8))
+			mode = FL_MODE_FLASH_LEVEL6; 
+		else if (brightness == (LED_HALF + 9))
+			mode = FL_MODE_FLASH_LEVEL7; 
+		else
+			mode = FL_MODE_FLASH; 
+	} else
+		
+		mode = FL_MODE_OFF;
+
+	if ((mode != FL_MODE_OFF) && switch_state == 0){
+		FLT_INFO_LOG("%s flashlight is disabled by switch, mode = %d\n",__func__, mode);
+		return;
+	}
+
+
+	ret = tps61310_flashlight_control(mode);
+	if (ret) {
+		FLT_ERR_LOG("%s: control failure rc:%d\n", __func__, ret);
+		return;
+	}
+}
+
+static void flashlight_early_suspend(struct early_suspend *handler)
+{
+	FLT_INFO_LOG("%s\n", __func__);
+	if (this_tps61310 != NULL && this_tps61310->mode_status)
+		flashlight_turn_off();
+}
+
+static void flashlight_late_resume(struct early_suspend *handler)
+{
+}
+
+static void flashlight_turn_off_work(struct work_struct *work)
+{
+	FLT_INFO_LOG("%s\n", __func__);
+	flashlight_turn_off();
+}
+
+static int tps61310_probe(struct i2c_client *client,
+					const struct i2c_device_id *id)
+{
+	struct tps61310_data *tps61310;
+	struct TPS61310_flashlight_platform_data *pdata;
+	int err = 0;
+
+	FLT_INFO_LOG("%s +\n", __func__);
+	pdata = client->dev.platform_data;
+	if (!pdata) {
+		FLT_ERR_LOG("%s: Assign platform_data error!!\n", __func__);
+		return -EINVAL;
+	}
+
+	if (pdata->gpio_init)
+		pdata->gpio_init();
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		err = -ENODEV;
+		goto check_functionality_failed;
+	}
+
+	tps61310 = kzalloc(sizeof(struct tps61310_data), GFP_KERNEL);
+	if (!tps61310) {
+		FLT_ERR_LOG("%s: kzalloc fail !!!\n", __func__);
+		return -ENOMEM;
+	}
+
+	i2c_set_clientdata(client, tps61310);
+	this_client = client;
+
+	INIT_DELAYED_WORK(&tps61310_delayed_work, flashlight_turn_off_work);
+	tps61310_work_queue = create_singlethread_workqueue("tps61310_wq");
+	if (!tps61310_work_queue)
+		goto err_create_tps61310_work_queue;
+
+	
+	tps61310->fl_lcdev.name           = FLASHLIGHT_NAME;
+	tps61310->fl_lcdev.brightness_set = fl_lcdev_brightness_set;
+	tps61310->strb0                   = pdata->tps61310_strb0;
+	tps61310->strb1                   = pdata->tps61310_strb1;
+	tps61310->flash_sw_timeout	  = pdata->flash_duration_ms;
+	tps61310->led_count = (pdata->led_count) ? pdata->led_count : 1;
+	tps61310->mode_pin_suspend_state_low = pdata->mode_pin_suspend_state_low;
+
+	if (tps61310->flash_sw_timeout <= 0)
+		tps61310->flash_sw_timeout = 600;
+
+	mutex_init(&tps61310_mutex);
+	err = led_classdev_register(&client->dev, &tps61310->fl_lcdev);
+	if (err < 0) {
+		FLT_ERR_LOG("%s: failed on led_classdev_register\n", __func__);
+		goto platform_data_null;
+	}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+	tps61310->fl_early_suspend.suspend = flashlight_early_suspend;
+	tps61310->fl_early_suspend.resume  = flashlight_late_resume;
+	register_early_suspend(&tps61310->fl_early_suspend);
+#endif
+	this_tps61310 = tps61310;
+
+	err = device_create_file(tps61310->fl_lcdev.dev, &dev_attr_function_switch);
+	if (err < 0) {
+		FLT_ERR_LOG("%s, create function_switch sysfs fail\n", __func__);
+	}
+	
+	tps61310_i2c_command(0x01, 0x00);
+	
+	tps61310_i2c_command(0x07, 0xF6);
+
+	FLT_INFO_LOG("%s -\n", __func__);
+	return 0;
+
+
+platform_data_null:
+	destroy_workqueue(tps61310_work_queue);
+	mutex_destroy(&tps61310_mutex);
+err_create_tps61310_work_queue:
+	kfree(tps61310);
+check_functionality_failed:
+	return err;
+}
+
+static int tps61310_remove(struct i2c_client *client)
+{
+	struct tps61310_data *tps61310 = i2c_get_clientdata(client);
+
+	led_classdev_unregister(&tps61310->fl_lcdev);
+	destroy_workqueue(tps61310_work_queue);
+	mutex_destroy(&tps61310_mutex);
+	unregister_early_suspend(&tps61310->fl_early_suspend);
+	kfree(tps61310);
+
+	FLT_INFO_LOG("%s:\n", __func__);
+	return 0;
+}
+
+static const struct i2c_device_id tps61310_id[] = {
+	{ "TPS61310_FLASHLIGHT", 0 },
+	{ }
+};
+static int tps61310_resume(struct i2c_client *client)
+{
+
+		FLT_INFO_LOG("%s:\n", __func__);
+		if (this_tps61310->mode_pin_suspend_state_low)
+			gpio_set_value_cansleep(this_tps61310->strb1, 1);
+		return 0;
+}
+static int tps61310_suspend(struct i2c_client *client, pm_message_t state)
+{
+
+		FLT_INFO_LOG("%s:\n", __func__);
+		if (this_tps61310->mode_pin_suspend_state_low)
+			gpio_set_value_cansleep(this_tps61310->strb1, 0);
+
+		return 0;
+}
+
+static struct i2c_driver tps61310_driver = {
+	.probe		= tps61310_probe,
+	.remove		= tps61310_remove,
+	.suspend	= tps61310_suspend,
+	.resume		= tps61310_resume,
+	.id_table	= tps61310_id,
+	.driver		= {
+		.name = "TPS61310_FLASHLIGHT",
+	},
+};
+
+static int __init tps61310_init(void)
+{
+	FLT_INFO_LOG("tps61310 Led Flash driver: init\n");
+	return i2c_add_driver(&tps61310_driver);
+}
+
+static void __exit tps61310_exit(void)
+{
+	i2c_del_driver(&tps61310_driver);
+}
+
+module_init(tps61310_init);
+module_exit(tps61310_exit);
+
+MODULE_DESCRIPTION("TPS61310 Led Flash driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/htc_flashlight.h b/include/linux/htc_flashlight.h
new file mode 100644
index 0000000..15be786
--- /dev/null
+++ b/include/linux/htc_flashlight.h
@@ -0,0 +1,92 @@
+/*
+ * arch/arm/mach-msm/include/mach/msm_flashlight.h - The flashlight header
+ * Copyright (C) 2009  HTC Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __HTC_FLASHLIGHT_H
+#define __HTC_FLASHLIGHT_H
+#include <linux/earlysuspend.h>
+
+#define FLASHLIGHT_NAME "flashlight"
+
+#define FLASHLIGHT_OFF   0
+#define FLASHLIGHT_TORCH 1
+#define FLASHLIGHT_FLASH 2
+#define FLASHLIGHT_NUM   3
+
+
+enum flashlight_mode_flags {
+	FL_MODE_OFF = 0,
+	FL_MODE_TORCH,
+	FL_MODE_FLASH,
+	FL_MODE_PRE_FLASH,
+	FL_MODE_TORCH_LED_A,
+	FL_MODE_TORCH_LED_B,
+	FL_MODE_TORCH_LEVEL_1,
+	FL_MODE_TORCH_LEVEL_2,
+	FL_MODE_CAMERA_EFFECT_FLASH,
+	FL_MODE_CAMERA_EFFECT_PRE_FLASH,
+	FL_MODE_FLASH_LEVEL1,
+	FL_MODE_FLASH_LEVEL2,
+	FL_MODE_FLASH_LEVEL3,
+	FL_MODE_FLASH_LEVEL4,
+	FL_MODE_FLASH_LEVEL5,
+	FL_MODE_FLASH_LEVEL6,
+	FL_MODE_FLASH_LEVEL7,
+
+};
+
+#ifdef CONFIG_FLASHLIGHT_AAT
+struct flashlight_platform_data {
+	void (*gpio_init) (void);
+	uint32_t torch;
+	uint32_t flash;
+	uint32_t flash_adj;
+	uint32_t torch_set1;
+	uint32_t torch_set2;
+	uint32_t flash_duration_ms;
+	uint8_t led_count; 
+	uint32_t chip_model;
+};
+
+enum flashlight_chip{
+	AAT1271 = 0,
+	AAT3177,
+	AAT1277,
+};
+#endif
+
+#ifdef CONFIG_FLASHLIGHT_TPS61310
+struct TPS61310_flashlight_platform_data {
+	void (*gpio_init) (void);
+	uint32_t flash_duration_ms;
+	uint8_t led_count; 
+	uint32_t tps61310_strb0;
+	uint32_t tps61310_strb1;
+	uint8_t mode_pin_suspend_state_low;
+};
+
+int aat1271_flashlight_control(int mode);
+int adp1650_flashlight_control(int mode);
+int aat3177_flashlight_control(int mode);
+int aat1277_flashlight_control(int mode);
+int tps61310_flashlight_control(int mode);
+int htc_flashlight_control(int flashlight_mode);
+#endif
+
+#undef __HTC_FLASHLIGHT_H
+#endif