diff options
author | Jacek Anaszewski <j.anaszewski@samsung.com> | 2015-04-30 06:34:57 -0400 |
---|---|---|
committer | Bryan Wu <cooloney@gmail.com> | 2015-05-04 14:21:16 -0400 |
commit | 49c34b8e0f43a6a9179b9d9894868b911da8501e (patch) | |
tree | b9d318bcaea2ffdb3c9d76f1d355c25f90765553 | |
parent | 2269fe093197ade0202128cbd08d8e1c253f1fcb (diff) |
leds: Add driver for AAT1290 flash LED controller
This patch adds a driver for the 1.5A Step-Up Current Regulator
for Flash LEDs. The device is programmed through a Skyworks proprietary
AS2Cwire serial digital interface.
Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Cc: Richard Purdie <rpurdie@rpsys.net>
Signed-off-by: Bryan Wu <cooloney@gmail.com>
-rw-r--r-- | drivers/leds/Kconfig | 8 | ||||
-rw-r--r-- | drivers/leds/Makefile | 1 | ||||
-rw-r--r-- | drivers/leds/leds-aat1290.c | 451 |
3 files changed, 460 insertions, 0 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 62be033100b6..dd7834c32608 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig | |||
@@ -42,6 +42,14 @@ config LEDS_88PM860X | |||
42 | This option enables support for on-chip LED drivers found on Marvell | 42 | This option enables support for on-chip LED drivers found on Marvell |
43 | Semiconductor 88PM8606 PMIC. | 43 | Semiconductor 88PM8606 PMIC. |
44 | 44 | ||
45 | config LEDS_AAT1290 | ||
46 | tristate "LED support for the AAT1290" | ||
47 | depends on LEDS_CLASS_FLASH | ||
48 | depends on GPIOLIB | ||
49 | depends on OF | ||
50 | help | ||
51 | This option enables support for the LEDs on the AAT1290. | ||
52 | |||
45 | config LEDS_LM3530 | 53 | config LEDS_LM3530 |
46 | tristate "LCD Backlight driver for LM3530" | 54 | tristate "LCD Backlight driver for LM3530" |
47 | depends on LEDS_CLASS | 55 | depends on LEDS_CLASS |
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 8bddae6b8ac2..71f7c9accb94 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile | |||
@@ -7,6 +7,7 @@ obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o | |||
7 | 7 | ||
8 | # LED Platform Drivers | 8 | # LED Platform Drivers |
9 | obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o | 9 | obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o |
10 | obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o | ||
10 | obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o | 11 | obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o |
11 | obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o | 12 | obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o |
12 | obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o | 13 | obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o |
diff --git a/drivers/leds/leds-aat1290.c b/drivers/leds/leds-aat1290.c new file mode 100644 index 000000000000..6ea1d5465309 --- /dev/null +++ b/drivers/leds/leds-aat1290.c | |||
@@ -0,0 +1,451 @@ | |||
1 | /* | ||
2 | * LED Flash class driver for the AAT1290 | ||
3 | * 1.5A Step-Up Current Regulator for Flash LEDs | ||
4 | * | ||
5 | * Copyright (C) 2015, Samsung Electronics Co., Ltd. | ||
6 | * Author: Jacek Anaszewski <j.anaszewski@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/delay.h> | ||
14 | #include <linux/gpio/consumer.h> | ||
15 | #include <linux/led-class-flash.h> | ||
16 | #include <linux/leds.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/mutex.h> | ||
19 | #include <linux/of.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/workqueue.h> | ||
23 | |||
24 | #define AAT1290_MOVIE_MODE_CURRENT_ADDR 17 | ||
25 | #define AAT1290_MAX_MM_CURR_PERCENT_0 16 | ||
26 | #define AAT1290_MAX_MM_CURR_PERCENT_100 1 | ||
27 | |||
28 | #define AAT1290_FLASH_SAFETY_TIMER_ADDR 18 | ||
29 | |||
30 | #define AAT1290_MOVIE_MODE_CONFIG_ADDR 19 | ||
31 | #define AAT1290_MOVIE_MODE_OFF 1 | ||
32 | #define AAT1290_MOVIE_MODE_ON 3 | ||
33 | |||
34 | #define AAT1290_MM_CURRENT_RATIO_ADDR 20 | ||
35 | #define AAT1290_MM_TO_FL_1_92 1 | ||
36 | |||
37 | #define AAT1290_MM_TO_FL_RATIO 1000 / 1920 | ||
38 | #define AAT1290_MAX_MM_CURRENT(fl_max) (fl_max * AAT1290_MM_TO_FL_RATIO) | ||
39 | |||
40 | #define AAT1290_LATCH_TIME_MIN_US 500 | ||
41 | #define AAT1290_LATCH_TIME_MAX_US 1000 | ||
42 | #define AAT1290_EN_SET_TICK_TIME_US 1 | ||
43 | #define AAT1290_FLEN_OFF_DELAY_TIME_US 10 | ||
44 | #define AAT1290_FLASH_TM_NUM_LEVELS 16 | ||
45 | #define AAT1290_MM_CURRENT_SCALE_SIZE 15 | ||
46 | |||
47 | |||
48 | struct aat1290_led_config_data { | ||
49 | /* maximum LED current in movie mode */ | ||
50 | u32 max_mm_current; | ||
51 | /* maximum LED current in flash mode */ | ||
52 | u32 max_flash_current; | ||
53 | /* maximum flash timeout */ | ||
54 | u32 max_flash_tm; | ||
55 | /* max LED brightness level */ | ||
56 | enum led_brightness max_brightness; | ||
57 | }; | ||
58 | |||
59 | struct aat1290_led { | ||
60 | /* platform device data */ | ||
61 | struct platform_device *pdev; | ||
62 | /* secures access to the device */ | ||
63 | struct mutex lock; | ||
64 | |||
65 | /* corresponding LED Flash class device */ | ||
66 | struct led_classdev_flash fled_cdev; | ||
67 | |||
68 | /* FLEN pin */ | ||
69 | struct gpio_desc *gpio_fl_en; | ||
70 | /* EN|SET pin */ | ||
71 | struct gpio_desc *gpio_en_set; | ||
72 | /* movie mode current scale */ | ||
73 | int *mm_current_scale; | ||
74 | /* device mode */ | ||
75 | bool movie_mode; | ||
76 | |||
77 | /* brightness cache */ | ||
78 | unsigned int torch_brightness; | ||
79 | /* assures led-triggers compatibility */ | ||
80 | struct work_struct work_brightness_set; | ||
81 | }; | ||
82 | |||
83 | static struct aat1290_led *fled_cdev_to_led( | ||
84 | struct led_classdev_flash *fled_cdev) | ||
85 | { | ||
86 | return container_of(fled_cdev, struct aat1290_led, fled_cdev); | ||
87 | } | ||
88 | |||
89 | static void aat1290_as2cwire_write(struct aat1290_led *led, int addr, int value) | ||
90 | { | ||
91 | int i; | ||
92 | |||
93 | gpiod_direction_output(led->gpio_fl_en, 0); | ||
94 | gpiod_direction_output(led->gpio_en_set, 0); | ||
95 | |||
96 | udelay(AAT1290_FLEN_OFF_DELAY_TIME_US); | ||
97 | |||
98 | /* write address */ | ||
99 | for (i = 0; i < addr; ++i) { | ||
100 | udelay(AAT1290_EN_SET_TICK_TIME_US); | ||
101 | gpiod_direction_output(led->gpio_en_set, 0); | ||
102 | udelay(AAT1290_EN_SET_TICK_TIME_US); | ||
103 | gpiod_direction_output(led->gpio_en_set, 1); | ||
104 | } | ||
105 | |||
106 | usleep_range(AAT1290_LATCH_TIME_MIN_US, AAT1290_LATCH_TIME_MAX_US); | ||
107 | |||
108 | /* write data */ | ||
109 | for (i = 0; i < value; ++i) { | ||
110 | udelay(AAT1290_EN_SET_TICK_TIME_US); | ||
111 | gpiod_direction_output(led->gpio_en_set, 0); | ||
112 | udelay(AAT1290_EN_SET_TICK_TIME_US); | ||
113 | gpiod_direction_output(led->gpio_en_set, 1); | ||
114 | } | ||
115 | |||
116 | usleep_range(AAT1290_LATCH_TIME_MIN_US, AAT1290_LATCH_TIME_MAX_US); | ||
117 | } | ||
118 | |||
119 | static void aat1290_set_flash_safety_timer(struct aat1290_led *led, | ||
120 | unsigned int micro_sec) | ||
121 | { | ||
122 | struct led_classdev_flash *fled_cdev = &led->fled_cdev; | ||
123 | struct led_flash_setting *flash_tm = &fled_cdev->timeout; | ||
124 | int flash_tm_reg = AAT1290_FLASH_TM_NUM_LEVELS - | ||
125 | (micro_sec / flash_tm->step) + 1; | ||
126 | |||
127 | aat1290_as2cwire_write(led, AAT1290_FLASH_SAFETY_TIMER_ADDR, | ||
128 | flash_tm_reg); | ||
129 | } | ||
130 | |||
131 | static void aat1290_brightness_set(struct aat1290_led *led, | ||
132 | enum led_brightness brightness) | ||
133 | { | ||
134 | mutex_lock(&led->lock); | ||
135 | |||
136 | if (brightness == 0) { | ||
137 | gpiod_direction_output(led->gpio_fl_en, 0); | ||
138 | gpiod_direction_output(led->gpio_en_set, 0); | ||
139 | led->movie_mode = false; | ||
140 | } else { | ||
141 | if (!led->movie_mode) { | ||
142 | aat1290_as2cwire_write(led, | ||
143 | AAT1290_MM_CURRENT_RATIO_ADDR, | ||
144 | AAT1290_MM_TO_FL_1_92); | ||
145 | led->movie_mode = true; | ||
146 | } | ||
147 | |||
148 | aat1290_as2cwire_write(led, AAT1290_MOVIE_MODE_CURRENT_ADDR, | ||
149 | AAT1290_MAX_MM_CURR_PERCENT_0 - brightness); | ||
150 | aat1290_as2cwire_write(led, AAT1290_MOVIE_MODE_CONFIG_ADDR, | ||
151 | AAT1290_MOVIE_MODE_ON); | ||
152 | } | ||
153 | |||
154 | mutex_unlock(&led->lock); | ||
155 | } | ||
156 | |||
157 | /* LED subsystem callbacks */ | ||
158 | |||
159 | static void aat1290_brightness_set_work(struct work_struct *work) | ||
160 | { | ||
161 | struct aat1290_led *led = | ||
162 | container_of(work, struct aat1290_led, work_brightness_set); | ||
163 | |||
164 | aat1290_brightness_set(led, led->torch_brightness); | ||
165 | } | ||
166 | |||
167 | static void aat1290_led_brightness_set(struct led_classdev *led_cdev, | ||
168 | enum led_brightness brightness) | ||
169 | { | ||
170 | struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); | ||
171 | struct aat1290_led *led = fled_cdev_to_led(fled_cdev); | ||
172 | |||
173 | led->torch_brightness = brightness; | ||
174 | schedule_work(&led->work_brightness_set); | ||
175 | } | ||
176 | |||
177 | static int aat1290_led_brightness_set_sync(struct led_classdev *led_cdev, | ||
178 | enum led_brightness brightness) | ||
179 | { | ||
180 | struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); | ||
181 | struct aat1290_led *led = fled_cdev_to_led(fled_cdev); | ||
182 | |||
183 | aat1290_brightness_set(led, brightness); | ||
184 | |||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | static int aat1290_led_flash_strobe_set(struct led_classdev_flash *fled_cdev, | ||
189 | bool state) | ||
190 | |||
191 | { | ||
192 | struct aat1290_led *led = fled_cdev_to_led(fled_cdev); | ||
193 | struct led_classdev *led_cdev = &fled_cdev->led_cdev; | ||
194 | struct led_flash_setting *timeout = &fled_cdev->timeout; | ||
195 | |||
196 | mutex_lock(&led->lock); | ||
197 | |||
198 | if (state) { | ||
199 | aat1290_set_flash_safety_timer(led, timeout->val); | ||
200 | gpiod_direction_output(led->gpio_fl_en, 1); | ||
201 | } else { | ||
202 | gpiod_direction_output(led->gpio_fl_en, 0); | ||
203 | gpiod_direction_output(led->gpio_en_set, 0); | ||
204 | } | ||
205 | |||
206 | /* | ||
207 | * To reenter movie mode after a flash event the part must be cycled | ||
208 | * off and back on to reset the movie mode and reprogrammed via the | ||
209 | * AS2Cwire. Therefore the brightness and movie_mode properties needs | ||
210 | * to be updated here to reflect the actual state. | ||
211 | */ | ||
212 | led_cdev->brightness = 0; | ||
213 | led->movie_mode = false; | ||
214 | |||
215 | mutex_unlock(&led->lock); | ||
216 | |||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | static int aat1290_led_flash_timeout_set(struct led_classdev_flash *fled_cdev, | ||
221 | u32 timeout) | ||
222 | { | ||
223 | /* | ||
224 | * Don't do anything - flash timeout is cached in the led-class-flash | ||
225 | * core and will be applied in the strobe_set op, as writing the | ||
226 | * safety timer register spuriously turns the torch mode on. | ||
227 | */ | ||
228 | |||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | static int aat1290_led_parse_dt(struct aat1290_led *led, | ||
233 | struct aat1290_led_config_data *cfg) | ||
234 | { | ||
235 | struct led_classdev *led_cdev = &led->fled_cdev.led_cdev; | ||
236 | struct device *dev = &led->pdev->dev; | ||
237 | struct device_node *child_node; | ||
238 | int ret = 0; | ||
239 | |||
240 | led->gpio_fl_en = devm_gpiod_get(dev, "flen"); | ||
241 | if (IS_ERR(led->gpio_fl_en)) { | ||
242 | ret = PTR_ERR(led->gpio_fl_en); | ||
243 | dev_err(dev, "Unable to claim gpio \"flen\".\n"); | ||
244 | return ret; | ||
245 | } | ||
246 | |||
247 | led->gpio_en_set = devm_gpiod_get(dev, "enset"); | ||
248 | if (IS_ERR(led->gpio_en_set)) { | ||
249 | ret = PTR_ERR(led->gpio_en_set); | ||
250 | dev_err(dev, "Unable to claim gpio \"enset\".\n"); | ||
251 | return ret; | ||
252 | } | ||
253 | |||
254 | child_node = of_get_next_available_child(dev->of_node, NULL); | ||
255 | if (!child_node) { | ||
256 | dev_err(dev, "No DT child node found for connected LED.\n"); | ||
257 | return -EINVAL; | ||
258 | } | ||
259 | |||
260 | led_cdev->name = of_get_property(child_node, "label", NULL) ? : | ||
261 | child_node->name; | ||
262 | |||
263 | ret = of_property_read_u32(child_node, "led-max-microamp", | ||
264 | &cfg->max_mm_current); | ||
265 | /* | ||
266 | * led-max-microamp will default to 1/20 of flash-max-microamp | ||
267 | * in case it is missing. | ||
268 | */ | ||
269 | if (ret < 0) | ||
270 | dev_warn(dev, | ||
271 | "led-max-microamp DT property missing\n"); | ||
272 | |||
273 | ret = of_property_read_u32(child_node, "flash-max-microamp", | ||
274 | &cfg->max_flash_current); | ||
275 | if (ret < 0) { | ||
276 | dev_err(dev, | ||
277 | "flash-max-microamp DT property missing\n"); | ||
278 | return ret; | ||
279 | } | ||
280 | |||
281 | ret = of_property_read_u32(child_node, "flash-max-timeout-us", | ||
282 | &cfg->max_flash_tm); | ||
283 | if (ret < 0) { | ||
284 | dev_err(dev, | ||
285 | "flash-max-timeout-us DT property missing\n"); | ||
286 | return ret; | ||
287 | } | ||
288 | |||
289 | of_node_put(child_node); | ||
290 | |||
291 | return ret; | ||
292 | } | ||
293 | |||
294 | static void aat1290_led_validate_mm_current(struct aat1290_led *led, | ||
295 | struct aat1290_led_config_data *cfg) | ||
296 | { | ||
297 | int i, b = 0, e = AAT1290_MM_CURRENT_SCALE_SIZE; | ||
298 | |||
299 | while (e - b > 1) { | ||
300 | i = b + (e - b) / 2; | ||
301 | if (cfg->max_mm_current < led->mm_current_scale[i]) | ||
302 | e = i; | ||
303 | else | ||
304 | b = i; | ||
305 | } | ||
306 | |||
307 | cfg->max_mm_current = led->mm_current_scale[b]; | ||
308 | cfg->max_brightness = b + 1; | ||
309 | } | ||
310 | |||
311 | int init_mm_current_scale(struct aat1290_led *led, | ||
312 | struct aat1290_led_config_data *cfg) | ||
313 | { | ||
314 | int max_mm_current_percent[] = { 20, 22, 25, 28, 32, 36, 40, 45, 50, 56, | ||
315 | 63, 71, 79, 89, 100 }; | ||
316 | int i, max_mm_current = | ||
317 | AAT1290_MAX_MM_CURRENT(cfg->max_flash_current); | ||
318 | |||
319 | led->mm_current_scale = kzalloc(sizeof(max_mm_current_percent), | ||
320 | GFP_KERNEL); | ||
321 | if (!led->mm_current_scale) | ||
322 | return -ENOMEM; | ||
323 | |||
324 | for (i = 0; i < AAT1290_MM_CURRENT_SCALE_SIZE; ++i) | ||
325 | led->mm_current_scale[i] = max_mm_current * | ||
326 | max_mm_current_percent[i] / 100; | ||
327 | |||
328 | return 0; | ||
329 | } | ||
330 | |||
331 | static int aat1290_led_get_configuration(struct aat1290_led *led, | ||
332 | struct aat1290_led_config_data *cfg) | ||
333 | { | ||
334 | int ret; | ||
335 | |||
336 | ret = aat1290_led_parse_dt(led, cfg); | ||
337 | if (ret < 0) | ||
338 | return ret; | ||
339 | /* | ||
340 | * Init non-linear movie mode current scale basing | ||
341 | * on the max flash current from led configuration. | ||
342 | */ | ||
343 | ret = init_mm_current_scale(led, cfg); | ||
344 | if (ret < 0) | ||
345 | return ret; | ||
346 | |||
347 | aat1290_led_validate_mm_current(led, cfg); | ||
348 | |||
349 | kfree(led->mm_current_scale); | ||
350 | |||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | static void aat1290_init_flash_timeout(struct aat1290_led *led, | ||
355 | struct aat1290_led_config_data *cfg) | ||
356 | { | ||
357 | struct led_classdev_flash *fled_cdev = &led->fled_cdev; | ||
358 | struct led_flash_setting *setting; | ||
359 | |||
360 | /* Init flash timeout setting */ | ||
361 | setting = &fled_cdev->timeout; | ||
362 | setting->min = cfg->max_flash_tm / AAT1290_FLASH_TM_NUM_LEVELS; | ||
363 | setting->max = cfg->max_flash_tm; | ||
364 | setting->step = setting->min; | ||
365 | setting->val = setting->max; | ||
366 | } | ||
367 | |||
368 | static const struct led_flash_ops flash_ops = { | ||
369 | .strobe_set = aat1290_led_flash_strobe_set, | ||
370 | .timeout_set = aat1290_led_flash_timeout_set, | ||
371 | }; | ||
372 | |||
373 | static int aat1290_led_probe(struct platform_device *pdev) | ||
374 | { | ||
375 | struct device *dev = &pdev->dev; | ||
376 | struct aat1290_led *led; | ||
377 | struct led_classdev *led_cdev; | ||
378 | struct led_classdev_flash *fled_cdev; | ||
379 | struct aat1290_led_config_data led_cfg = {}; | ||
380 | int ret; | ||
381 | |||
382 | led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); | ||
383 | if (!led) | ||
384 | return -ENOMEM; | ||
385 | |||
386 | led->pdev = pdev; | ||
387 | platform_set_drvdata(pdev, led); | ||
388 | |||
389 | fled_cdev = &led->fled_cdev; | ||
390 | fled_cdev->ops = &flash_ops; | ||
391 | led_cdev = &fled_cdev->led_cdev; | ||
392 | |||
393 | ret = aat1290_led_get_configuration(led, &led_cfg); | ||
394 | if (ret < 0) | ||
395 | return ret; | ||
396 | |||
397 | mutex_init(&led->lock); | ||
398 | |||
399 | /* Initialize LED Flash class device */ | ||
400 | led_cdev->brightness_set = aat1290_led_brightness_set; | ||
401 | led_cdev->brightness_set_sync = aat1290_led_brightness_set_sync; | ||
402 | led_cdev->max_brightness = led_cfg.max_brightness; | ||
403 | led_cdev->flags |= LED_DEV_CAP_FLASH; | ||
404 | INIT_WORK(&led->work_brightness_set, aat1290_brightness_set_work); | ||
405 | |||
406 | aat1290_init_flash_timeout(led, &led_cfg); | ||
407 | |||
408 | /* Register LED Flash class device */ | ||
409 | ret = led_classdev_flash_register(&pdev->dev, fled_cdev); | ||
410 | if (ret < 0) | ||
411 | goto err_flash_register; | ||
412 | |||
413 | return 0; | ||
414 | |||
415 | err_flash_register: | ||
416 | mutex_destroy(&led->lock); | ||
417 | |||
418 | return ret; | ||
419 | } | ||
420 | |||
421 | static int aat1290_led_remove(struct platform_device *pdev) | ||
422 | { | ||
423 | struct aat1290_led *led = platform_get_drvdata(pdev); | ||
424 | |||
425 | led_classdev_flash_unregister(&led->fled_cdev); | ||
426 | cancel_work_sync(&led->work_brightness_set); | ||
427 | |||
428 | mutex_destroy(&led->lock); | ||
429 | |||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | static const struct of_device_id aat1290_led_dt_match[] = { | ||
434 | { .compatible = "skyworks,aat1290" }, | ||
435 | {}, | ||
436 | }; | ||
437 | |||
438 | static struct platform_driver aat1290_led_driver = { | ||
439 | .probe = aat1290_led_probe, | ||
440 | .remove = aat1290_led_remove, | ||
441 | .driver = { | ||
442 | .name = "aat1290", | ||
443 | .of_match_table = aat1290_led_dt_match, | ||
444 | }, | ||
445 | }; | ||
446 | |||
447 | module_platform_driver(aat1290_led_driver); | ||
448 | |||
449 | MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>"); | ||
450 | MODULE_DESCRIPTION("Skyworks Current Regulator for Flash LEDs"); | ||
451 | MODULE_LICENSE("GPL v2"); | ||