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/slab.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 int enabled; 28 struct mutex mutex; 29 30 struct regulator *vcc; 31 }; 32 33 static inline int led_regulator_get_max_brightness(struct regulator *supply) 34 { 35 int ret; 36 int voltage = regulator_list_voltage(supply, 0); 37 38 if (voltage <= 0) 39 return 1; 40 41 /* even if regulator can't change voltages, 42 * we still assume it can change status 43 * and the LED can be turned on and off. 44 */ 45 ret = regulator_set_voltage(supply, voltage, voltage); 46 if (ret < 0) 47 return 1; 48 49 return regulator_count_voltages(supply); 50 } 51 52 static int led_regulator_get_voltage(struct regulator *supply, 53 enum led_brightness brightness) 54 { 55 if (brightness == 0) 56 return -EINVAL; 57 58 return regulator_list_voltage(supply, brightness - 1); 59 } 60 61 62 static void regulator_led_enable(struct regulator_led *led) 63 { 64 int ret; 65 66 if (led->enabled) 67 return; 68 69 ret = regulator_enable(led->vcc); 70 if (ret != 0) { 71 dev_err(led->cdev.dev, "Failed to enable vcc: %d\n", ret); 72 return; 73 } 74 75 led->enabled = 1; 76 } 77 78 static void regulator_led_disable(struct regulator_led *led) 79 { 80 int ret; 81 82 if (!led->enabled) 83 return; 84 85 ret = regulator_disable(led->vcc); 86 if (ret != 0) { 87 dev_err(led->cdev.dev, "Failed to disable vcc: %d\n", ret); 88 return; 89 } 90 91 led->enabled = 0; 92 } 93 94 static int regulator_led_brightness_set(struct led_classdev *led_cdev, 95 enum led_brightness value) 96 { 97 struct regulator_led *led = to_regulator_led(led_cdev); 98 int voltage; 99 int ret = 0; 100 101 mutex_lock(&led->mutex); 102 103 if (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, value); 110 dev_dbg(led->cdev.dev, "brightness: %d voltage: %d\n", 111 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 return ret; 124 } 125 126 static int regulator_led_probe(struct platform_device *pdev) 127 { 128 struct led_regulator_platform_data *pdata = 129 dev_get_platdata(&pdev->dev); 130 struct regulator_led *led; 131 struct regulator *vcc; 132 int ret = 0; 133 134 if (pdata == NULL) { 135 dev_err(&pdev->dev, "no platform data\n"); 136 return -ENODEV; 137 } 138 139 vcc = devm_regulator_get_exclusive(&pdev->dev, "vled"); 140 if (IS_ERR(vcc)) { 141 dev_err(&pdev->dev, "Cannot get vcc for %s\n", pdata->name); 142 return PTR_ERR(vcc); 143 } 144 145 led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); 146 if (led == NULL) 147 return -ENOMEM; 148 149 led->cdev.max_brightness = led_regulator_get_max_brightness(vcc); 150 if (pdata->brightness > led->cdev.max_brightness) { 151 dev_err(&pdev->dev, "Invalid default brightness %d\n", 152 pdata->brightness); 153 return -EINVAL; 154 } 155 156 led->cdev.brightness_set_blocking = regulator_led_brightness_set; 157 led->cdev.name = pdata->name; 158 led->cdev.flags |= LED_CORE_SUSPENDRESUME; 159 led->vcc = vcc; 160 161 /* to handle correctly an already enabled regulator */ 162 if (regulator_is_enabled(led->vcc)) 163 led->enabled = 1; 164 165 mutex_init(&led->mutex); 166 167 platform_set_drvdata(pdev, led); 168 169 ret = led_classdev_register(&pdev->dev, &led->cdev); 170 if (ret < 0) 171 return ret; 172 173 /* to expose the default value to userspace */ 174 led->cdev.brightness = pdata->brightness; 175 176 /* Set the default led status */ 177 regulator_led_brightness_set(&led->cdev, led->cdev.brightness); 178 179 return 0; 180 } 181 182 static int regulator_led_remove(struct platform_device *pdev) 183 { 184 struct regulator_led *led = platform_get_drvdata(pdev); 185 186 led_classdev_unregister(&led->cdev); 187 regulator_led_disable(led); 188 return 0; 189 } 190 191 static struct platform_driver regulator_led_driver = { 192 .driver = { 193 .name = "leds-regulator", 194 }, 195 .probe = regulator_led_probe, 196 .remove = regulator_led_remove, 197 }; 198 199 module_platform_driver(regulator_led_driver); 200 201 MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>"); 202 MODULE_DESCRIPTION("Regulator driven LED driver"); 203 MODULE_LICENSE("GPL"); 204 MODULE_ALIAS("platform:leds-regulator"); 205