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(®ulator_led_driver); 230 } 231 module_init(regulator_led_init); 232 233 static void __exit regulator_led_exit(void) 234 { 235 platform_driver_unregister(®ulator_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"); 243