^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * linux/drivers/leds-pwm.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * simple PWM based LED control
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Copyright 2009 Luotao Fu @ Pengutronix (l.fu@pengutronix.de)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * based on leds-gpio.c by Raphael Assenat <raph@8d.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/of_platform.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/leds.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/err.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/pwm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) struct led_pwm {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) const char *name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) u8 active_low;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) unsigned int max_brightness;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) struct led_pwm_data {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) struct led_classdev cdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) struct pwm_device *pwm;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) struct pwm_state pwmstate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) unsigned int active_low;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) struct led_pwm_priv {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) int num_leds;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) struct led_pwm_data leds[];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) static int led_pwm_set(struct led_classdev *led_cdev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) enum led_brightness brightness)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) struct led_pwm_data *led_dat =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) container_of(led_cdev, struct led_pwm_data, cdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) unsigned int max = led_dat->cdev.max_brightness;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) unsigned long long duty = led_dat->pwmstate.period;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) duty *= brightness;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) do_div(duty, max);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) if (led_dat->active_low)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) duty = led_dat->pwmstate.period - duty;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) led_dat->pwmstate.duty_cycle = duty;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) led_dat->pwmstate.enabled = duty > 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) return pwm_apply_state(led_dat->pwm, &led_dat->pwmstate);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) __attribute__((nonnull))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) struct led_pwm *led, struct fwnode_handle *fwnode)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) struct led_pwm_data *led_data = &priv->leds[priv->num_leds];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) struct led_init_data init_data = { .fwnode = fwnode };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) led_data->active_low = led->active_low;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) led_data->cdev.name = led->name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) led_data->cdev.brightness = LED_OFF;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) led_data->cdev.max_brightness = led->max_brightness;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) led_data->cdev.flags = LED_CORE_SUSPENDRESUME;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) led_data->pwm = devm_fwnode_pwm_get(dev, fwnode, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) if (IS_ERR(led_data->pwm))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) return dev_err_probe(dev, PTR_ERR(led_data->pwm),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) "unable to request PWM for %s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) led->name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) led_data->cdev.brightness_set_blocking = led_pwm_set;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) pwm_init_state(led_data->pwm, &led_data->pwmstate);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) ret = devm_led_classdev_register_ext(dev, &led_data->cdev, &init_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) dev_err(dev, "failed to register PWM led for %s: %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) led->name, ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) ret = led_pwm_set(&led_data->cdev, led_data->cdev.brightness);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) dev_err(dev, "failed to set led PWM value for %s: %d",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) led->name, ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) priv->num_leds++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) static int led_pwm_create_fwnode(struct device *dev, struct led_pwm_priv *priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) struct fwnode_handle *fwnode;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) struct led_pwm led;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) int ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) memset(&led, 0, sizeof(led));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) device_for_each_child_node(dev, fwnode) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) ret = fwnode_property_read_string(fwnode, "label", &led.name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) if (ret && is_of_node(fwnode))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) led.name = to_of_node(fwnode)->name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) if (!led.name) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) fwnode_handle_put(fwnode);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) led.active_low = fwnode_property_read_bool(fwnode,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) "active-low");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) fwnode_property_read_u32(fwnode, "max-brightness",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) &led.max_brightness);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) ret = led_pwm_add(dev, priv, &led, fwnode);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) fwnode_handle_put(fwnode);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) static int led_pwm_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) struct led_pwm_priv *priv;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) int ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) int count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) count = device_get_child_node_count(&pdev->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) if (!count)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) priv = devm_kzalloc(&pdev->dev, struct_size(priv, leds, count),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) if (!priv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) ret = led_pwm_create_fwnode(&pdev->dev, priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) platform_set_drvdata(pdev, priv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) static const struct of_device_id of_pwm_leds_match[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) { .compatible = "pwm-leds", },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) {},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) MODULE_DEVICE_TABLE(of, of_pwm_leds_match);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) static struct platform_driver led_pwm_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) .probe = led_pwm_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) .name = "leds_pwm",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) .of_match_table = of_pwm_leds_match,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) module_platform_driver(led_pwm_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) MODULE_DESCRIPTION("generic PWM LED driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) MODULE_LICENSE("GPL v2");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) MODULE_ALIAS("platform:leds-pwm");