Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

leds: Add LED class driver for regulator driven LEDs.

This driver provides an interface for controlling LEDs (or vibrators)
connected to PMICs for which there is a regulator framework driver.

This driver can be used, for instance, to control vibrator on all Motorola EZX
phones using the pcap-regulator driver services.

Signed-off-by: Antonio Ospite <ospite@studenti.unina.it>
Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Richard Purdie <rpurdie@linux.intel.com>

authored by

Antonio Ospite and committed by
Richard Purdie
d4cc6a2e 3c0f6e1e

+295
+6
drivers/leds/Kconfig
··· 229 229 help 230 230 This option enables support for pwm driven LEDs 231 231 232 + config LEDS_REGULATOR 233 + tristate "REGULATOR driven LED support" 234 + depends on LEDS_CLASS && REGULATOR 235 + help 236 + This option enables support for regulator driven LEDs. 237 + 232 238 config LEDS_BD2802 233 239 tristate "LED driver for BD2802 RGB LED" 234 240 depends on LEDS_CLASS && I2C
+1
drivers/leds/Makefile
··· 29 29 obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o 30 30 obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o 31 31 obj-$(CONFIG_LEDS_PWM) += leds-pwm.o 32 + obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o 32 33 obj-$(CONFIG_LEDS_INTEL_SS4200) += leds-ss4200.o 33 34 obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o 34 35 obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o
+242
drivers/leds/leds-regulator.c
··· 1 + /* 2 + * leds-regulator.c - LED class driver for regulator driven LEDs. 3 + * 4 + * Copyright (C) 2009 Antonio Ospite <ospite@studenti.unina.it> 5 + * 6 + * Inspired by leds-wm8350 driver. 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License version 2 as 10 + * published by the Free Software Foundation. 11 + * 12 + */ 13 + 14 + #include <linux/module.h> 15 + #include <linux/err.h> 16 + #include <linux/workqueue.h> 17 + #include <linux/leds.h> 18 + #include <linux/leds-regulator.h> 19 + #include <linux/platform_device.h> 20 + #include <linux/regulator/consumer.h> 21 + 22 + #define to_regulator_led(led_cdev) \ 23 + container_of(led_cdev, struct regulator_led, cdev) 24 + 25 + struct regulator_led { 26 + struct led_classdev cdev; 27 + enum led_brightness value; 28 + int enabled; 29 + struct mutex mutex; 30 + struct work_struct work; 31 + 32 + struct regulator *vcc; 33 + }; 34 + 35 + static inline int led_regulator_get_max_brightness(struct regulator *supply) 36 + { 37 + int ret; 38 + int voltage = regulator_list_voltage(supply, 0); 39 + 40 + if (voltage <= 0) 41 + return 1; 42 + 43 + /* even if regulator can't change voltages, 44 + * we still assume it can change status 45 + * and the LED can be turned on and off. 46 + */ 47 + ret = regulator_set_voltage(supply, voltage, voltage); 48 + if (ret < 0) 49 + return 1; 50 + 51 + return regulator_count_voltages(supply); 52 + } 53 + 54 + static int led_regulator_get_voltage(struct regulator *supply, 55 + enum led_brightness brightness) 56 + { 57 + if (brightness == 0) 58 + return -EINVAL; 59 + 60 + return regulator_list_voltage(supply, brightness - 1); 61 + } 62 + 63 + 64 + static void regulator_led_enable(struct regulator_led *led) 65 + { 66 + int ret; 67 + 68 + if (led->enabled) 69 + return; 70 + 71 + ret = regulator_enable(led->vcc); 72 + if (ret != 0) { 73 + dev_err(led->cdev.dev, "Failed to enable vcc: %d\n", ret); 74 + return; 75 + } 76 + 77 + led->enabled = 1; 78 + } 79 + 80 + static void regulator_led_disable(struct regulator_led *led) 81 + { 82 + int ret; 83 + 84 + if (!led->enabled) 85 + return; 86 + 87 + ret = regulator_disable(led->vcc); 88 + if (ret != 0) { 89 + dev_err(led->cdev.dev, "Failed to disable vcc: %d\n", ret); 90 + return; 91 + } 92 + 93 + led->enabled = 0; 94 + } 95 + 96 + static void regulator_led_set_value(struct regulator_led *led) 97 + { 98 + int voltage; 99 + int ret; 100 + 101 + mutex_lock(&led->mutex); 102 + 103 + if (led->value == LED_OFF) { 104 + regulator_led_disable(led); 105 + goto out; 106 + } 107 + 108 + if (led->cdev.max_brightness > 1) { 109 + voltage = led_regulator_get_voltage(led->vcc, led->value); 110 + dev_dbg(led->cdev.dev, "brightness: %d voltage: %d\n", 111 + led->value, voltage); 112 + 113 + ret = regulator_set_voltage(led->vcc, voltage, voltage); 114 + if (ret != 0) 115 + dev_err(led->cdev.dev, "Failed to set voltage %d: %d\n", 116 + voltage, ret); 117 + } 118 + 119 + regulator_led_enable(led); 120 + 121 + out: 122 + mutex_unlock(&led->mutex); 123 + } 124 + 125 + static void led_work(struct work_struct *work) 126 + { 127 + struct regulator_led *led; 128 + 129 + led = container_of(work, struct regulator_led, work); 130 + regulator_led_set_value(led); 131 + } 132 + 133 + static void regulator_led_brightness_set(struct led_classdev *led_cdev, 134 + enum led_brightness value) 135 + { 136 + struct regulator_led *led = to_regulator_led(led_cdev); 137 + 138 + led->value = value; 139 + schedule_work(&led->work); 140 + } 141 + 142 + static int __devinit regulator_led_probe(struct platform_device *pdev) 143 + { 144 + struct led_regulator_platform_data *pdata = pdev->dev.platform_data; 145 + struct regulator_led *led; 146 + struct regulator *vcc; 147 + int ret = 0; 148 + 149 + if (pdata == NULL) { 150 + dev_err(&pdev->dev, "no platform data\n"); 151 + return -ENODEV; 152 + } 153 + 154 + vcc = regulator_get_exclusive(&pdev->dev, "vled"); 155 + if (IS_ERR(vcc)) { 156 + dev_err(&pdev->dev, "Cannot get vcc for %s\n", pdata->name); 157 + return PTR_ERR(vcc); 158 + } 159 + 160 + led = kzalloc(sizeof(*led), GFP_KERNEL); 161 + if (led == NULL) { 162 + ret = -ENOMEM; 163 + goto err_vcc; 164 + } 165 + 166 + led->cdev.max_brightness = led_regulator_get_max_brightness(vcc); 167 + if (pdata->brightness > led->cdev.max_brightness) { 168 + dev_err(&pdev->dev, "Invalid default brightness %d\n", 169 + pdata->brightness); 170 + ret = -EINVAL; 171 + goto err_led; 172 + } 173 + led->value = pdata->brightness; 174 + 175 + led->cdev.brightness_set = regulator_led_brightness_set; 176 + led->cdev.name = pdata->name; 177 + led->cdev.flags |= LED_CORE_SUSPENDRESUME; 178 + led->vcc = vcc; 179 + 180 + mutex_init(&led->mutex); 181 + INIT_WORK(&led->work, led_work); 182 + 183 + platform_set_drvdata(pdev, led); 184 + 185 + ret = led_classdev_register(&pdev->dev, &led->cdev); 186 + if (ret < 0) { 187 + cancel_work_sync(&led->work); 188 + goto err_led; 189 + } 190 + 191 + /* to expose the default value to userspace */ 192 + led->cdev.brightness = led->value; 193 + 194 + /* Set the default led status */ 195 + regulator_led_set_value(led); 196 + 197 + return 0; 198 + 199 + err_led: 200 + kfree(led); 201 + err_vcc: 202 + regulator_put(vcc); 203 + return ret; 204 + } 205 + 206 + static int __devexit regulator_led_remove(struct platform_device *pdev) 207 + { 208 + struct regulator_led *led = platform_get_drvdata(pdev); 209 + 210 + led_classdev_unregister(&led->cdev); 211 + cancel_work_sync(&led->work); 212 + regulator_led_disable(led); 213 + regulator_put(led->vcc); 214 + kfree(led); 215 + return 0; 216 + } 217 + 218 + static struct platform_driver regulator_led_driver = { 219 + .driver = { 220 + .name = "leds-regulator", 221 + .owner = THIS_MODULE, 222 + }, 223 + .probe = regulator_led_probe, 224 + .remove = __devexit_p(regulator_led_remove), 225 + }; 226 + 227 + static int __init regulator_led_init(void) 228 + { 229 + return platform_driver_register(&regulator_led_driver); 230 + } 231 + module_init(regulator_led_init); 232 + 233 + static void __exit regulator_led_exit(void) 234 + { 235 + platform_driver_unregister(&regulator_led_driver); 236 + } 237 + module_exit(regulator_led_exit); 238 + 239 + MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>"); 240 + MODULE_DESCRIPTION("Regulator driven LED driver"); 241 + MODULE_LICENSE("GPL"); 242 + MODULE_ALIAS("platform:leds-regulator");
+46
include/linux/leds-regulator.h
··· 1 + /* 2 + * leds-regulator.h - platform data structure for regulator driven LEDs. 3 + * 4 + * Copyright (C) 2009 Antonio Ospite <ospite@studenti.unina.it> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + * 10 + */ 11 + 12 + #ifndef __LINUX_LEDS_REGULATOR_H 13 + #define __LINUX_LEDS_REGULATOR_H 14 + 15 + /* 16 + * Use "vled" as supply id when declaring the regulator consumer: 17 + * 18 + * static struct regulator_consumer_supply pcap_regulator_VVIB_consumers [] = { 19 + * { .dev_name = "leds-regulator.0", supply = "vled" }, 20 + * }; 21 + * 22 + * If you have several regulator driven LEDs, you can append a numerical id to 23 + * .dev_name as done above, and use the same id when declaring the platform 24 + * device: 25 + * 26 + * static struct led_regulator_platform_data a780_vibrator_data = { 27 + * .name = "a780::vibrator", 28 + * }; 29 + * 30 + * static struct platform_device a780_vibrator = { 31 + * .name = "leds-regulator", 32 + * .id = 0, 33 + * .dev = { 34 + * .platform_data = &a780_vibrator_data, 35 + * }, 36 + * }; 37 + */ 38 + 39 + #include <linux/leds.h> 40 + 41 + struct led_regulator_platform_data { 42 + char *name; /* LED name as expected by LED class */ 43 + enum led_brightness brightness; /* initial brightness value */ 44 + }; 45 + 46 + #endif /* __LINUX_LEDS_REGULATOR_H */