]> Git Repo - linux.git/blob - drivers/leds/led-core.c
Merge tag 'kbuild-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy...
[linux.git] / drivers / leds / led-core.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * LED Class Core
4  *
5  * Copyright 2005-2006 Openedhand Ltd.
6  *
7  * Author: Richard Purdie <[email protected]>
8  */
9
10 #include <linux/kernel.h>
11 #include <linux/led-class-multicolor.h>
12 #include <linux/leds.h>
13 #include <linux/list.h>
14 #include <linux/module.h>
15 #include <linux/mutex.h>
16 #include <linux/of.h>
17 #include <linux/property.h>
18 #include <linux/rwsem.h>
19 #include <linux/slab.h>
20 #include <uapi/linux/uleds.h>
21 #include "leds.h"
22
23 DECLARE_RWSEM(leds_list_lock);
24 EXPORT_SYMBOL_GPL(leds_list_lock);
25
26 LIST_HEAD(leds_list);
27 EXPORT_SYMBOL_GPL(leds_list);
28
29 static const char * const led_colors[LED_COLOR_ID_MAX] = {
30         [LED_COLOR_ID_WHITE] = "white",
31         [LED_COLOR_ID_RED] = "red",
32         [LED_COLOR_ID_GREEN] = "green",
33         [LED_COLOR_ID_BLUE] = "blue",
34         [LED_COLOR_ID_AMBER] = "amber",
35         [LED_COLOR_ID_VIOLET] = "violet",
36         [LED_COLOR_ID_YELLOW] = "yellow",
37         [LED_COLOR_ID_IR] = "ir",
38         [LED_COLOR_ID_MULTI] = "multicolor",
39         [LED_COLOR_ID_RGB] = "rgb",
40         [LED_COLOR_ID_PURPLE] = "purple",
41         [LED_COLOR_ID_ORANGE] = "orange",
42         [LED_COLOR_ID_PINK] = "pink",
43         [LED_COLOR_ID_CYAN] = "cyan",
44         [LED_COLOR_ID_LIME] = "lime",
45 };
46
47 static int __led_set_brightness(struct led_classdev *led_cdev, unsigned int value)
48 {
49         if (!led_cdev->brightness_set)
50                 return -ENOTSUPP;
51
52         led_cdev->brightness_set(led_cdev, value);
53
54         return 0;
55 }
56
57 static int __led_set_brightness_blocking(struct led_classdev *led_cdev, unsigned int value)
58 {
59         if (!led_cdev->brightness_set_blocking)
60                 return -ENOTSUPP;
61
62         return led_cdev->brightness_set_blocking(led_cdev, value);
63 }
64
65 static void led_timer_function(struct timer_list *t)
66 {
67         struct led_classdev *led_cdev = from_timer(led_cdev, t, blink_timer);
68         unsigned long brightness;
69         unsigned long delay;
70
71         if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
72                 led_set_brightness_nosleep(led_cdev, LED_OFF);
73                 clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
74                 return;
75         }
76
77         if (test_and_clear_bit(LED_BLINK_ONESHOT_STOP,
78                                &led_cdev->work_flags)) {
79                 clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
80                 return;
81         }
82
83         brightness = led_get_brightness(led_cdev);
84         if (!brightness) {
85                 /* Time to switch the LED on. */
86                 if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE,
87                                         &led_cdev->work_flags))
88                         brightness = led_cdev->new_blink_brightness;
89                 else
90                         brightness = led_cdev->blink_brightness;
91                 delay = led_cdev->blink_delay_on;
92         } else {
93                 /* Store the current brightness value to be able
94                  * to restore it when the delay_off period is over.
95                  */
96                 led_cdev->blink_brightness = brightness;
97                 brightness = LED_OFF;
98                 delay = led_cdev->blink_delay_off;
99         }
100
101         led_set_brightness_nosleep(led_cdev, brightness);
102
103         /* Return in next iteration if led is in one-shot mode and we are in
104          * the final blink state so that the led is toggled each delay_on +
105          * delay_off milliseconds in worst case.
106          */
107         if (test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags)) {
108                 if (test_bit(LED_BLINK_INVERT, &led_cdev->work_flags)) {
109                         if (brightness)
110                                 set_bit(LED_BLINK_ONESHOT_STOP,
111                                         &led_cdev->work_flags);
112                 } else {
113                         if (!brightness)
114                                 set_bit(LED_BLINK_ONESHOT_STOP,
115                                         &led_cdev->work_flags);
116                 }
117         }
118
119         mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
120 }
121
122 static void set_brightness_delayed_set_brightness(struct led_classdev *led_cdev,
123                                                   unsigned int value)
124 {
125         int ret;
126
127         ret = __led_set_brightness(led_cdev, value);
128         if (ret == -ENOTSUPP) {
129                 ret = __led_set_brightness_blocking(led_cdev, value);
130                 if (ret == -ENOTSUPP)
131                         /* No back-end support to set a fixed brightness value */
132                         return;
133         }
134
135         /* LED HW might have been unplugged, therefore don't warn */
136         if (ret == -ENODEV && led_cdev->flags & LED_UNREGISTERING &&
137             led_cdev->flags & LED_HW_PLUGGABLE)
138                 return;
139
140         if (ret < 0)
141                 dev_err(led_cdev->dev,
142                         "Setting an LED's brightness failed (%d)\n", ret);
143 }
144
145 static void set_brightness_delayed(struct work_struct *ws)
146 {
147         struct led_classdev *led_cdev =
148                 container_of(ws, struct led_classdev, set_brightness_work);
149
150         if (test_and_clear_bit(LED_BLINK_DISABLE, &led_cdev->work_flags)) {
151                 led_stop_software_blink(led_cdev);
152                 set_bit(LED_SET_BRIGHTNESS_OFF, &led_cdev->work_flags);
153         }
154
155         /*
156          * Triggers may call led_set_brightness(LED_OFF),
157          * led_set_brightness(LED_FULL) in quick succession to disable blinking
158          * and turn the LED on. Both actions may have been scheduled to run
159          * before this work item runs once. To make sure this works properly
160          * handle LED_SET_BRIGHTNESS_OFF first.
161          */
162         if (test_and_clear_bit(LED_SET_BRIGHTNESS_OFF, &led_cdev->work_flags))
163                 set_brightness_delayed_set_brightness(led_cdev, LED_OFF);
164
165         if (test_and_clear_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags))
166                 set_brightness_delayed_set_brightness(led_cdev, led_cdev->delayed_set_value);
167
168         if (test_and_clear_bit(LED_SET_BLINK, &led_cdev->work_flags)) {
169                 unsigned long delay_on = led_cdev->delayed_delay_on;
170                 unsigned long delay_off = led_cdev->delayed_delay_off;
171
172                 led_blink_set(led_cdev, &delay_on, &delay_off);
173         }
174 }
175
176 static void led_set_software_blink(struct led_classdev *led_cdev,
177                                    unsigned long delay_on,
178                                    unsigned long delay_off)
179 {
180         int current_brightness;
181
182         current_brightness = led_get_brightness(led_cdev);
183         if (current_brightness)
184                 led_cdev->blink_brightness = current_brightness;
185         if (!led_cdev->blink_brightness)
186                 led_cdev->blink_brightness = led_cdev->max_brightness;
187
188         led_cdev->blink_delay_on = delay_on;
189         led_cdev->blink_delay_off = delay_off;
190
191         /* never on - just set to off */
192         if (!delay_on) {
193                 led_set_brightness_nosleep(led_cdev, LED_OFF);
194                 return;
195         }
196
197         /* never off - just set to brightness */
198         if (!delay_off) {
199                 led_set_brightness_nosleep(led_cdev,
200                                            led_cdev->blink_brightness);
201                 return;
202         }
203
204         set_bit(LED_BLINK_SW, &led_cdev->work_flags);
205         mod_timer(&led_cdev->blink_timer, jiffies + 1);
206 }
207
208
209 static void led_blink_setup(struct led_classdev *led_cdev,
210                      unsigned long *delay_on,
211                      unsigned long *delay_off)
212 {
213         if (!test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
214             led_cdev->blink_set &&
215             !led_cdev->blink_set(led_cdev, delay_on, delay_off))
216                 return;
217
218         /* blink with 1 Hz as default if nothing specified */
219         if (!*delay_on && !*delay_off)
220                 *delay_on = *delay_off = 500;
221
222         led_set_software_blink(led_cdev, *delay_on, *delay_off);
223 }
224
225 void led_init_core(struct led_classdev *led_cdev)
226 {
227         INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed);
228
229         timer_setup(&led_cdev->blink_timer, led_timer_function, 0);
230 }
231 EXPORT_SYMBOL_GPL(led_init_core);
232
233 void led_blink_set(struct led_classdev *led_cdev,
234                    unsigned long *delay_on,
235                    unsigned long *delay_off)
236 {
237         del_timer_sync(&led_cdev->blink_timer);
238
239         clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
240         clear_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags);
241         clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags);
242
243         led_blink_setup(led_cdev, delay_on, delay_off);
244 }
245 EXPORT_SYMBOL_GPL(led_blink_set);
246
247 void led_blink_set_oneshot(struct led_classdev *led_cdev,
248                            unsigned long *delay_on,
249                            unsigned long *delay_off,
250                            int invert)
251 {
252         if (test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
253              timer_pending(&led_cdev->blink_timer))
254                 return;
255
256         set_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags);
257         clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags);
258
259         if (invert)
260                 set_bit(LED_BLINK_INVERT, &led_cdev->work_flags);
261         else
262                 clear_bit(LED_BLINK_INVERT, &led_cdev->work_flags);
263
264         led_blink_setup(led_cdev, delay_on, delay_off);
265 }
266 EXPORT_SYMBOL_GPL(led_blink_set_oneshot);
267
268 void led_blink_set_nosleep(struct led_classdev *led_cdev, unsigned long delay_on,
269                            unsigned long delay_off)
270 {
271         /* If necessary delegate to a work queue task. */
272         if (led_cdev->blink_set && led_cdev->brightness_set_blocking) {
273                 led_cdev->delayed_delay_on = delay_on;
274                 led_cdev->delayed_delay_off = delay_off;
275                 set_bit(LED_SET_BLINK, &led_cdev->work_flags);
276                 schedule_work(&led_cdev->set_brightness_work);
277                 return;
278         }
279
280         led_blink_set(led_cdev, &delay_on, &delay_off);
281 }
282 EXPORT_SYMBOL_GPL(led_blink_set_nosleep);
283
284 void led_stop_software_blink(struct led_classdev *led_cdev)
285 {
286         del_timer_sync(&led_cdev->blink_timer);
287         led_cdev->blink_delay_on = 0;
288         led_cdev->blink_delay_off = 0;
289         clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
290 }
291 EXPORT_SYMBOL_GPL(led_stop_software_blink);
292
293 void led_set_brightness(struct led_classdev *led_cdev, unsigned int brightness)
294 {
295         /*
296          * If software blink is active, delay brightness setting
297          * until the next timer tick.
298          */
299         if (test_bit(LED_BLINK_SW, &led_cdev->work_flags)) {
300                 /*
301                  * If we need to disable soft blinking delegate this to the
302                  * work queue task to avoid problems in case we are called
303                  * from hard irq context.
304                  */
305                 if (!brightness) {
306                         set_bit(LED_BLINK_DISABLE, &led_cdev->work_flags);
307                         schedule_work(&led_cdev->set_brightness_work);
308                 } else {
309                         set_bit(LED_BLINK_BRIGHTNESS_CHANGE,
310                                 &led_cdev->work_flags);
311                         led_cdev->new_blink_brightness = brightness;
312                 }
313                 return;
314         }
315
316         led_set_brightness_nosleep(led_cdev, brightness);
317 }
318 EXPORT_SYMBOL_GPL(led_set_brightness);
319
320 void led_set_brightness_nopm(struct led_classdev *led_cdev, unsigned int value)
321 {
322         /* Use brightness_set op if available, it is guaranteed not to sleep */
323         if (!__led_set_brightness(led_cdev, value))
324                 return;
325
326         /*
327          * Brightness setting can sleep, delegate it to a work queue task.
328          * value 0 / LED_OFF is special, since it also disables hw-blinking
329          * (sw-blink disable is handled in led_set_brightness()).
330          * To avoid a hw-blink-disable getting lost when a second brightness
331          * change is done immediately afterwards (before the work runs),
332          * it uses a separate work_flag.
333          */
334         if (value) {
335                 led_cdev->delayed_set_value = value;
336                 set_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags);
337         } else {
338                 clear_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags);
339                 clear_bit(LED_SET_BLINK, &led_cdev->work_flags);
340                 set_bit(LED_SET_BRIGHTNESS_OFF, &led_cdev->work_flags);
341         }
342
343         schedule_work(&led_cdev->set_brightness_work);
344 }
345 EXPORT_SYMBOL_GPL(led_set_brightness_nopm);
346
347 void led_set_brightness_nosleep(struct led_classdev *led_cdev, unsigned int value)
348 {
349         led_cdev->brightness = min(value, led_cdev->max_brightness);
350
351         if (led_cdev->flags & LED_SUSPENDED)
352                 return;
353
354         led_set_brightness_nopm(led_cdev, led_cdev->brightness);
355 }
356 EXPORT_SYMBOL_GPL(led_set_brightness_nosleep);
357
358 int led_set_brightness_sync(struct led_classdev *led_cdev, unsigned int value)
359 {
360         if (led_cdev->blink_delay_on || led_cdev->blink_delay_off)
361                 return -EBUSY;
362
363         led_cdev->brightness = min(value, led_cdev->max_brightness);
364
365         if (led_cdev->flags & LED_SUSPENDED)
366                 return 0;
367
368         return __led_set_brightness_blocking(led_cdev, led_cdev->brightness);
369 }
370 EXPORT_SYMBOL_GPL(led_set_brightness_sync);
371
372 /*
373  * This is a led-core function because just like led_set_brightness()
374  * it is used in the kernel by e.g. triggers.
375  */
376 void led_mc_set_brightness(struct led_classdev *led_cdev,
377                            unsigned int *intensity_value, unsigned int num_colors,
378                            unsigned int brightness)
379 {
380         struct led_classdev_mc *mcled_cdev;
381         unsigned int i;
382
383         if (!(led_cdev->flags & LED_MULTI_COLOR)) {
384                 dev_err_once(led_cdev->dev, "error not a multi-color LED\n");
385                 return;
386         }
387
388         mcled_cdev = lcdev_to_mccdev(led_cdev);
389         if (num_colors != mcled_cdev->num_colors) {
390                 dev_err_once(led_cdev->dev, "error num_colors mismatch %u != %u\n",
391                              num_colors, mcled_cdev->num_colors);
392                 return;
393         }
394
395         for (i = 0; i < mcled_cdev->num_colors; i++)
396                 mcled_cdev->subled_info[i].intensity = intensity_value[i];
397
398         led_set_brightness(led_cdev, brightness);
399 }
400 EXPORT_SYMBOL_GPL(led_mc_set_brightness);
401
402 int led_update_brightness(struct led_classdev *led_cdev)
403 {
404         int ret;
405
406         if (led_cdev->brightness_get) {
407                 ret = led_cdev->brightness_get(led_cdev);
408                 if (ret < 0)
409                         return ret;
410
411                 led_cdev->brightness = ret;
412         }
413
414         return 0;
415 }
416 EXPORT_SYMBOL_GPL(led_update_brightness);
417
418 u32 *led_get_default_pattern(struct led_classdev *led_cdev, unsigned int *size)
419 {
420         struct fwnode_handle *fwnode = led_cdev->dev->fwnode;
421         u32 *pattern;
422         int count;
423
424         count = fwnode_property_count_u32(fwnode, "led-pattern");
425         if (count < 0)
426                 return NULL;
427
428         pattern = kcalloc(count, sizeof(*pattern), GFP_KERNEL);
429         if (!pattern)
430                 return NULL;
431
432         if (fwnode_property_read_u32_array(fwnode, "led-pattern", pattern, count)) {
433                 kfree(pattern);
434                 return NULL;
435         }
436
437         *size = count;
438
439         return pattern;
440 }
441 EXPORT_SYMBOL_GPL(led_get_default_pattern);
442
443 /* Caller must ensure led_cdev->led_access held */
444 void led_sysfs_disable(struct led_classdev *led_cdev)
445 {
446         lockdep_assert_held(&led_cdev->led_access);
447
448         led_cdev->flags |= LED_SYSFS_DISABLE;
449 }
450 EXPORT_SYMBOL_GPL(led_sysfs_disable);
451
452 /* Caller must ensure led_cdev->led_access held */
453 void led_sysfs_enable(struct led_classdev *led_cdev)
454 {
455         lockdep_assert_held(&led_cdev->led_access);
456
457         led_cdev->flags &= ~LED_SYSFS_DISABLE;
458 }
459 EXPORT_SYMBOL_GPL(led_sysfs_enable);
460
461 static void led_parse_fwnode_props(struct device *dev,
462                                    struct fwnode_handle *fwnode,
463                                    struct led_properties *props)
464 {
465         int ret;
466
467         if (!fwnode)
468                 return;
469
470         if (fwnode_property_present(fwnode, "label")) {
471                 ret = fwnode_property_read_string(fwnode, "label", &props->label);
472                 if (ret)
473                         dev_err(dev, "Error parsing 'label' property (%d)\n", ret);
474                 return;
475         }
476
477         if (fwnode_property_present(fwnode, "color")) {
478                 ret = fwnode_property_read_u32(fwnode, "color", &props->color);
479                 if (ret)
480                         dev_err(dev, "Error parsing 'color' property (%d)\n", ret);
481                 else if (props->color >= LED_COLOR_ID_MAX)
482                         dev_err(dev, "LED color identifier out of range\n");
483                 else
484                         props->color_present = true;
485         }
486
487
488         if (!fwnode_property_present(fwnode, "function"))
489                 return;
490
491         ret = fwnode_property_read_string(fwnode, "function", &props->function);
492         if (ret) {
493                 dev_err(dev,
494                         "Error parsing 'function' property (%d)\n",
495                         ret);
496         }
497
498         if (!fwnode_property_present(fwnode, "function-enumerator"))
499                 return;
500
501         ret = fwnode_property_read_u32(fwnode, "function-enumerator",
502                                        &props->func_enum);
503         if (ret) {
504                 dev_err(dev,
505                         "Error parsing 'function-enumerator' property (%d)\n",
506                         ret);
507         } else {
508                 props->func_enum_present = true;
509         }
510 }
511
512 int led_compose_name(struct device *dev, struct led_init_data *init_data,
513                      char *led_classdev_name)
514 {
515         struct led_properties props = {};
516         struct fwnode_handle *fwnode = init_data->fwnode;
517         const char *devicename = init_data->devicename;
518
519         if (!led_classdev_name)
520                 return -EINVAL;
521
522         led_parse_fwnode_props(dev, fwnode, &props);
523
524         if (props.label) {
525                 /*
526                  * If init_data.devicename is NULL, then it indicates that
527                  * DT label should be used as-is for LED class device name.
528                  * Otherwise the label is prepended with devicename to compose
529                  * the final LED class device name.
530                  */
531                 if (!devicename) {
532                         strscpy(led_classdev_name, props.label,
533                                 LED_MAX_NAME_SIZE);
534                 } else {
535                         snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
536                                  devicename, props.label);
537                 }
538         } else if (props.function || props.color_present) {
539                 char tmp_buf[LED_MAX_NAME_SIZE];
540
541                 if (props.func_enum_present) {
542                         snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s-%d",
543                                  props.color_present ? led_colors[props.color] : "",
544                                  props.function ?: "", props.func_enum);
545                 } else {
546                         snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s",
547                                  props.color_present ? led_colors[props.color] : "",
548                                  props.function ?: "");
549                 }
550                 if (init_data->devname_mandatory) {
551                         snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
552                                  devicename, tmp_buf);
553                 } else {
554                         strscpy(led_classdev_name, tmp_buf, LED_MAX_NAME_SIZE);
555
556                 }
557         } else if (init_data->default_label) {
558                 if (!devicename) {
559                         dev_err(dev, "Legacy LED naming requires devicename segment");
560                         return -EINVAL;
561                 }
562                 snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
563                          devicename, init_data->default_label);
564         } else if (is_of_node(fwnode)) {
565                 strscpy(led_classdev_name, to_of_node(fwnode)->name,
566                         LED_MAX_NAME_SIZE);
567         } else
568                 return -EINVAL;
569
570         return 0;
571 }
572 EXPORT_SYMBOL_GPL(led_compose_name);
573
574 const char *led_get_color_name(u8 color_id)
575 {
576         if (color_id >= ARRAY_SIZE(led_colors))
577                 return NULL;
578
579         return led_colors[color_id];
580 }
581 EXPORT_SYMBOL_GPL(led_get_color_name);
582
583 enum led_default_state led_init_default_state_get(struct fwnode_handle *fwnode)
584 {
585         const char *state = NULL;
586
587         if (!fwnode_property_read_string(fwnode, "default-state", &state)) {
588                 if (!strcmp(state, "keep"))
589                         return LEDS_DEFSTATE_KEEP;
590                 if (!strcmp(state, "on"))
591                         return LEDS_DEFSTATE_ON;
592         }
593
594         return LEDS_DEFSTATE_OFF;
595 }
596 EXPORT_SYMBOL_GPL(led_init_default_state_get);
This page took 0.068083 seconds and 4 git commands to generate.