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/arch/arm/mach-msm/board-mahimahi-flashlight.c b/arch/arm/mach-msm/board-mahimahi-flashlight.c
new file mode 100644
index 0000000..829b1f1
--- /dev/null
+++ b/arch/arm/mach-msm/board-mahimahi-flashlight.c
@@ -0,0 +1,279 @@
+/*
+ * arch/arm/mach-msm/flashlight.c - flashlight driver
+ *
+ * Copyright (C) 2009 zion huang <zion_huang@htc.com>
+ *
+ * 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; version 2 of the License.
+ */
+
+#define DEBUG
+
+#include <linux/delay.h>
+#include <linux/earlysuspend.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/wakelock.h>
+#include <linux/hrtimer.h>
+#include <mach/msm_iomap.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+
+#include "board-mahimahi-flashlight.h"
+
+struct flashlight_struct {
+ struct led_classdev fl_lcdev;
+ struct early_suspend early_suspend_flashlight;
+ spinlock_t spin_lock;
+ struct hrtimer timer;
+ int brightness;
+ int gpio_torch;
+ int gpio_flash;
+ int flash_duration_ms;
+};
+
+static struct flashlight_struct the_fl;
+
+static inline void toggle(void)
+{
+ gpio_direction_output(the_fl.gpio_torch, 0);
+ udelay(2);
+ gpio_direction_output(the_fl.gpio_torch, 1);
+ udelay(2);
+}
+
+static void flashlight_hw_command(uint8_t addr, uint8_t data)
+{
+ int i;
+
+ for (i = 0; i < addr + 17; i++)
+ toggle();
+ udelay(500);
+
+ for (i = 0; i < data; i++)
+ toggle();
+ udelay(500);
+}
+
+static enum hrtimer_restart flashlight_timeout(struct hrtimer *timer)
+{
+ unsigned long flags;
+
+ pr_debug("%s\n", __func__);
+
+ spin_lock_irqsave(&the_fl.spin_lock, flags);
+ gpio_direction_output(the_fl.gpio_flash, 0);
+ the_fl.brightness = LED_OFF;
+ spin_unlock_irqrestore(&the_fl.spin_lock, flags);
+
+ return HRTIMER_NORESTART;
+}
+
+int flashlight_control(int mode)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ pr_debug("%s: mode %d -> %d\n", __func__,
+ the_fl.brightness, mode);
+
+ spin_lock_irqsave(&the_fl.spin_lock, flags);
+
+ the_fl.brightness = mode;
+
+ switch (mode) {
+ case FLASHLIGHT_TORCH:
+ pr_info("%s: half\n", __func__);
+ /* If we are transitioning from flash to torch, make sure to
+ * cancel the flash timeout timer, otherwise when it expires,
+ * the torch will go off as well.
+ */
+ hrtimer_cancel(&the_fl.timer);
+ flashlight_hw_command(2, 4);
+ break;
+
+ case FLASHLIGHT_FLASH:
+ pr_info("%s: full\n", __func__);
+ hrtimer_cancel(&the_fl.timer);
+ gpio_direction_output(the_fl.gpio_flash, 0);
+ udelay(40);
+ gpio_direction_output(the_fl.gpio_flash, 1);
+ hrtimer_start(&the_fl.timer,
+ ktime_set(the_fl.flash_duration_ms / 1000,
+ (the_fl.flash_duration_ms % 1000) *
+ NSEC_PER_MSEC),
+ HRTIMER_MODE_REL);
+ /* Flash overrides torch mode, and after the flash period, the
+ * flash LED will turn off.
+ */
+ mode = LED_OFF;
+ break;
+
+ case FLASHLIGHT_OFF:
+ pr_info("%s: off\n", __func__);
+ gpio_direction_output(the_fl.gpio_flash, 0);
+ gpio_direction_output(the_fl.gpio_torch, 0);
+ break;
+
+ default:
+ pr_err("%s: unknown flash_light flags: %d\n", __func__, mode);
+ ret = -EINVAL;
+ goto done;
+ }
+
+done:
+ spin_unlock_irqrestore(&the_fl.spin_lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL(flashlight_control);
+
+static void fl_lcdev_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ int level;
+ switch (brightness) {
+ case LED_HALF:
+ level = FLASHLIGHT_TORCH;
+ break;
+ case LED_FULL:
+ level = FLASHLIGHT_FLASH;
+ break;
+ case LED_OFF:
+ default:
+ level = FLASHLIGHT_OFF;
+ };
+
+ flashlight_control(level);
+}
+
+static void flashlight_early_suspend(struct early_suspend *handler)
+{
+ flashlight_control(FLASHLIGHT_OFF);
+}
+
+static int flashlight_setup_gpio(struct flashlight_platform_data *fl_pdata)
+{
+ int ret;
+
+ pr_debug("%s\n", __func__);
+
+ if (fl_pdata->gpio_init) {
+ ret = fl_pdata->gpio_init();
+ if (ret < 0) {
+ pr_err("%s: gpio init failed: %d\n", __func__,
+ ret);
+ return ret;
+ }
+ }
+
+ if (fl_pdata->torch) {
+ ret = gpio_request(fl_pdata->torch, "flashlight_torch");
+ if (ret < 0) {
+ pr_err("%s: gpio_request failed\n", __func__);
+ return ret;
+ }
+ }
+
+ if (fl_pdata->flash) {
+ ret = gpio_request(fl_pdata->flash, "flashlight_flash");
+ if (ret < 0) {
+ pr_err("%s: gpio_request failed\n", __func__);
+ gpio_free(fl_pdata->torch);
+ return ret;
+ }
+ }
+
+ the_fl.gpio_torch = fl_pdata->torch;
+ the_fl.gpio_flash = fl_pdata->flash;
+ the_fl.flash_duration_ms = fl_pdata->flash_duration_ms;
+ return 0;
+}
+
+static int flashlight_probe(struct platform_device *pdev)
+{
+ struct flashlight_platform_data *fl_pdata = pdev->dev.platform_data;
+ int err = 0;
+
+ pr_debug("%s\n", __func__);
+
+ err = flashlight_setup_gpio(fl_pdata);
+ if (err < 0) {
+ pr_err("%s: setup GPIO failed\n", __func__);
+ goto fail_free_mem;
+ }
+
+ spin_lock_init(&the_fl.spin_lock);
+ the_fl.fl_lcdev.name = pdev->name;
+ the_fl.fl_lcdev.brightness_set = fl_lcdev_brightness_set;
+ the_fl.fl_lcdev.brightness = LED_OFF;
+ err = led_classdev_register(&pdev->dev, &the_fl.fl_lcdev);
+ if (err < 0) {
+ pr_err("failed on led_classdev_register\n");
+ goto fail_free_gpio;
+ }
+
+ hrtimer_init(&the_fl.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ the_fl.timer.function = flashlight_timeout;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ the_fl.early_suspend_flashlight.suspend = flashlight_early_suspend;
+ the_fl.early_suspend_flashlight.resume = NULL;
+ register_early_suspend(&the_fl.early_suspend_flashlight);
+#endif
+
+ return 0;
+
+fail_free_gpio:
+ if (fl_pdata->torch)
+ gpio_free(fl_pdata->torch);
+ if (fl_pdata->flash)
+ gpio_free(fl_pdata->flash);
+fail_free_mem:
+ return err;
+}
+
+static int flashlight_remove(struct platform_device *pdev)
+{
+ struct flashlight_platform_data *fl_pdata = pdev->dev.platform_data;
+
+ pr_debug("%s\n", __func__);
+
+ hrtimer_cancel(&the_fl.timer);
+ unregister_early_suspend(&the_fl.early_suspend_flashlight);
+ flashlight_control(FLASHLIGHT_OFF);
+ led_classdev_unregister(&the_fl.fl_lcdev);
+ if (fl_pdata->torch)
+ gpio_free(fl_pdata->torch);
+ if (fl_pdata->flash)
+ gpio_free(fl_pdata->flash);
+ return 0;
+}
+
+static struct platform_driver flashlight_driver = {
+ .probe = flashlight_probe,
+ .remove = flashlight_remove,
+ .driver = {
+ .name = FLASHLIGHT_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init flashlight_init(void)
+{
+ pr_debug("%s\n", __func__);
+ return platform_driver_register(&flashlight_driver);
+}
+
+static void __exit flashlight_exit(void)
+{
+ pr_debug("%s\n", __func__);
+ platform_driver_unregister(&flashlight_driver);
+}
+
+module_init(flashlight_init);
+module_exit(flashlight_exit);
+
+MODULE_AUTHOR("Zion Huang <zion_huang@htc.com>");
+MODULE_DESCRIPTION("flash light driver");
+MODULE_LICENSE("GPL");