]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/leds/led-class-flash.c
Revert "i2c: core: Dispose OF IRQ mapping at client removal time"
[karo-tx-linux.git] / drivers / leds / led-class-flash.c
1 /*
2  * LED Flash class interface
3  *
4  * Copyright (C) 2015 Samsung Electronics Co., Ltd.
5  * Author: Jacek Anaszewski <j.anaszewski@samsung.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11
12 #include <linux/device.h>
13 #include <linux/init.h>
14 #include <linux/led-class-flash.h>
15 #include <linux/leds.h>
16 #include <linux/module.h>
17 #include <linux/slab.h>
18 #include "leds.h"
19
20 #define has_flash_op(fled_cdev, op)                             \
21         (fled_cdev && fled_cdev->ops->op)
22
23 #define call_flash_op(fled_cdev, op, args...)           \
24         ((has_flash_op(fled_cdev, op)) ?                        \
25                         (fled_cdev->ops->op(fled_cdev, args)) : \
26                         -EINVAL)
27
28 static const char * const led_flash_fault_names[] = {
29         "led-over-voltage",
30         "flash-timeout-exceeded",
31         "controller-over-temperature",
32         "controller-short-circuit",
33         "led-power-supply-over-current",
34         "indicator-led-fault",
35         "led-under-voltage",
36         "controller-under-voltage",
37         "led-over-temperature",
38 };
39
40 static ssize_t flash_brightness_store(struct device *dev,
41                 struct device_attribute *attr, const char *buf, size_t size)
42 {
43         struct led_classdev *led_cdev = dev_get_drvdata(dev);
44         struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
45         unsigned long state;
46         ssize_t ret;
47
48         mutex_lock(&led_cdev->led_access);
49
50         if (led_sysfs_is_disabled(led_cdev)) {
51                 ret = -EBUSY;
52                 goto unlock;
53         }
54
55         ret = kstrtoul(buf, 10, &state);
56         if (ret)
57                 goto unlock;
58
59         ret = led_set_flash_brightness(fled_cdev, state);
60         if (ret < 0)
61                 goto unlock;
62
63         ret = size;
64 unlock:
65         mutex_unlock(&led_cdev->led_access);
66         return ret;
67 }
68
69 static ssize_t flash_brightness_show(struct device *dev,
70                 struct device_attribute *attr, char *buf)
71 {
72         struct led_classdev *led_cdev = dev_get_drvdata(dev);
73         struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
74
75         /* no lock needed for this */
76         led_update_flash_brightness(fled_cdev);
77
78         return sprintf(buf, "%u\n", fled_cdev->brightness.val);
79 }
80 static DEVICE_ATTR_RW(flash_brightness);
81
82 static ssize_t max_flash_brightness_show(struct device *dev,
83                 struct device_attribute *attr, char *buf)
84 {
85         struct led_classdev *led_cdev = dev_get_drvdata(dev);
86         struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
87
88         return sprintf(buf, "%u\n", fled_cdev->brightness.max);
89 }
90 static DEVICE_ATTR_RO(max_flash_brightness);
91
92 static ssize_t flash_strobe_store(struct device *dev,
93                 struct device_attribute *attr, const char *buf, size_t size)
94 {
95         struct led_classdev *led_cdev = dev_get_drvdata(dev);
96         struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
97         unsigned long state;
98         ssize_t ret = -EINVAL;
99
100         mutex_lock(&led_cdev->led_access);
101
102         if (led_sysfs_is_disabled(led_cdev)) {
103                 ret = -EBUSY;
104                 goto unlock;
105         }
106
107         ret = kstrtoul(buf, 10, &state);
108         if (ret)
109                 goto unlock;
110
111         if (state < 0 || state > 1) {
112                 ret = -EINVAL;
113                 goto unlock;
114         }
115
116         ret = led_set_flash_strobe(fled_cdev, state);
117         if (ret < 0)
118                 goto unlock;
119         ret = size;
120 unlock:
121         mutex_unlock(&led_cdev->led_access);
122         return ret;
123 }
124
125 static ssize_t flash_strobe_show(struct device *dev,
126                 struct device_attribute *attr, char *buf)
127 {
128         struct led_classdev *led_cdev = dev_get_drvdata(dev);
129         struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
130         bool state;
131         int ret;
132
133         /* no lock needed for this */
134         ret = led_get_flash_strobe(fled_cdev, &state);
135         if (ret < 0)
136                 return ret;
137
138         return sprintf(buf, "%u\n", state);
139 }
140 static DEVICE_ATTR_RW(flash_strobe);
141
142 static ssize_t flash_timeout_store(struct device *dev,
143                 struct device_attribute *attr, const char *buf, size_t size)
144 {
145         struct led_classdev *led_cdev = dev_get_drvdata(dev);
146         struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
147         unsigned long flash_timeout;
148         ssize_t ret;
149
150         mutex_lock(&led_cdev->led_access);
151
152         if (led_sysfs_is_disabled(led_cdev)) {
153                 ret = -EBUSY;
154                 goto unlock;
155         }
156
157         ret = kstrtoul(buf, 10, &flash_timeout);
158         if (ret)
159                 goto unlock;
160
161         ret = led_set_flash_timeout(fled_cdev, flash_timeout);
162         if (ret < 0)
163                 goto unlock;
164
165         ret = size;
166 unlock:
167         mutex_unlock(&led_cdev->led_access);
168         return ret;
169 }
170
171 static ssize_t flash_timeout_show(struct device *dev,
172                 struct device_attribute *attr, char *buf)
173 {
174         struct led_classdev *led_cdev = dev_get_drvdata(dev);
175         struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
176
177         return sprintf(buf, "%u\n", fled_cdev->timeout.val);
178 }
179 static DEVICE_ATTR_RW(flash_timeout);
180
181 static ssize_t max_flash_timeout_show(struct device *dev,
182                 struct device_attribute *attr, char *buf)
183 {
184         struct led_classdev *led_cdev = dev_get_drvdata(dev);
185         struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
186
187         return sprintf(buf, "%u\n", fled_cdev->timeout.max);
188 }
189 static DEVICE_ATTR_RO(max_flash_timeout);
190
191 static ssize_t flash_fault_show(struct device *dev,
192                 struct device_attribute *attr, char *buf)
193 {
194         struct led_classdev *led_cdev = dev_get_drvdata(dev);
195         struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
196         u32 fault, mask = 0x1;
197         char *pbuf = buf;
198         int i, ret, buf_len;
199
200         ret = led_get_flash_fault(fled_cdev, &fault);
201         if (ret < 0)
202                 return -EINVAL;
203
204         *buf = '\0';
205
206         for (i = 0; i < LED_NUM_FLASH_FAULTS; ++i) {
207                 if (fault & mask) {
208                         buf_len = sprintf(pbuf, "%s ",
209                                           led_flash_fault_names[i]);
210                         pbuf += buf_len;
211                 }
212                 mask <<= 1;
213         }
214
215         return sprintf(buf, "%s\n", buf);
216 }
217 static DEVICE_ATTR_RO(flash_fault);
218
219 static ssize_t available_sync_leds_show(struct device *dev,
220                 struct device_attribute *attr, char *buf)
221 {
222         struct led_classdev *led_cdev = dev_get_drvdata(dev);
223         struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
224         char *pbuf = buf;
225         int i, buf_len;
226
227         buf_len = sprintf(pbuf, "[0: none] ");
228         pbuf += buf_len;
229
230         for (i = 0; i < fled_cdev->num_sync_leds; ++i) {
231                 buf_len = sprintf(pbuf, "[%d: %s] ", i + 1,
232                                   fled_cdev->sync_leds[i]->led_cdev.name);
233                 pbuf += buf_len;
234         }
235
236         return sprintf(buf, "%s\n", buf);
237 }
238 static DEVICE_ATTR_RO(available_sync_leds);
239
240 static ssize_t flash_sync_strobe_store(struct device *dev,
241                 struct device_attribute *attr, const char *buf, size_t size)
242 {
243         struct led_classdev *led_cdev = dev_get_drvdata(dev);
244         struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
245         unsigned long led_id;
246         ssize_t ret;
247
248         mutex_lock(&led_cdev->led_access);
249
250         if (led_sysfs_is_disabled(led_cdev)) {
251                 ret = -EBUSY;
252                 goto unlock;
253         }
254
255         ret = kstrtoul(buf, 10, &led_id);
256         if (ret)
257                 goto unlock;
258
259         if (led_id > fled_cdev->num_sync_leds) {
260                 ret = -ERANGE;
261                 goto unlock;
262         }
263
264         fled_cdev->sync_led_id = led_id;
265
266         ret = size;
267 unlock:
268         mutex_unlock(&led_cdev->led_access);
269         return ret;
270 }
271
272 static ssize_t flash_sync_strobe_show(struct device *dev,
273                 struct device_attribute *attr, char *buf)
274 {
275         struct led_classdev *led_cdev = dev_get_drvdata(dev);
276         struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
277         int sled_id = fled_cdev->sync_led_id;
278         char *sync_led_name = "none";
279
280         if (fled_cdev->sync_led_id > 0)
281                 sync_led_name = (char *)
282                         fled_cdev->sync_leds[sled_id - 1]->led_cdev.name;
283
284         return sprintf(buf, "[%d: %s]\n", sled_id, sync_led_name);
285 }
286 static DEVICE_ATTR_RW(flash_sync_strobe);
287
288 static struct attribute *led_flash_strobe_attrs[] = {
289         &dev_attr_flash_strobe.attr,
290         NULL,
291 };
292
293 static struct attribute *led_flash_timeout_attrs[] = {
294         &dev_attr_flash_timeout.attr,
295         &dev_attr_max_flash_timeout.attr,
296         NULL,
297 };
298
299 static struct attribute *led_flash_brightness_attrs[] = {
300         &dev_attr_flash_brightness.attr,
301         &dev_attr_max_flash_brightness.attr,
302         NULL,
303 };
304
305 static struct attribute *led_flash_fault_attrs[] = {
306         &dev_attr_flash_fault.attr,
307         NULL,
308 };
309
310 static struct attribute *led_flash_sync_strobe_attrs[] = {
311         &dev_attr_available_sync_leds.attr,
312         &dev_attr_flash_sync_strobe.attr,
313         NULL,
314 };
315
316 static const struct attribute_group led_flash_strobe_group = {
317         .attrs = led_flash_strobe_attrs,
318 };
319
320 static const struct attribute_group led_flash_timeout_group = {
321         .attrs = led_flash_timeout_attrs,
322 };
323
324 static const struct attribute_group led_flash_brightness_group = {
325         .attrs = led_flash_brightness_attrs,
326 };
327
328 static const struct attribute_group led_flash_fault_group = {
329         .attrs = led_flash_fault_attrs,
330 };
331
332 static const struct attribute_group led_flash_sync_strobe_group = {
333         .attrs = led_flash_sync_strobe_attrs,
334 };
335
336 static void led_flash_resume(struct led_classdev *led_cdev)
337 {
338         struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
339
340         call_flash_op(fled_cdev, flash_brightness_set,
341                                         fled_cdev->brightness.val);
342         call_flash_op(fled_cdev, timeout_set, fled_cdev->timeout.val);
343 }
344
345 static void led_flash_init_sysfs_groups(struct led_classdev_flash *fled_cdev)
346 {
347         struct led_classdev *led_cdev = &fled_cdev->led_cdev;
348         const struct led_flash_ops *ops = fled_cdev->ops;
349         const struct attribute_group **flash_groups = fled_cdev->sysfs_groups;
350
351         int num_sysfs_groups = 0;
352
353         flash_groups[num_sysfs_groups++] = &led_flash_strobe_group;
354
355         if (ops->flash_brightness_set)
356                 flash_groups[num_sysfs_groups++] = &led_flash_brightness_group;
357
358         if (ops->timeout_set)
359                 flash_groups[num_sysfs_groups++] = &led_flash_timeout_group;
360
361         if (ops->fault_get)
362                 flash_groups[num_sysfs_groups++] = &led_flash_fault_group;
363
364         if (led_cdev->flags & LED_DEV_CAP_SYNC_STROBE)
365                 flash_groups[num_sysfs_groups++] = &led_flash_sync_strobe_group;
366
367         led_cdev->groups = flash_groups;
368 }
369
370 int led_classdev_flash_register(struct device *parent,
371                                 struct led_classdev_flash *fled_cdev)
372 {
373         struct led_classdev *led_cdev;
374         const struct led_flash_ops *ops;
375         int ret;
376
377         if (!fled_cdev)
378                 return -EINVAL;
379
380         led_cdev = &fled_cdev->led_cdev;
381
382         if (led_cdev->flags & LED_DEV_CAP_FLASH) {
383                 if (!led_cdev->brightness_set_sync)
384                         return -EINVAL;
385
386                 ops = fled_cdev->ops;
387                 if (!ops || !ops->strobe_set)
388                         return -EINVAL;
389
390                 led_cdev->flash_resume = led_flash_resume;
391
392                 /* Select the sysfs attributes to be created for the device */
393                 led_flash_init_sysfs_groups(fled_cdev);
394         }
395
396         /* Register led class device */
397         ret = led_classdev_register(parent, led_cdev);
398         if (ret < 0)
399                 return ret;
400
401         /* Setting a torch brightness needs to have immediate effect */
402         led_cdev->flags &= ~SET_BRIGHTNESS_ASYNC;
403         led_cdev->flags |= SET_BRIGHTNESS_SYNC;
404
405         return 0;
406 }
407 EXPORT_SYMBOL_GPL(led_classdev_flash_register);
408
409 void led_classdev_flash_unregister(struct led_classdev_flash *fled_cdev)
410 {
411         if (!fled_cdev)
412                 return;
413
414         led_classdev_unregister(&fled_cdev->led_cdev);
415 }
416 EXPORT_SYMBOL_GPL(led_classdev_flash_unregister);
417
418 static void led_clamp_align(struct led_flash_setting *s)
419 {
420         u32 v, offset;
421
422         v = s->val + s->step / 2;
423         v = clamp(v, s->min, s->max);
424         offset = v - s->min;
425         offset = s->step * (offset / s->step);
426         s->val = s->min + offset;
427 }
428
429 int led_set_flash_timeout(struct led_classdev_flash *fled_cdev, u32 timeout)
430 {
431         struct led_classdev *led_cdev = &fled_cdev->led_cdev;
432         struct led_flash_setting *s = &fled_cdev->timeout;
433
434         s->val = timeout;
435         led_clamp_align(s);
436
437         if (!(led_cdev->flags & LED_SUSPENDED))
438                 return call_flash_op(fled_cdev, timeout_set, s->val);
439
440         return 0;
441 }
442 EXPORT_SYMBOL_GPL(led_set_flash_timeout);
443
444 int led_get_flash_fault(struct led_classdev_flash *fled_cdev, u32 *fault)
445 {
446         return call_flash_op(fled_cdev, fault_get, fault);
447 }
448 EXPORT_SYMBOL_GPL(led_get_flash_fault);
449
450 int led_set_flash_brightness(struct led_classdev_flash *fled_cdev,
451                                 u32 brightness)
452 {
453         struct led_classdev *led_cdev = &fled_cdev->led_cdev;
454         struct led_flash_setting *s = &fled_cdev->brightness;
455
456         s->val = brightness;
457         led_clamp_align(s);
458
459         if (!(led_cdev->flags & LED_SUSPENDED))
460                 return call_flash_op(fled_cdev, flash_brightness_set, s->val);
461
462         return 0;
463 }
464 EXPORT_SYMBOL_GPL(led_set_flash_brightness);
465
466 int led_update_flash_brightness(struct led_classdev_flash *fled_cdev)
467 {
468         struct led_flash_setting *s = &fled_cdev->brightness;
469         u32 brightness;
470
471         if (has_flash_op(fled_cdev, flash_brightness_get)) {
472                 int ret = call_flash_op(fled_cdev, flash_brightness_get,
473                                                 &brightness);
474                 if (ret < 0)
475                         return ret;
476
477                 s->val = brightness;
478         }
479
480         return 0;
481 }
482 EXPORT_SYMBOL_GPL(led_update_flash_brightness);
483
484 MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
485 MODULE_DESCRIPTION("LED Flash class interface");
486 MODULE_LICENSE("GPL v2");