board-mahimahi-flashlight.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. /*
  2. * arch/arm/mach-msm/flashlight.c - flashlight driver
  3. *
  4. * Copyright (C) 2009 zion huang <zion_huang@htc.com>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; version 2 of the License.
  9. */
  10. #define DEBUG
  11. #include <linux/delay.h>
  12. #include <linux/earlysuspend.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/leds.h>
  15. #include <linux/wakelock.h>
  16. #include <linux/hrtimer.h>
  17. #include <mach/msm_iomap.h>
  18. #include <asm/gpio.h>
  19. #include <asm/io.h>
  20. #include "board-mahimahi-flashlight.h"
  21. struct flashlight_struct {
  22. struct led_classdev fl_lcdev;
  23. struct early_suspend early_suspend_flashlight;
  24. spinlock_t spin_lock;
  25. struct hrtimer timer;
  26. int brightness;
  27. int gpio_torch;
  28. int gpio_flash;
  29. int flash_duration_ms;
  30. };
  31. static struct flashlight_struct the_fl;
  32. static inline void toggle(void)
  33. {
  34. gpio_direction_output(the_fl.gpio_torch, 0);
  35. udelay(2);
  36. gpio_direction_output(the_fl.gpio_torch, 1);
  37. udelay(2);
  38. }
  39. static void flashlight_hw_command(uint8_t addr, uint8_t data)
  40. {
  41. int i;
  42. for (i = 0; i < addr + 17; i++)
  43. toggle();
  44. udelay(500);
  45. for (i = 0; i < data; i++)
  46. toggle();
  47. udelay(500);
  48. }
  49. static enum hrtimer_restart flashlight_timeout(struct hrtimer *timer)
  50. {
  51. unsigned long flags;
  52. pr_debug("%s\n", __func__);
  53. spin_lock_irqsave(&the_fl.spin_lock, flags);
  54. gpio_direction_output(the_fl.gpio_flash, 0);
  55. the_fl.brightness = LED_OFF;
  56. spin_unlock_irqrestore(&the_fl.spin_lock, flags);
  57. return HRTIMER_NORESTART;
  58. }
  59. int flashlight_control(int mode)
  60. {
  61. int ret = 0;
  62. unsigned long flags;
  63. pr_debug("%s: mode %d -> %d\n", __func__,
  64. the_fl.brightness, mode);
  65. spin_lock_irqsave(&the_fl.spin_lock, flags);
  66. the_fl.brightness = mode;
  67. switch (mode) {
  68. case FLASHLIGHT_TORCH:
  69. pr_info("%s: half\n", __func__);
  70. /* If we are transitioning from flash to torch, make sure to
  71. * cancel the flash timeout timer, otherwise when it expires,
  72. * the torch will go off as well.
  73. */
  74. hrtimer_cancel(&the_fl.timer);
  75. flashlight_hw_command(2, 4);
  76. break;
  77. case FLASHLIGHT_FLASH:
  78. pr_info("%s: full\n", __func__);
  79. hrtimer_cancel(&the_fl.timer);
  80. gpio_direction_output(the_fl.gpio_flash, 0);
  81. udelay(40);
  82. gpio_direction_output(the_fl.gpio_flash, 1);
  83. hrtimer_start(&the_fl.timer,
  84. ktime_set(the_fl.flash_duration_ms / 1000,
  85. (the_fl.flash_duration_ms % 1000) *
  86. NSEC_PER_MSEC),
  87. HRTIMER_MODE_REL);
  88. /* Flash overrides torch mode, and after the flash period, the
  89. * flash LED will turn off.
  90. */
  91. mode = LED_OFF;
  92. break;
  93. case FLASHLIGHT_OFF:
  94. pr_info("%s: off\n", __func__);
  95. gpio_direction_output(the_fl.gpio_flash, 0);
  96. gpio_direction_output(the_fl.gpio_torch, 0);
  97. break;
  98. default:
  99. pr_err("%s: unknown flash_light flags: %d\n", __func__, mode);
  100. ret = -EINVAL;
  101. goto done;
  102. }
  103. done:
  104. spin_unlock_irqrestore(&the_fl.spin_lock, flags);
  105. return ret;
  106. }
  107. EXPORT_SYMBOL(flashlight_control);
  108. static void fl_lcdev_brightness_set(struct led_classdev *led_cdev,
  109. enum led_brightness brightness)
  110. {
  111. int level;
  112. switch (brightness) {
  113. case LED_HALF:
  114. level = FLASHLIGHT_TORCH;
  115. break;
  116. case LED_FULL:
  117. level = FLASHLIGHT_FLASH;
  118. break;
  119. case LED_OFF:
  120. default:
  121. level = FLASHLIGHT_OFF;
  122. };
  123. flashlight_control(level);
  124. }
  125. static void flashlight_early_suspend(struct early_suspend *handler)
  126. {
  127. flashlight_control(FLASHLIGHT_OFF);
  128. }
  129. static int flashlight_setup_gpio(struct flashlight_platform_data *fl_pdata)
  130. {
  131. int ret;
  132. pr_debug("%s\n", __func__);
  133. if (fl_pdata->gpio_init) {
  134. ret = fl_pdata->gpio_init();
  135. if (ret < 0) {
  136. pr_err("%s: gpio init failed: %d\n", __func__,
  137. ret);
  138. return ret;
  139. }
  140. }
  141. if (fl_pdata->torch) {
  142. ret = gpio_request(fl_pdata->torch, "flashlight_torch");
  143. if (ret < 0) {
  144. pr_err("%s: gpio_request failed\n", __func__);
  145. return ret;
  146. }
  147. }
  148. if (fl_pdata->flash) {
  149. ret = gpio_request(fl_pdata->flash, "flashlight_flash");
  150. if (ret < 0) {
  151. pr_err("%s: gpio_request failed\n", __func__);
  152. gpio_free(fl_pdata->torch);
  153. return ret;
  154. }
  155. }
  156. the_fl.gpio_torch = fl_pdata->torch;
  157. the_fl.gpio_flash = fl_pdata->flash;
  158. the_fl.flash_duration_ms = fl_pdata->flash_duration_ms;
  159. return 0;
  160. }
  161. static int flashlight_probe(struct platform_device *pdev)
  162. {
  163. struct flashlight_platform_data *fl_pdata = pdev->dev.platform_data;
  164. int err = 0;
  165. pr_debug("%s\n", __func__);
  166. err = flashlight_setup_gpio(fl_pdata);
  167. if (err < 0) {
  168. pr_err("%s: setup GPIO failed\n", __func__);
  169. goto fail_free_mem;
  170. }
  171. spin_lock_init(&the_fl.spin_lock);
  172. the_fl.fl_lcdev.name = pdev->name;
  173. the_fl.fl_lcdev.brightness_set = fl_lcdev_brightness_set;
  174. the_fl.fl_lcdev.brightness = LED_OFF;
  175. err = led_classdev_register(&pdev->dev, &the_fl.fl_lcdev);
  176. if (err < 0) {
  177. pr_err("failed on led_classdev_register\n");
  178. goto fail_free_gpio;
  179. }
  180. hrtimer_init(&the_fl.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
  181. the_fl.timer.function = flashlight_timeout;
  182. #ifdef CONFIG_HAS_EARLYSUSPEND
  183. the_fl.early_suspend_flashlight.suspend = flashlight_early_suspend;
  184. the_fl.early_suspend_flashlight.resume = NULL;
  185. register_early_suspend(&the_fl.early_suspend_flashlight);
  186. #endif
  187. return 0;
  188. fail_free_gpio:
  189. if (fl_pdata->torch)
  190. gpio_free(fl_pdata->torch);
  191. if (fl_pdata->flash)
  192. gpio_free(fl_pdata->flash);
  193. fail_free_mem:
  194. return err;
  195. }
  196. static int flashlight_remove(struct platform_device *pdev)
  197. {
  198. struct flashlight_platform_data *fl_pdata = pdev->dev.platform_data;
  199. pr_debug("%s\n", __func__);
  200. hrtimer_cancel(&the_fl.timer);
  201. unregister_early_suspend(&the_fl.early_suspend_flashlight);
  202. flashlight_control(FLASHLIGHT_OFF);
  203. led_classdev_unregister(&the_fl.fl_lcdev);
  204. if (fl_pdata->torch)
  205. gpio_free(fl_pdata->torch);
  206. if (fl_pdata->flash)
  207. gpio_free(fl_pdata->flash);
  208. return 0;
  209. }
  210. static struct platform_driver flashlight_driver = {
  211. .probe = flashlight_probe,
  212. .remove = flashlight_remove,
  213. .driver = {
  214. .name = FLASHLIGHT_NAME,
  215. .owner = THIS_MODULE,
  216. },
  217. };
  218. static int __init flashlight_init(void)
  219. {
  220. pr_debug("%s\n", __func__);
  221. return platform_driver_register(&flashlight_driver);
  222. }
  223. static void __exit flashlight_exit(void)
  224. {
  225. pr_debug("%s\n", __func__);
  226. platform_driver_unregister(&flashlight_driver);
  227. }
  228. module_init(flashlight_init);
  229. module_exit(flashlight_exit);
  230. MODULE_AUTHOR("Zion Huang <zion_huang@htc.com>");
  231. MODULE_DESCRIPTION("flash light driver");
  232. MODULE_LICENSE("GPL");