162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * LED Class Core
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2005-2006 Openedhand Ltd.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Richard Purdie <rpurdie@openedhand.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/leds.h>
1262306a36Sopenharmony_ci#include <linux/list.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/mutex.h>
1562306a36Sopenharmony_ci#include <linux/of.h>
1662306a36Sopenharmony_ci#include <linux/property.h>
1762306a36Sopenharmony_ci#include <linux/rwsem.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci#include <uapi/linux/uleds.h>
2062306a36Sopenharmony_ci#include "leds.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ciDECLARE_RWSEM(leds_list_lock);
2362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(leds_list_lock);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ciLIST_HEAD(leds_list);
2662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(leds_list);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ciconst char * const led_colors[LED_COLOR_ID_MAX] = {
2962306a36Sopenharmony_ci	[LED_COLOR_ID_WHITE] = "white",
3062306a36Sopenharmony_ci	[LED_COLOR_ID_RED] = "red",
3162306a36Sopenharmony_ci	[LED_COLOR_ID_GREEN] = "green",
3262306a36Sopenharmony_ci	[LED_COLOR_ID_BLUE] = "blue",
3362306a36Sopenharmony_ci	[LED_COLOR_ID_AMBER] = "amber",
3462306a36Sopenharmony_ci	[LED_COLOR_ID_VIOLET] = "violet",
3562306a36Sopenharmony_ci	[LED_COLOR_ID_YELLOW] = "yellow",
3662306a36Sopenharmony_ci	[LED_COLOR_ID_IR] = "ir",
3762306a36Sopenharmony_ci	[LED_COLOR_ID_MULTI] = "multicolor",
3862306a36Sopenharmony_ci	[LED_COLOR_ID_RGB] = "rgb",
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(led_colors);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic int __led_set_brightness(struct led_classdev *led_cdev, unsigned int value)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	if (!led_cdev->brightness_set)
4562306a36Sopenharmony_ci		return -ENOTSUPP;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	led_cdev->brightness_set(led_cdev, value);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	return 0;
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic int __led_set_brightness_blocking(struct led_classdev *led_cdev, unsigned int value)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	if (!led_cdev->brightness_set_blocking)
5562306a36Sopenharmony_ci		return -ENOTSUPP;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	return led_cdev->brightness_set_blocking(led_cdev, value);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic void led_timer_function(struct timer_list *t)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	struct led_classdev *led_cdev = from_timer(led_cdev, t, blink_timer);
6362306a36Sopenharmony_ci	unsigned long brightness;
6462306a36Sopenharmony_ci	unsigned long delay;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
6762306a36Sopenharmony_ci		led_set_brightness_nosleep(led_cdev, LED_OFF);
6862306a36Sopenharmony_ci		clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
6962306a36Sopenharmony_ci		return;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	if (test_and_clear_bit(LED_BLINK_ONESHOT_STOP,
7362306a36Sopenharmony_ci			       &led_cdev->work_flags)) {
7462306a36Sopenharmony_ci		clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
7562306a36Sopenharmony_ci		return;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	brightness = led_get_brightness(led_cdev);
7962306a36Sopenharmony_ci	if (!brightness) {
8062306a36Sopenharmony_ci		/* Time to switch the LED on. */
8162306a36Sopenharmony_ci		if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE,
8262306a36Sopenharmony_ci					&led_cdev->work_flags))
8362306a36Sopenharmony_ci			brightness = led_cdev->new_blink_brightness;
8462306a36Sopenharmony_ci		else
8562306a36Sopenharmony_ci			brightness = led_cdev->blink_brightness;
8662306a36Sopenharmony_ci		delay = led_cdev->blink_delay_on;
8762306a36Sopenharmony_ci	} else {
8862306a36Sopenharmony_ci		/* Store the current brightness value to be able
8962306a36Sopenharmony_ci		 * to restore it when the delay_off period is over.
9062306a36Sopenharmony_ci		 */
9162306a36Sopenharmony_ci		led_cdev->blink_brightness = brightness;
9262306a36Sopenharmony_ci		brightness = LED_OFF;
9362306a36Sopenharmony_ci		delay = led_cdev->blink_delay_off;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	led_set_brightness_nosleep(led_cdev, brightness);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	/* Return in next iteration if led is in one-shot mode and we are in
9962306a36Sopenharmony_ci	 * the final blink state so that the led is toggled each delay_on +
10062306a36Sopenharmony_ci	 * delay_off milliseconds in worst case.
10162306a36Sopenharmony_ci	 */
10262306a36Sopenharmony_ci	if (test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags)) {
10362306a36Sopenharmony_ci		if (test_bit(LED_BLINK_INVERT, &led_cdev->work_flags)) {
10462306a36Sopenharmony_ci			if (brightness)
10562306a36Sopenharmony_ci				set_bit(LED_BLINK_ONESHOT_STOP,
10662306a36Sopenharmony_ci					&led_cdev->work_flags);
10762306a36Sopenharmony_ci		} else {
10862306a36Sopenharmony_ci			if (!brightness)
10962306a36Sopenharmony_ci				set_bit(LED_BLINK_ONESHOT_STOP,
11062306a36Sopenharmony_ci					&led_cdev->work_flags);
11162306a36Sopenharmony_ci		}
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic void set_brightness_delayed_set_brightness(struct led_classdev *led_cdev,
11862306a36Sopenharmony_ci						  unsigned int value)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	int ret = 0;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	ret = __led_set_brightness(led_cdev, value);
12362306a36Sopenharmony_ci	if (ret == -ENOTSUPP)
12462306a36Sopenharmony_ci		ret = __led_set_brightness_blocking(led_cdev, value);
12562306a36Sopenharmony_ci	if (ret < 0 &&
12662306a36Sopenharmony_ci	    /* LED HW might have been unplugged, therefore don't warn */
12762306a36Sopenharmony_ci	    !(ret == -ENODEV && (led_cdev->flags & LED_UNREGISTERING) &&
12862306a36Sopenharmony_ci	    (led_cdev->flags & LED_HW_PLUGGABLE)))
12962306a36Sopenharmony_ci		dev_err(led_cdev->dev,
13062306a36Sopenharmony_ci			"Setting an LED's brightness failed (%d)\n", ret);
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic void set_brightness_delayed(struct work_struct *ws)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct led_classdev *led_cdev =
13662306a36Sopenharmony_ci		container_of(ws, struct led_classdev, set_brightness_work);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (test_and_clear_bit(LED_BLINK_DISABLE, &led_cdev->work_flags)) {
13962306a36Sopenharmony_ci		led_stop_software_blink(led_cdev);
14062306a36Sopenharmony_ci		set_bit(LED_SET_BRIGHTNESS_OFF, &led_cdev->work_flags);
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	/*
14462306a36Sopenharmony_ci	 * Triggers may call led_set_brightness(LED_OFF),
14562306a36Sopenharmony_ci	 * led_set_brightness(LED_FULL) in quick succession to disable blinking
14662306a36Sopenharmony_ci	 * and turn the LED on. Both actions may have been scheduled to run
14762306a36Sopenharmony_ci	 * before this work item runs once. To make sure this works properly
14862306a36Sopenharmony_ci	 * handle LED_SET_BRIGHTNESS_OFF first.
14962306a36Sopenharmony_ci	 */
15062306a36Sopenharmony_ci	if (test_and_clear_bit(LED_SET_BRIGHTNESS_OFF, &led_cdev->work_flags))
15162306a36Sopenharmony_ci		set_brightness_delayed_set_brightness(led_cdev, LED_OFF);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (test_and_clear_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags))
15462306a36Sopenharmony_ci		set_brightness_delayed_set_brightness(led_cdev, led_cdev->delayed_set_value);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (test_and_clear_bit(LED_SET_BLINK, &led_cdev->work_flags)) {
15762306a36Sopenharmony_ci		unsigned long delay_on = led_cdev->delayed_delay_on;
15862306a36Sopenharmony_ci		unsigned long delay_off = led_cdev->delayed_delay_off;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		led_blink_set(led_cdev, &delay_on, &delay_off);
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic void led_set_software_blink(struct led_classdev *led_cdev,
16562306a36Sopenharmony_ci				   unsigned long delay_on,
16662306a36Sopenharmony_ci				   unsigned long delay_off)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	int current_brightness;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	current_brightness = led_get_brightness(led_cdev);
17162306a36Sopenharmony_ci	if (current_brightness)
17262306a36Sopenharmony_ci		led_cdev->blink_brightness = current_brightness;
17362306a36Sopenharmony_ci	if (!led_cdev->blink_brightness)
17462306a36Sopenharmony_ci		led_cdev->blink_brightness = led_cdev->max_brightness;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	led_cdev->blink_delay_on = delay_on;
17762306a36Sopenharmony_ci	led_cdev->blink_delay_off = delay_off;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* never on - just set to off */
18062306a36Sopenharmony_ci	if (!delay_on) {
18162306a36Sopenharmony_ci		led_set_brightness_nosleep(led_cdev, LED_OFF);
18262306a36Sopenharmony_ci		return;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	/* never off - just set to brightness */
18662306a36Sopenharmony_ci	if (!delay_off) {
18762306a36Sopenharmony_ci		led_set_brightness_nosleep(led_cdev,
18862306a36Sopenharmony_ci					   led_cdev->blink_brightness);
18962306a36Sopenharmony_ci		return;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	set_bit(LED_BLINK_SW, &led_cdev->work_flags);
19362306a36Sopenharmony_ci	mod_timer(&led_cdev->blink_timer, jiffies + 1);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic void led_blink_setup(struct led_classdev *led_cdev,
19862306a36Sopenharmony_ci		     unsigned long *delay_on,
19962306a36Sopenharmony_ci		     unsigned long *delay_off)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	if (!test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
20262306a36Sopenharmony_ci	    led_cdev->blink_set &&
20362306a36Sopenharmony_ci	    !led_cdev->blink_set(led_cdev, delay_on, delay_off))
20462306a36Sopenharmony_ci		return;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/* blink with 1 Hz as default if nothing specified */
20762306a36Sopenharmony_ci	if (!*delay_on && !*delay_off)
20862306a36Sopenharmony_ci		*delay_on = *delay_off = 500;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	led_set_software_blink(led_cdev, *delay_on, *delay_off);
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_civoid led_init_core(struct led_classdev *led_cdev)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	timer_setup(&led_cdev->blink_timer, led_timer_function, 0);
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(led_init_core);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_civoid led_blink_set(struct led_classdev *led_cdev,
22262306a36Sopenharmony_ci		   unsigned long *delay_on,
22362306a36Sopenharmony_ci		   unsigned long *delay_off)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	del_timer_sync(&led_cdev->blink_timer);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
22862306a36Sopenharmony_ci	clear_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags);
22962306a36Sopenharmony_ci	clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	led_blink_setup(led_cdev, delay_on, delay_off);
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(led_blink_set);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_civoid led_blink_set_oneshot(struct led_classdev *led_cdev,
23662306a36Sopenharmony_ci			   unsigned long *delay_on,
23762306a36Sopenharmony_ci			   unsigned long *delay_off,
23862306a36Sopenharmony_ci			   int invert)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	if (test_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags) &&
24162306a36Sopenharmony_ci	     timer_pending(&led_cdev->blink_timer))
24262306a36Sopenharmony_ci		return;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	set_bit(LED_BLINK_ONESHOT, &led_cdev->work_flags);
24562306a36Sopenharmony_ci	clear_bit(LED_BLINK_ONESHOT_STOP, &led_cdev->work_flags);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	if (invert)
24862306a36Sopenharmony_ci		set_bit(LED_BLINK_INVERT, &led_cdev->work_flags);
24962306a36Sopenharmony_ci	else
25062306a36Sopenharmony_ci		clear_bit(LED_BLINK_INVERT, &led_cdev->work_flags);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	led_blink_setup(led_cdev, delay_on, delay_off);
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(led_blink_set_oneshot);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_civoid led_blink_set_nosleep(struct led_classdev *led_cdev, unsigned long delay_on,
25762306a36Sopenharmony_ci			   unsigned long delay_off)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	/* If necessary delegate to a work queue task. */
26062306a36Sopenharmony_ci	if (led_cdev->blink_set && led_cdev->brightness_set_blocking) {
26162306a36Sopenharmony_ci		led_cdev->delayed_delay_on = delay_on;
26262306a36Sopenharmony_ci		led_cdev->delayed_delay_off = delay_off;
26362306a36Sopenharmony_ci		set_bit(LED_SET_BLINK, &led_cdev->work_flags);
26462306a36Sopenharmony_ci		schedule_work(&led_cdev->set_brightness_work);
26562306a36Sopenharmony_ci		return;
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	led_blink_set(led_cdev, &delay_on, &delay_off);
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(led_blink_set_nosleep);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_civoid led_stop_software_blink(struct led_classdev *led_cdev)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	del_timer_sync(&led_cdev->blink_timer);
27562306a36Sopenharmony_ci	led_cdev->blink_delay_on = 0;
27662306a36Sopenharmony_ci	led_cdev->blink_delay_off = 0;
27762306a36Sopenharmony_ci	clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(led_stop_software_blink);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_civoid led_set_brightness(struct led_classdev *led_cdev, unsigned int brightness)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	/*
28462306a36Sopenharmony_ci	 * If software blink is active, delay brightness setting
28562306a36Sopenharmony_ci	 * until the next timer tick.
28662306a36Sopenharmony_ci	 */
28762306a36Sopenharmony_ci	if (test_bit(LED_BLINK_SW, &led_cdev->work_flags)) {
28862306a36Sopenharmony_ci		/*
28962306a36Sopenharmony_ci		 * If we need to disable soft blinking delegate this to the
29062306a36Sopenharmony_ci		 * work queue task to avoid problems in case we are called
29162306a36Sopenharmony_ci		 * from hard irq context.
29262306a36Sopenharmony_ci		 */
29362306a36Sopenharmony_ci		if (!brightness) {
29462306a36Sopenharmony_ci			set_bit(LED_BLINK_DISABLE, &led_cdev->work_flags);
29562306a36Sopenharmony_ci			schedule_work(&led_cdev->set_brightness_work);
29662306a36Sopenharmony_ci		} else {
29762306a36Sopenharmony_ci			set_bit(LED_BLINK_BRIGHTNESS_CHANGE,
29862306a36Sopenharmony_ci				&led_cdev->work_flags);
29962306a36Sopenharmony_ci			led_cdev->new_blink_brightness = brightness;
30062306a36Sopenharmony_ci		}
30162306a36Sopenharmony_ci		return;
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	led_set_brightness_nosleep(led_cdev, brightness);
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(led_set_brightness);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_civoid led_set_brightness_nopm(struct led_classdev *led_cdev, unsigned int value)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	/* Use brightness_set op if available, it is guaranteed not to sleep */
31162306a36Sopenharmony_ci	if (!__led_set_brightness(led_cdev, value))
31262306a36Sopenharmony_ci		return;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	/*
31562306a36Sopenharmony_ci	 * Brightness setting can sleep, delegate it to a work queue task.
31662306a36Sopenharmony_ci	 * value 0 / LED_OFF is special, since it also disables hw-blinking
31762306a36Sopenharmony_ci	 * (sw-blink disable is handled in led_set_brightness()).
31862306a36Sopenharmony_ci	 * To avoid a hw-blink-disable getting lost when a second brightness
31962306a36Sopenharmony_ci	 * change is done immediately afterwards (before the work runs),
32062306a36Sopenharmony_ci	 * it uses a separate work_flag.
32162306a36Sopenharmony_ci	 */
32262306a36Sopenharmony_ci	if (value) {
32362306a36Sopenharmony_ci		led_cdev->delayed_set_value = value;
32462306a36Sopenharmony_ci		set_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags);
32562306a36Sopenharmony_ci	} else {
32662306a36Sopenharmony_ci		clear_bit(LED_SET_BRIGHTNESS, &led_cdev->work_flags);
32762306a36Sopenharmony_ci		clear_bit(LED_SET_BLINK, &led_cdev->work_flags);
32862306a36Sopenharmony_ci		set_bit(LED_SET_BRIGHTNESS_OFF, &led_cdev->work_flags);
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	schedule_work(&led_cdev->set_brightness_work);
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(led_set_brightness_nopm);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_civoid led_set_brightness_nosleep(struct led_classdev *led_cdev, unsigned int value)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	led_cdev->brightness = min(value, led_cdev->max_brightness);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	if (led_cdev->flags & LED_SUSPENDED)
34062306a36Sopenharmony_ci		return;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	led_set_brightness_nopm(led_cdev, led_cdev->brightness);
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(led_set_brightness_nosleep);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ciint led_set_brightness_sync(struct led_classdev *led_cdev, unsigned int value)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	if (led_cdev->blink_delay_on || led_cdev->blink_delay_off)
34962306a36Sopenharmony_ci		return -EBUSY;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	led_cdev->brightness = min(value, led_cdev->max_brightness);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	if (led_cdev->flags & LED_SUSPENDED)
35462306a36Sopenharmony_ci		return 0;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	return __led_set_brightness_blocking(led_cdev, led_cdev->brightness);
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(led_set_brightness_sync);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ciint led_update_brightness(struct led_classdev *led_cdev)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	int ret = 0;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (led_cdev->brightness_get) {
36562306a36Sopenharmony_ci		ret = led_cdev->brightness_get(led_cdev);
36662306a36Sopenharmony_ci		if (ret >= 0) {
36762306a36Sopenharmony_ci			led_cdev->brightness = ret;
36862306a36Sopenharmony_ci			return 0;
36962306a36Sopenharmony_ci		}
37062306a36Sopenharmony_ci	}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	return ret;
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(led_update_brightness);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ciu32 *led_get_default_pattern(struct led_classdev *led_cdev, unsigned int *size)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	struct fwnode_handle *fwnode = led_cdev->dev->fwnode;
37962306a36Sopenharmony_ci	u32 *pattern;
38062306a36Sopenharmony_ci	int count;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	count = fwnode_property_count_u32(fwnode, "led-pattern");
38362306a36Sopenharmony_ci	if (count < 0)
38462306a36Sopenharmony_ci		return NULL;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	pattern = kcalloc(count, sizeof(*pattern), GFP_KERNEL);
38762306a36Sopenharmony_ci	if (!pattern)
38862306a36Sopenharmony_ci		return NULL;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (fwnode_property_read_u32_array(fwnode, "led-pattern", pattern, count)) {
39162306a36Sopenharmony_ci		kfree(pattern);
39262306a36Sopenharmony_ci		return NULL;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	*size = count;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	return pattern;
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(led_get_default_pattern);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci/* Caller must ensure led_cdev->led_access held */
40262306a36Sopenharmony_civoid led_sysfs_disable(struct led_classdev *led_cdev)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	lockdep_assert_held(&led_cdev->led_access);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	led_cdev->flags |= LED_SYSFS_DISABLE;
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(led_sysfs_disable);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci/* Caller must ensure led_cdev->led_access held */
41162306a36Sopenharmony_civoid led_sysfs_enable(struct led_classdev *led_cdev)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	lockdep_assert_held(&led_cdev->led_access);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	led_cdev->flags &= ~LED_SYSFS_DISABLE;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(led_sysfs_enable);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic void led_parse_fwnode_props(struct device *dev,
42062306a36Sopenharmony_ci				   struct fwnode_handle *fwnode,
42162306a36Sopenharmony_ci				   struct led_properties *props)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	int ret;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	if (!fwnode)
42662306a36Sopenharmony_ci		return;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (fwnode_property_present(fwnode, "label")) {
42962306a36Sopenharmony_ci		ret = fwnode_property_read_string(fwnode, "label", &props->label);
43062306a36Sopenharmony_ci		if (ret)
43162306a36Sopenharmony_ci			dev_err(dev, "Error parsing 'label' property (%d)\n", ret);
43262306a36Sopenharmony_ci		return;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (fwnode_property_present(fwnode, "color")) {
43662306a36Sopenharmony_ci		ret = fwnode_property_read_u32(fwnode, "color", &props->color);
43762306a36Sopenharmony_ci		if (ret)
43862306a36Sopenharmony_ci			dev_err(dev, "Error parsing 'color' property (%d)\n", ret);
43962306a36Sopenharmony_ci		else if (props->color >= LED_COLOR_ID_MAX)
44062306a36Sopenharmony_ci			dev_err(dev, "LED color identifier out of range\n");
44162306a36Sopenharmony_ci		else
44262306a36Sopenharmony_ci			props->color_present = true;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	if (!fwnode_property_present(fwnode, "function"))
44762306a36Sopenharmony_ci		return;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	ret = fwnode_property_read_string(fwnode, "function", &props->function);
45062306a36Sopenharmony_ci	if (ret) {
45162306a36Sopenharmony_ci		dev_err(dev,
45262306a36Sopenharmony_ci			"Error parsing 'function' property (%d)\n",
45362306a36Sopenharmony_ci			ret);
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	if (!fwnode_property_present(fwnode, "function-enumerator"))
45762306a36Sopenharmony_ci		return;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	ret = fwnode_property_read_u32(fwnode, "function-enumerator",
46062306a36Sopenharmony_ci				       &props->func_enum);
46162306a36Sopenharmony_ci	if (ret) {
46262306a36Sopenharmony_ci		dev_err(dev,
46362306a36Sopenharmony_ci			"Error parsing 'function-enumerator' property (%d)\n",
46462306a36Sopenharmony_ci			ret);
46562306a36Sopenharmony_ci	} else {
46662306a36Sopenharmony_ci		props->func_enum_present = true;
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ciint led_compose_name(struct device *dev, struct led_init_data *init_data,
47162306a36Sopenharmony_ci		     char *led_classdev_name)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	struct led_properties props = {};
47462306a36Sopenharmony_ci	struct fwnode_handle *fwnode = init_data->fwnode;
47562306a36Sopenharmony_ci	const char *devicename = init_data->devicename;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	if (!led_classdev_name)
47862306a36Sopenharmony_ci		return -EINVAL;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	led_parse_fwnode_props(dev, fwnode, &props);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	if (props.label) {
48362306a36Sopenharmony_ci		/*
48462306a36Sopenharmony_ci		 * If init_data.devicename is NULL, then it indicates that
48562306a36Sopenharmony_ci		 * DT label should be used as-is for LED class device name.
48662306a36Sopenharmony_ci		 * Otherwise the label is prepended with devicename to compose
48762306a36Sopenharmony_ci		 * the final LED class device name.
48862306a36Sopenharmony_ci		 */
48962306a36Sopenharmony_ci		if (!devicename) {
49062306a36Sopenharmony_ci			strscpy(led_classdev_name, props.label,
49162306a36Sopenharmony_ci				LED_MAX_NAME_SIZE);
49262306a36Sopenharmony_ci		} else {
49362306a36Sopenharmony_ci			snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
49462306a36Sopenharmony_ci				 devicename, props.label);
49562306a36Sopenharmony_ci		}
49662306a36Sopenharmony_ci	} else if (props.function || props.color_present) {
49762306a36Sopenharmony_ci		char tmp_buf[LED_MAX_NAME_SIZE];
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci		if (props.func_enum_present) {
50062306a36Sopenharmony_ci			snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s-%d",
50162306a36Sopenharmony_ci				 props.color_present ? led_colors[props.color] : "",
50262306a36Sopenharmony_ci				 props.function ?: "", props.func_enum);
50362306a36Sopenharmony_ci		} else {
50462306a36Sopenharmony_ci			snprintf(tmp_buf, LED_MAX_NAME_SIZE, "%s:%s",
50562306a36Sopenharmony_ci				 props.color_present ? led_colors[props.color] : "",
50662306a36Sopenharmony_ci				 props.function ?: "");
50762306a36Sopenharmony_ci		}
50862306a36Sopenharmony_ci		if (init_data->devname_mandatory) {
50962306a36Sopenharmony_ci			snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
51062306a36Sopenharmony_ci				 devicename, tmp_buf);
51162306a36Sopenharmony_ci		} else {
51262306a36Sopenharmony_ci			strscpy(led_classdev_name, tmp_buf, LED_MAX_NAME_SIZE);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci		}
51562306a36Sopenharmony_ci	} else if (init_data->default_label) {
51662306a36Sopenharmony_ci		if (!devicename) {
51762306a36Sopenharmony_ci			dev_err(dev, "Legacy LED naming requires devicename segment");
51862306a36Sopenharmony_ci			return -EINVAL;
51962306a36Sopenharmony_ci		}
52062306a36Sopenharmony_ci		snprintf(led_classdev_name, LED_MAX_NAME_SIZE, "%s:%s",
52162306a36Sopenharmony_ci			 devicename, init_data->default_label);
52262306a36Sopenharmony_ci	} else if (is_of_node(fwnode)) {
52362306a36Sopenharmony_ci		strscpy(led_classdev_name, to_of_node(fwnode)->name,
52462306a36Sopenharmony_ci			LED_MAX_NAME_SIZE);
52562306a36Sopenharmony_ci	} else
52662306a36Sopenharmony_ci		return -EINVAL;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	return 0;
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(led_compose_name);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cienum led_default_state led_init_default_state_get(struct fwnode_handle *fwnode)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	const char *state = NULL;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	if (!fwnode_property_read_string(fwnode, "default-state", &state)) {
53762306a36Sopenharmony_ci		if (!strcmp(state, "keep"))
53862306a36Sopenharmony_ci			return LEDS_DEFSTATE_KEEP;
53962306a36Sopenharmony_ci		if (!strcmp(state, "on"))
54062306a36Sopenharmony_ci			return LEDS_DEFSTATE_ON;
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	return LEDS_DEFSTATE_OFF;
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(led_init_default_state_get);
546