leds: leds-gpio-flash: add led gpio flash driver

the flash led pins flash_en and flash_now are connected with msm gpios.
add led gpio flash driver for qrd board.

Change-Id: I057958f8756c8fc31ba1a5032cf096d7a7e27e17
Signed-off-by: Tingting Yang <tingting@codeaurora.org>
diff --git a/drivers/leds/leds-msm-gpio-flash.c b/drivers/leds/leds-msm-gpio-flash.c
new file mode 100644
index 0000000..60d4681
--- /dev/null
+++ b/drivers/leds/leds-msm-gpio-flash.c
@@ -0,0 +1,208 @@
+
+/* Copyright (c) 2013, The Linux Foundation. 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/module.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/platform_device.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/printk.h>
+
+#define LED_GPIO_FLASH_DRIVER_NAME	"qcom,leds-gpio-flash"
+#define LED_TRIGGER_DEFAULT		"none"
+
+struct led_gpio_flash_data {
+	int flash_en;
+	int flash_now;
+	int brightness;
+	struct led_classdev cdev;
+};
+
+static struct of_device_id led_gpio_flash_of_match[] = {
+	{.compatible = LED_GPIO_FLASH_DRIVER_NAME,},
+	{},
+};
+
+static void led_gpio_brightness_set(struct led_classdev *led_cdev,
+				    enum led_brightness value)
+{
+	int rc = 0;
+	struct led_gpio_flash_data *flash_led =
+	    container_of(led_cdev, struct led_gpio_flash_data, cdev);
+
+	int brightness = value;
+	int flash_en = 0, flash_now = 0;
+
+	if (brightness > LED_HALF) {
+		flash_en = 0;
+		flash_now = 1;
+	} else if (brightness > LED_OFF) {
+		flash_en = 1;
+		flash_now = 0;
+	} else {
+		flash_en = 0;
+		flash_now = 0;
+	}
+
+	rc = gpio_direction_output(flash_led->flash_en, flash_en);
+	if (rc) {
+		pr_err("%s: Failed to set gpio %d\n", __func__,
+		       flash_led->flash_en);
+		goto err;
+	}
+	rc = gpio_direction_output(flash_led->flash_now, flash_now);
+	if (rc) {
+		pr_err("%s: Failed to set gpio %d\n", __func__,
+		       flash_led->flash_now);
+		goto err;
+	}
+
+	flash_led->brightness = brightness;
+err:
+	return;
+}
+
+static enum led_brightness led_gpio_brightness_get(struct led_classdev
+						   *led_cdev)
+{
+	struct led_gpio_flash_data *flash_led =
+	    container_of(led_cdev, struct led_gpio_flash_data, cdev);
+	return flash_led->brightness;
+}
+
+int __devinit led_gpio_flash_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	const char *temp_str;
+	struct led_gpio_flash_data *flash_led = NULL;
+	struct device_node *node = pdev->dev.of_node;
+
+	flash_led = devm_kzalloc(&pdev->dev, sizeof(struct led_gpio_flash_data),
+				 GFP_KERNEL);
+	if (flash_led == NULL) {
+		dev_err(&pdev->dev, "%s:%d Unable to allocate memory\n",
+			__func__, __LINE__);
+		return -ENOMEM;
+	}
+
+	flash_led->cdev.default_trigger = LED_TRIGGER_DEFAULT;
+	rc = of_property_read_string(node, "linux,default-trigger", &temp_str);
+	if (!rc)
+		flash_led->cdev.default_trigger = temp_str;
+
+	flash_led->flash_en = of_get_named_gpio(node, "qcom,flash-en", 0);
+
+	if (flash_led->flash_en < 0) {
+		dev_err(&pdev->dev,
+			"Looking up %s property in node %s failed. rc =  %d\n",
+			"flash-en", node->full_name, flash_led->flash_en);
+		goto error;
+	} else {
+		rc = gpio_request(flash_led->flash_en, "FLASH_EN");
+		if (rc) {
+			dev_err(&pdev->dev,
+				"%s: Failed to request gpio %d,rc = %d\n",
+				__func__, flash_led->flash_en, rc);
+
+			goto error;
+		}
+	}
+
+	flash_led->flash_now = of_get_named_gpio(node, "qcom,flash-now", 0);
+	if (flash_led->flash_now < 0) {
+		dev_err(&pdev->dev,
+			"Looking up %s property in node %s failed. rc =  %d\n",
+			"flash-now", node->full_name, flash_led->flash_now);
+		goto error;
+	} else {
+		rc = gpio_request(flash_led->flash_now, "FLASH_NOW");
+		if (rc) {
+			dev_err(&pdev->dev,
+				"%s: Failed to request gpio %d,rc = %d\n",
+				__func__, flash_led->flash_now, rc);
+
+			goto error;
+		}
+	}
+
+	gpio_tlmm_config(GPIO_CFG(flash_led->flash_en, 0,
+				  GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
+				  GPIO_CFG_2MA), GPIO_CFG_ENABLE);
+	gpio_tlmm_config(GPIO_CFG(flash_led->flash_now, 0,
+				  GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
+				  GPIO_CFG_2MA), GPIO_CFG_ENABLE);
+
+	rc = of_property_read_string(node, "linux,name", &flash_led->cdev.name);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: Failed to read linux name. rc = %d\n",
+			__func__, rc);
+		goto error;
+	}
+
+	platform_set_drvdata(pdev, flash_led);
+	flash_led->cdev.max_brightness = LED_FULL;
+	flash_led->cdev.brightness_set = led_gpio_brightness_set;
+	flash_led->cdev.brightness_get = led_gpio_brightness_get;
+
+	rc = led_classdev_register(&pdev->dev, &flash_led->cdev);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: Failed to register led dev. rc = %d\n",
+			__func__, rc);
+		goto error;
+	}
+	return 0;
+
+error:
+	devm_kfree(&pdev->dev, flash_led);
+	return rc;
+}
+
+int __devexit led_gpio_flash_remove(struct platform_device *pdev)
+{
+	struct led_gpio_flash_data *flash_led =
+	    (struct led_gpio_flash_data *)platform_get_drvdata(pdev);
+
+	led_classdev_unregister(&flash_led->cdev);
+	devm_kfree(&pdev->dev, flash_led);
+	return 0;
+}
+
+static struct platform_driver led_gpio_flash_driver = {
+	.probe = led_gpio_flash_probe,
+	.remove = __devexit_p(led_gpio_flash_remove),
+	.driver = {
+		   .name = LED_GPIO_FLASH_DRIVER_NAME,
+		   .owner = THIS_MODULE,
+		   .of_match_table = led_gpio_flash_of_match,
+		   }
+};
+
+static int __init led_gpio_flash_init(void)
+{
+	return platform_driver_register(&led_gpio_flash_driver);
+}
+
+static void __exit led_gpio_flash_exit(void)
+{
+	return platform_driver_unregister(&led_gpio_flash_driver);
+}
+
+late_initcall(led_gpio_flash_init);
+module_exit(led_gpio_flash_exit);
+
+MODULE_DESCRIPTION("QCOM GPIO LEDs driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("leds:leds-msm-gpio-flash");