f14edd82cb004475e67fe493f71ee269ee9cd0c7
[linux-2.6.git] / drivers / leds / leds-wm8350.c
1 /*
2  * LED driver for WM8350 driven LEDS.
3  *
4  * Copyright(C) 2007, 2008 Wolfson Microelectronics PLC.
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 #include <linux/kernel.h>
13 #include <linux/init.h>
14 #include <linux/platform_device.h>
15 #include <linux/leds.h>
16 #include <linux/err.h>
17 #include <linux/mfd/wm8350/pmic.h>
18 #include <linux/regulator/consumer.h>
19 #include <linux/slab.h>
20
21 /* Microamps */
22 static const int isink_cur[] = {
23         4,
24         5,
25         6,
26         7,
27         8,
28         10,
29         11,
30         14,
31         16,
32         19,
33         23,
34         27,
35         32,
36         39,
37         46,
38         54,
39         65,
40         77,
41         92,
42         109,
43         130,
44         154,
45         183,
46         218,
47         259,
48         308,
49         367,
50         436,
51         518,
52         616,
53         733,
54         872,
55         1037,
56         1233,
57         1466,
58         1744,
59         2073,
60         2466,
61         2933,
62         3487,
63         4147,
64         4932,
65         5865,
66         6975,
67         8294,
68         9864,
69         11730,
70         13949,
71         16589,
72         19728,
73         23460,
74         27899,
75         33178,
76         39455,
77         46920,
78         55798,
79         66355,
80         78910,
81         93840,
82         111596,
83         132710,
84         157820,
85         187681,
86         223191
87 };
88
89 #define to_wm8350_led(led_cdev) \
90         container_of(led_cdev, struct wm8350_led, cdev)
91
92 static void wm8350_led_enable(struct wm8350_led *led)
93 {
94         int ret;
95
96         if (led->enabled)
97                 return;
98
99         ret = regulator_enable(led->isink);
100         if (ret != 0) {
101                 dev_err(led->cdev.dev, "Failed to enable ISINK: %d\n", ret);
102                 return;
103         }
104
105         ret = regulator_enable(led->dcdc);
106         if (ret != 0) {
107                 dev_err(led->cdev.dev, "Failed to enable DCDC: %d\n", ret);
108                 regulator_disable(led->isink);
109                 return;
110         }
111
112         led->enabled = 1;
113 }
114
115 static void wm8350_led_disable(struct wm8350_led *led)
116 {
117         int ret;
118
119         if (!led->enabled)
120                 return;
121
122         ret = regulator_disable(led->dcdc);
123         if (ret != 0) {
124                 dev_err(led->cdev.dev, "Failed to disable DCDC: %d\n", ret);
125                 return;
126         }
127
128         ret = regulator_disable(led->isink);
129         if (ret != 0) {
130                 dev_err(led->cdev.dev, "Failed to disable ISINK: %d\n", ret);
131                 regulator_enable(led->dcdc);
132                 return;
133         }
134
135         led->enabled = 0;
136 }
137
138 static void led_work(struct work_struct *work)
139 {
140         struct wm8350_led *led = container_of(work, struct wm8350_led, work);
141         int ret;
142         int uA;
143         unsigned long flags;
144
145         mutex_lock(&led->mutex);
146
147         spin_lock_irqsave(&led->value_lock, flags);
148
149         if (led->value == LED_OFF) {
150                 spin_unlock_irqrestore(&led->value_lock, flags);
151                 wm8350_led_disable(led);
152                 goto out;
153         }
154
155         /* This scales linearly into the index of valid current
156          * settings which results in a linear scaling of perceived
157          * brightness due to the non-linear current settings provided
158          * by the hardware.
159          */
160         uA = (led->max_uA_index * led->value) / LED_FULL;
161         spin_unlock_irqrestore(&led->value_lock, flags);
162         BUG_ON(uA >= ARRAY_SIZE(isink_cur));
163
164         ret = regulator_set_current_limit(led->isink, isink_cur[uA],
165                                           isink_cur[uA]);
166         if (ret != 0)
167                 dev_err(led->cdev.dev, "Failed to set %duA: %d\n",
168                         isink_cur[uA], ret);
169
170         wm8350_led_enable(led);
171
172 out:
173         mutex_unlock(&led->mutex);
174 }
175
176 static void wm8350_led_set(struct led_classdev *led_cdev,
177                            enum led_brightness value)
178 {
179         struct wm8350_led *led = to_wm8350_led(led_cdev);
180         unsigned long flags;
181
182         spin_lock_irqsave(&led->value_lock, flags);
183         led->value = value;
184         schedule_work(&led->work);
185         spin_unlock_irqrestore(&led->value_lock, flags);
186 }
187
188 static void wm8350_led_shutdown(struct platform_device *pdev)
189 {
190         struct wm8350_led *led = platform_get_drvdata(pdev);
191
192         mutex_lock(&led->mutex);
193         led->value = LED_OFF;
194         wm8350_led_disable(led);
195         mutex_unlock(&led->mutex);
196 }
197
198 static int wm8350_led_probe(struct platform_device *pdev)
199 {
200         struct regulator *isink, *dcdc;
201         struct wm8350_led *led;
202         struct wm8350_led_platform_data *pdata = pdev->dev.platform_data;
203         int ret, i;
204
205         if (pdata == NULL) {
206                 dev_err(&pdev->dev, "no platform data\n");
207                 return -ENODEV;
208         }
209
210         if (pdata->max_uA < isink_cur[0]) {
211                 dev_err(&pdev->dev, "Invalid maximum current %duA\n",
212                         pdata->max_uA);
213                 return -EINVAL;
214         }
215
216         isink = regulator_get(&pdev->dev, "led_isink");
217         if (IS_ERR(isink)) {
218                 printk(KERN_ERR "%s: can't get ISINK\n", __func__);
219                 return PTR_ERR(isink);
220         }
221
222         dcdc = regulator_get(&pdev->dev, "led_vcc");
223         if (IS_ERR(dcdc)) {
224                 printk(KERN_ERR "%s: can't get DCDC\n", __func__);
225                 ret = PTR_ERR(dcdc);
226                 goto err_isink;
227         }
228
229         led = kzalloc(sizeof(*led), GFP_KERNEL);
230         if (led == NULL) {
231                 ret = -ENOMEM;
232                 goto err_dcdc;
233         }
234
235         led->cdev.brightness_set = wm8350_led_set;
236         led->cdev.default_trigger = pdata->default_trigger;
237         led->cdev.name = pdata->name;
238         led->cdev.flags |= LED_CORE_SUSPENDRESUME;
239         led->enabled = regulator_is_enabled(isink);
240         led->isink = isink;
241         led->dcdc = dcdc;
242
243         for (i = 0; i < ARRAY_SIZE(isink_cur) - 1; i++)
244                 if (isink_cur[i] >= pdata->max_uA)
245                         break;
246         led->max_uA_index = i;
247         if (pdata->max_uA != isink_cur[i])
248                 dev_warn(&pdev->dev,
249                          "Maximum current %duA is not directly supported,"
250                          " check platform data\n",
251                          pdata->max_uA);
252
253         spin_lock_init(&led->value_lock);
254         mutex_init(&led->mutex);
255         INIT_WORK(&led->work, led_work);
256         led->value = LED_OFF;
257         platform_set_drvdata(pdev, led);
258
259         ret = led_classdev_register(&pdev->dev, &led->cdev);
260         if (ret < 0)
261                 goto err_led;
262
263         return 0;
264
265  err_led:
266         kfree(led);
267  err_dcdc:
268         regulator_put(dcdc);
269  err_isink:
270         regulator_put(isink);
271         return ret;
272 }
273
274 static int wm8350_led_remove(struct platform_device *pdev)
275 {
276         struct wm8350_led *led = platform_get_drvdata(pdev);
277
278         led_classdev_unregister(&led->cdev);
279         flush_work_sync(&led->work);
280         wm8350_led_disable(led);
281         regulator_put(led->dcdc);
282         regulator_put(led->isink);
283         kfree(led);
284         return 0;
285 }
286
287 static struct platform_driver wm8350_led_driver = {
288         .driver = {
289                    .name = "wm8350-led",
290                    .owner = THIS_MODULE,
291                    },
292         .probe = wm8350_led_probe,
293         .remove = wm8350_led_remove,
294         .shutdown = wm8350_led_shutdown,
295 };
296
297 static int __devinit wm8350_led_init(void)
298 {
299         return platform_driver_register(&wm8350_led_driver);
300 }
301 module_init(wm8350_led_init);
302
303 static void wm8350_led_exit(void)
304 {
305         platform_driver_unregister(&wm8350_led_driver);
306 }
307 module_exit(wm8350_led_exit);
308
309 MODULE_AUTHOR("Mark Brown");
310 MODULE_DESCRIPTION("WM8350 LED driver");
311 MODULE_LICENSE("GPL");
312 MODULE_ALIAS("platform:wm8350-led");