1 // SPDX-License-Identifier: GPL-2.0-only
3 * linux/drivers/leds-pwm.c
5 * simple PWM based LED control
7 * Copyright 2009 Luotao Fu @ Pengutronix (l.fu@pengutronix.de)
9 * based on leds-gpio.c by Raphael Assenat <raph@8d.com>
12 #include <linux/module.h>
13 #include <linux/kernel.h>
14 #include <linux/platform_device.h>
15 #include <linux/of_platform.h>
16 #include <linux/leds.h>
17 #include <linux/err.h>
18 #include <linux/pwm.h>
19 #include <linux/slab.h>
24 unsigned int max_brightness
;
28 struct led_classdev cdev
;
29 struct pwm_device
*pwm
;
30 struct pwm_state pwmstate
;
31 unsigned int active_low
;
36 struct led_pwm_data leds
[];
39 static int led_pwm_set(struct led_classdev
*led_cdev
,
40 enum led_brightness brightness
)
42 struct led_pwm_data
*led_dat
=
43 container_of(led_cdev
, struct led_pwm_data
, cdev
);
44 unsigned int max
= led_dat
->cdev
.max_brightness
;
45 unsigned long long duty
= led_dat
->pwmstate
.period
;
50 if (led_dat
->active_low
)
51 duty
= led_dat
->pwmstate
.period
- duty
;
53 led_dat
->pwmstate
.duty_cycle
= duty
;
54 led_dat
->pwmstate
.enabled
= duty
> 0;
55 return pwm_apply_state(led_dat
->pwm
, &led_dat
->pwmstate
);
58 __attribute__((nonnull
))
59 static int led_pwm_add(struct device
*dev
, struct led_pwm_priv
*priv
,
60 struct led_pwm
*led
, struct fwnode_handle
*fwnode
)
62 struct led_pwm_data
*led_data
= &priv
->leds
[priv
->num_leds
];
63 struct led_init_data init_data
= { .fwnode
= fwnode
};
66 led_data
->active_low
= led
->active_low
;
67 led_data
->cdev
.name
= led
->name
;
68 led_data
->cdev
.brightness
= LED_OFF
;
69 led_data
->cdev
.max_brightness
= led
->max_brightness
;
70 led_data
->cdev
.flags
= LED_CORE_SUSPENDRESUME
;
72 led_data
->pwm
= devm_fwnode_pwm_get(dev
, fwnode
, NULL
);
73 if (IS_ERR(led_data
->pwm
))
74 return dev_err_probe(dev
, PTR_ERR(led_data
->pwm
),
75 "unable to request PWM for %s\n",
78 led_data
->cdev
.brightness_set_blocking
= led_pwm_set
;
80 pwm_init_state(led_data
->pwm
, &led_data
->pwmstate
);
82 ret
= devm_led_classdev_register_ext(dev
, &led_data
->cdev
, &init_data
);
84 dev_err(dev
, "failed to register PWM led for %s: %d\n",
89 ret
= led_pwm_set(&led_data
->cdev
, led_data
->cdev
.brightness
);
91 dev_err(dev
, "failed to set led PWM value for %s: %d",
100 static int led_pwm_create_fwnode(struct device
*dev
, struct led_pwm_priv
*priv
)
102 struct fwnode_handle
*fwnode
;
106 memset(&led
, 0, sizeof(led
));
108 device_for_each_child_node(dev
, fwnode
) {
109 ret
= fwnode_property_read_string(fwnode
, "label", &led
.name
);
110 if (ret
&& is_of_node(fwnode
))
111 led
.name
= to_of_node(fwnode
)->name
;
114 fwnode_handle_put(fwnode
);
118 led
.active_low
= fwnode_property_read_bool(fwnode
,
120 fwnode_property_read_u32(fwnode
, "max-brightness",
121 &led
.max_brightness
);
123 ret
= led_pwm_add(dev
, priv
, &led
, fwnode
);
125 fwnode_handle_put(fwnode
);
133 static int led_pwm_probe(struct platform_device
*pdev
)
135 struct led_pwm_priv
*priv
;
139 count
= device_get_child_node_count(&pdev
->dev
);
144 priv
= devm_kzalloc(&pdev
->dev
, struct_size(priv
, leds
, count
),
149 ret
= led_pwm_create_fwnode(&pdev
->dev
, priv
);
154 platform_set_drvdata(pdev
, priv
);
159 static const struct of_device_id of_pwm_leds_match
[] = {
160 { .compatible
= "pwm-leds", },
163 MODULE_DEVICE_TABLE(of
, of_pwm_leds_match
);
165 static struct platform_driver led_pwm_driver
= {
166 .probe
= led_pwm_probe
,
169 .of_match_table
= of_pwm_leds_match
,
173 module_platform_driver(led_pwm_driver
);
175 MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
176 MODULE_DESCRIPTION("generic PWM LED driver");
177 MODULE_LICENSE("GPL v2");
178 MODULE_ALIAS("platform:leds-pwm");