Blame view
drivers/leds/leds-wm8350.c
5.16 KB
0081e8020
|
1 2 3 4 5 6 7 8 9 10 11 12 |
/* * LED driver for WM8350 driven LEDS. * * Copyright(C) 2007, 2008 Wolfson Microelectronics PLC. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */ #include <linux/kernel.h> |
0081e8020
|
13 14 15 16 17 |
#include <linux/platform_device.h> #include <linux/leds.h> #include <linux/err.h> #include <linux/mfd/wm8350/pmic.h> #include <linux/regulator/consumer.h> |
5a0e3ad6a
|
18 |
#include <linux/slab.h> |
54f4dedb5
|
19 |
#include <linux/module.h> |
0081e8020
|
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
/* Microamps */ static const int isink_cur[] = { 4, 5, 6, 7, 8, 10, 11, 14, 16, 19, 23, 27, 32, 39, 46, 54, 65, 77, 92, 109, 130, 154, 183, 218, 259, 308, 367, 436, 518, 616, 733, 872, 1037, 1233, 1466, 1744, 2073, 2466, 2933, 3487, 4147, 4932, 5865, 6975, 8294, 9864, 11730, 13949, 16589, 19728, 23460, 27899, 33178, 39455, 46920, 55798, 66355, 78910, 93840, 111596, 132710, 157820, 187681, 223191 }; #define to_wm8350_led(led_cdev) \ container_of(led_cdev, struct wm8350_led, cdev) |
0dd756f76
|
91 |
static int wm8350_led_enable(struct wm8350_led *led) |
0081e8020
|
92 |
{ |
0dd756f76
|
93 |
int ret = 0; |
0081e8020
|
94 95 |
if (led->enabled) |
0dd756f76
|
96 |
return ret; |
0081e8020
|
97 98 99 100 101 |
ret = regulator_enable(led->isink); if (ret != 0) { dev_err(led->cdev.dev, "Failed to enable ISINK: %d ", ret); |
0dd756f76
|
102 |
return ret; |
0081e8020
|
103 104 105 106 107 108 109 |
} ret = regulator_enable(led->dcdc); if (ret != 0) { dev_err(led->cdev.dev, "Failed to enable DCDC: %d ", ret); regulator_disable(led->isink); |
0dd756f76
|
110 |
return ret; |
0081e8020
|
111 112 113 |
} led->enabled = 1; |
0dd756f76
|
114 115 |
return ret; |
0081e8020
|
116 |
} |
0dd756f76
|
117 |
static int wm8350_led_disable(struct wm8350_led *led) |
0081e8020
|
118 |
{ |
0dd756f76
|
119 |
int ret = 0; |
0081e8020
|
120 121 |
if (!led->enabled) |
0dd756f76
|
122 |
return ret; |
0081e8020
|
123 124 125 126 127 |
ret = regulator_disable(led->dcdc); if (ret != 0) { dev_err(led->cdev.dev, "Failed to disable DCDC: %d ", ret); |
0dd756f76
|
128 |
return ret; |
0081e8020
|
129 130 131 132 133 134 |
} ret = regulator_disable(led->isink); if (ret != 0) { dev_err(led->cdev.dev, "Failed to disable ISINK: %d ", ret); |
d67eb8e66
|
135 136 137 138 139 |
ret = regulator_enable(led->dcdc); if (ret != 0) dev_err(led->cdev.dev, "Failed to reenable DCDC: %d ", ret); |
0dd756f76
|
140 |
return ret; |
0081e8020
|
141 142 143 |
} led->enabled = 0; |
0dd756f76
|
144 145 |
return ret; |
0081e8020
|
146 |
} |
0dd756f76
|
147 148 |
static int wm8350_led_set(struct led_classdev *led_cdev, enum led_brightness value) |
0081e8020
|
149 |
{ |
0dd756f76
|
150 151 |
struct wm8350_led *led = to_wm8350_led(led_cdev); unsigned long flags; |
0081e8020
|
152 153 |
int ret; int uA; |
0081e8020
|
154 |
|
0dd756f76
|
155 |
led->value = value; |
0081e8020
|
156 157 158 159 160 |
spin_lock_irqsave(&led->value_lock, flags); if (led->value == LED_OFF) { spin_unlock_irqrestore(&led->value_lock, flags); |
0dd756f76
|
161 |
return wm8350_led_disable(led); |
0081e8020
|
162 163 164 165 166 167 168 169 170 171 172 173 174 |
} /* This scales linearly into the index of valid current * settings which results in a linear scaling of perceived * brightness due to the non-linear current settings provided * by the hardware. */ uA = (led->max_uA_index * led->value) / LED_FULL; spin_unlock_irqrestore(&led->value_lock, flags); BUG_ON(uA >= ARRAY_SIZE(isink_cur)); ret = regulator_set_current_limit(led->isink, isink_cur[uA], isink_cur[uA]); |
0dd756f76
|
175 |
if (ret != 0) { |
0081e8020
|
176 177 178 |
dev_err(led->cdev.dev, "Failed to set %duA: %d ", isink_cur[uA], ret); |
0dd756f76
|
179 180 |
return ret; } |
0081e8020
|
181 |
|
0dd756f76
|
182 |
return wm8350_led_enable(led); |
0081e8020
|
183 |
} |
0081e8020
|
184 185 186 |
static void wm8350_led_shutdown(struct platform_device *pdev) { struct wm8350_led *led = platform_get_drvdata(pdev); |
0081e8020
|
187 188 |
led->value = LED_OFF; wm8350_led_disable(led); |
0081e8020
|
189 190 191 192 193 194 |
} static int wm8350_led_probe(struct platform_device *pdev) { struct regulator *isink, *dcdc; struct wm8350_led *led; |
87aae1ea8
|
195 |
struct wm8350_led_platform_data *pdata = dev_get_platdata(&pdev->dev); |
490dcee9b
|
196 |
int i; |
0081e8020
|
197 198 199 200 201 202 203 204 205 206 207 208 209 |
if (pdata == NULL) { dev_err(&pdev->dev, "no platform data "); return -ENODEV; } if (pdata->max_uA < isink_cur[0]) { dev_err(&pdev->dev, "Invalid maximum current %duA ", pdata->max_uA); return -EINVAL; } |
490dcee9b
|
210 |
isink = devm_regulator_get(&pdev->dev, "led_isink"); |
0081e8020
|
211 |
if (IS_ERR(isink)) { |
b75d28027
|
212 213 |
dev_err(&pdev->dev, "%s: can't get ISINK ", __func__); |
0081e8020
|
214 215 |
return PTR_ERR(isink); } |
490dcee9b
|
216 |
dcdc = devm_regulator_get(&pdev->dev, "led_vcc"); |
0081e8020
|
217 |
if (IS_ERR(dcdc)) { |
b75d28027
|
218 219 |
dev_err(&pdev->dev, "%s: can't get DCDC ", __func__); |
490dcee9b
|
220 |
return PTR_ERR(dcdc); |
0081e8020
|
221 |
} |
c957b614b
|
222 |
led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); |
490dcee9b
|
223 224 |
if (led == NULL) return -ENOMEM; |
0081e8020
|
225 |
|
0dd756f76
|
226 |
led->cdev.brightness_set_blocking = wm8350_led_set; |
0081e8020
|
227 228 |
led->cdev.default_trigger = pdata->default_trigger; led->cdev.name = pdata->name; |
859cb7f2a
|
229 |
led->cdev.flags |= LED_CORE_SUSPENDRESUME; |
0081e8020
|
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
led->enabled = regulator_is_enabled(isink); led->isink = isink; led->dcdc = dcdc; for (i = 0; i < ARRAY_SIZE(isink_cur) - 1; i++) if (isink_cur[i] >= pdata->max_uA) break; led->max_uA_index = i; if (pdata->max_uA != isink_cur[i]) dev_warn(&pdev->dev, "Maximum current %duA is not directly supported," " check platform data ", pdata->max_uA); spin_lock_init(&led->value_lock); |
0081e8020
|
246 247 |
led->value = LED_OFF; platform_set_drvdata(pdev, led); |
490dcee9b
|
248 |
return led_classdev_register(&pdev->dev, &led->cdev); |
0081e8020
|
249 250 251 252 253 254 255 |
} static int wm8350_led_remove(struct platform_device *pdev) { struct wm8350_led *led = platform_get_drvdata(pdev); led_classdev_unregister(&led->cdev); |
0081e8020
|
256 |
wm8350_led_disable(led); |
0081e8020
|
257 258 259 260 261 262 |
return 0; } static struct platform_driver wm8350_led_driver = { .driver = { .name = "wm8350-led", |
0081e8020
|
263 264 265 266 |
}, .probe = wm8350_led_probe, .remove = wm8350_led_remove, .shutdown = wm8350_led_shutdown, |
0081e8020
|
267 |
}; |
892a8843f
|
268 |
module_platform_driver(wm8350_led_driver); |
0081e8020
|
269 270 271 272 273 |
MODULE_AUTHOR("Mark Brown"); MODULE_DESCRIPTION("WM8350 LED driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:wm8350-led"); |