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