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");