diff --git a/Documentation/devicetree/bindings/leds/leds-msm-gpio-flash.txt b/Documentation/devicetree/bindings/leds/leds-msm-gpio-flash.txt new file mode 100644 index 00000000000..ddbfd5a676f --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-msm-gpio-flash.txt @@ -0,0 +1,30 @@ +Qualcomm GPIO Flash Leds + +Qualcomm GPIO flash LEDs driver is used for +controlling flash LEDs that are connected with 2 GPIOs. The 2 GPIOs are name +flash_en and flash now. + + +The flash LEDs has torch mode and flash mode. The flash mode is used for camera. +The torch mode is used for torch. + + +Required properties for each node: +- compatible : should be "qcom,leds-gpio-flash" +- qcom,flash-en : the GPIO pin number of flash-en function +- qcom,flash-now : the GPIO pin number of flash-now function +- linux,name : name of the led that is used in led framework +- linux,default-trigger : name of the led trigger event + +No other optional properties for it. + +Example: + + flashlight { + compatible = "leds-gpio-flash"; + status = "okay"; + qcom,flash-en = <&msmgpio 18 0>; + qcom,flash-now = <&msmgpio 19 0>; + linux,name = "flashlight"; + linux,default-trigger = "flashlight-trigger"; + }; diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index f13e55a724e..159c6f6e01e 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -300,6 +300,16 @@ config LEDS_QPNP To compile this driver as a module, choose M here: the module will be called leds-qpnp. +config LEDS_MSM_GPIO_FLASH + tristate "Support for GPIO Flash LEDs" + depends on GPIO_MSM_V3 + help + This driver supports the leds functionality of GPIO Flash LED. It + includes flash mode and torch mode. + + To compile this driver as a module, choose M here: the module will + be called leds-gpio-flash. + config LEDS_WM831X_STATUS tristate "LED support for status LEDs on WM831x PMICs" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index c688898cbf3..f20a0d0c784 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_LEDS_PMIC_MPP) += leds-pmic-mpp.o obj-$(CONFIG_LEDS_QCIBL) += leds-qci-backlight.o obj-$(CONFIG_LEDS_MSM_PDM) += leds-msm-pdm.o obj-$(CONFIG_LEDS_MSM_TRICOLOR) += leds-msm-tricolor.o +obj-$(CONFIG_LEDS_MSM_GPIO_FLASH) += leds-msm-gpio-flash.o # LED SPI Drivers obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o diff --git a/drivers/leds/leds-msm-gpio-flash.c b/drivers/leds/leds-msm-gpio-flash.c new file mode 100644 index 00000000000..60d46815235 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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");