aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/leds/Kconfig7
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-wm8350.c333
-rw-r--r--drivers/mfd/wm8350-core.c3
-rw-r--r--drivers/regulator/wm8350-regulator.c91
-rw-r--r--include/linux/mfd/wm8350/pmic.h36
6 files changed, 471 insertions, 0 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 3d0d2625f6ab..a4a1ae214630 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -164,6 +164,13 @@ config LEDS_PCA955X
164 LED driver chips accessed via the I2C bus. Supported 164 LED driver chips accessed via the I2C bus. Supported
165 devices include PCA9550, PCA9551, PCA9552, and PCA9553. 165 devices include PCA9550, PCA9551, PCA9552, and PCA9553.
166 166
167config LEDS_WM8350
168 tristate "LED Support for WM8350 AudioPlus PMIC"
169 depends on LEDS_CLASS && MFD_WM8350
170 help
171 This option enables support for LEDs driven by the Wolfson
172 Microelectronics WM8350 AudioPlus PMIC.
173
167config LEDS_DA903X 174config LEDS_DA903X
168 tristate "LED Support for DA9030/DA9034 PMIC" 175 tristate "LED Support for DA9030/DA9034 PMIC"
169 depends on LEDS_CLASS && PMIC_DA903X 176 depends on LEDS_CLASS && PMIC_DA903X
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index ff4616a49043..bc247cb02e82 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_LEDS_FSG) += leds-fsg.o
24obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o 24obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o
25obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o 25obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o
26obj-$(CONFIG_LEDS_HP_DISK) += leds-hp-disk.o 26obj-$(CONFIG_LEDS_HP_DISK) += leds-hp-disk.o
27obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o
27 28
28# LED Triggers 29# LED Triggers
29obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o 30obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o
diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c
new file mode 100644
index 000000000000..846ed978103f
--- /dev/null
+++ b/drivers/leds/leds-wm8350.c
@@ -0,0 +1,333 @@
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
20/* Microamps */
21static const int isink_cur[] = {
22 4,
23 5,
24 6,
25 7,
26 8,
27 10,
28 11,
29 14,
30 16,
31 19,
32 23,
33 27,
34 32,
35 39,
36 46,
37 54,
38 65,
39 77,
40 92,
41 109,
42 130,
43 154,
44 183,
45 218,
46 259,
47 308,
48 367,
49 436,
50 518,
51 616,
52 733,
53 872,
54 1037,
55 1233,
56 1466,
57 1744,
58 2073,
59 2466,
60 2933,
61 3487,
62 4147,
63 4932,
64 5865,
65 6975,
66 8294,
67 9864,
68 11730,
69 13949,
70 16589,
71 19728,
72 23460,
73 27899,
74 33178,
75 39455,
76 46920,
77 55798,
78 66355,
79 78910,
80 93840,
81 111596,
82 132710,
83 157820,
84 187681,
85 223191
86};
87
88#define to_wm8350_led(led_cdev) \
89 container_of(led_cdev, struct wm8350_led, cdev)
90
91static void wm8350_led_enable(struct wm8350_led *led)
92{
93 int ret;
94
95 if (led->enabled)
96 return;
97
98 ret = regulator_enable(led->isink);
99 if (ret != 0) {
100 dev_err(led->cdev.dev, "Failed to enable ISINK: %d\n", ret);
101 return;
102 }
103
104 ret = regulator_enable(led->dcdc);
105 if (ret != 0) {
106 dev_err(led->cdev.dev, "Failed to enable DCDC: %d\n", ret);
107 regulator_disable(led->isink);
108 return;
109 }
110
111 led->enabled = 1;
112}
113
114static void wm8350_led_disable(struct wm8350_led *led)
115{
116 int ret;
117
118 if (!led->enabled)
119 return;
120
121 ret = regulator_disable(led->dcdc);
122 if (ret != 0) {
123 dev_err(led->cdev.dev, "Failed to disable DCDC: %d\n", ret);
124 return;
125 }
126
127 ret = regulator_disable(led->isink);
128 if (ret != 0) {
129 dev_err(led->cdev.dev, "Failed to disable ISINK: %d\n", ret);
130 regulator_enable(led->dcdc);
131 return;
132 }
133
134 led->enabled = 0;
135}
136
137static void led_work(struct work_struct *work)
138{
139 struct wm8350_led *led = container_of(work, struct wm8350_led, work);
140 int ret;
141 int uA;
142 unsigned long flags;
143
144 mutex_lock(&led->mutex);
145
146 spin_lock_irqsave(&led->value_lock, flags);
147
148 if (led->value == LED_OFF) {
149 spin_unlock_irqrestore(&led->value_lock, flags);
150 wm8350_led_disable(led);
151 goto out;
152 }
153
154 /* This scales linearly into the index of valid current
155 * settings which results in a linear scaling of perceived
156 * brightness due to the non-linear current settings provided
157 * by the hardware.
158 */
159 uA = (led->max_uA_index * led->value) / LED_FULL;
160 spin_unlock_irqrestore(&led->value_lock, flags);
161 BUG_ON(uA >= ARRAY_SIZE(isink_cur));
162
163 ret = regulator_set_current_limit(led->isink, isink_cur[uA],
164 isink_cur[uA]);
165 if (ret != 0)
166 dev_err(led->cdev.dev, "Failed to set %duA: %d\n",
167 isink_cur[uA], ret);
168
169 wm8350_led_enable(led);
170
171out:
172 mutex_unlock(&led->mutex);
173}
174
175static void wm8350_led_set(struct led_classdev *led_cdev,
176 enum led_brightness value)
177{
178 struct wm8350_led *led = to_wm8350_led(led_cdev);
179 unsigned long flags;
180
181 spin_lock_irqsave(&led->value_lock, flags);
182 led->value = value;
183 schedule_work(&led->work);
184 spin_unlock_irqrestore(&led->value_lock, flags);
185}
186
187#ifdef CONFIG_PM
188static int wm8350_led_suspend(struct platform_device *pdev, pm_message_t state)
189{
190 struct wm8350_led *led = platform_get_drvdata(pdev);
191
192 led_classdev_suspend(&led->cdev);
193 return 0;
194}
195
196static int wm8350_led_resume(struct platform_device *pdev)
197{
198 struct wm8350_led *led = platform_get_drvdata(pdev);
199
200 led_classdev_resume(&led->cdev);
201 return 0;
202}
203#else
204#define wm8350_led_suspend NULL
205#define wm8350_led_resume NULL
206#endif
207
208static void wm8350_led_shutdown(struct platform_device *pdev)
209{
210 struct wm8350_led *led = platform_get_drvdata(pdev);
211
212 mutex_lock(&led->mutex);
213 led->value = LED_OFF;
214 wm8350_led_disable(led);
215 mutex_unlock(&led->mutex);
216}
217
218static int wm8350_led_probe(struct platform_device *pdev)
219{
220 struct regulator *isink, *dcdc;
221 struct wm8350_led *led;
222 struct wm8350_led_platform_data *pdata = pdev->dev.platform_data;
223 int ret, i;
224
225 if (pdata == NULL) {
226 dev_err(&pdev->dev, "no platform data\n");
227 return -ENODEV;
228 }
229
230 if (pdata->max_uA < isink_cur[0]) {
231 dev_err(&pdev->dev, "Invalid maximum current %duA\n",
232 pdata->max_uA);
233 return -EINVAL;
234 }
235
236 isink = regulator_get(&pdev->dev, "led_isink");
237 if (IS_ERR(isink)) {
238 printk(KERN_ERR "%s: cant get ISINK\n", __func__);
239 return PTR_ERR(isink);
240 }
241
242 dcdc = regulator_get(&pdev->dev, "led_vcc");
243 if (IS_ERR(dcdc)) {
244 printk(KERN_ERR "%s: cant get DCDC\n", __func__);
245 ret = PTR_ERR(dcdc);
246 goto err_isink;
247 }
248
249 led = kzalloc(sizeof(*led), GFP_KERNEL);
250 if (led == NULL) {
251 ret = -ENOMEM;
252 goto err_dcdc;
253 }
254
255 led->cdev.brightness_set = wm8350_led_set;
256 led->cdev.default_trigger = pdata->default_trigger;
257 led->cdev.name = pdata->name;
258 led->enabled = regulator_is_enabled(isink);
259 led->isink = isink;
260 led->dcdc = dcdc;
261
262 for (i = 0; i < ARRAY_SIZE(isink_cur) - 1; i++)
263 if (isink_cur[i] >= pdata->max_uA)
264 break;
265 led->max_uA_index = i;
266 if (pdata->max_uA != isink_cur[i])
267 dev_warn(&pdev->dev,
268 "Maximum current %duA is not directly supported,"
269 " check platform data\n",
270 pdata->max_uA);
271
272 spin_lock_init(&led->value_lock);
273 mutex_init(&led->mutex);
274 INIT_WORK(&led->work, led_work);
275 led->value = LED_OFF;
276 platform_set_drvdata(pdev, led);
277
278 ret = led_classdev_register(&pdev->dev, &led->cdev);
279 if (ret < 0)
280 goto err_led;
281
282 return 0;
283
284 err_led:
285 kfree(led);
286 err_dcdc:
287 regulator_put(dcdc);
288 err_isink:
289 regulator_put(isink);
290 return ret;
291}
292
293static int wm8350_led_remove(struct platform_device *pdev)
294{
295 struct wm8350_led *led = platform_get_drvdata(pdev);
296
297 led_classdev_unregister(&led->cdev);
298 flush_scheduled_work();
299 wm8350_led_disable(led);
300 regulator_put(led->dcdc);
301 regulator_put(led->isink);
302 kfree(led);
303 return 0;
304}
305
306static struct platform_driver wm8350_led_driver = {
307 .driver = {
308 .name = "wm8350-led",
309 .owner = THIS_MODULE,
310 },
311 .probe = wm8350_led_probe,
312 .remove = wm8350_led_remove,
313 .shutdown = wm8350_led_shutdown,
314 .suspend = wm8350_led_suspend,
315 .resume = wm8350_led_resume,
316};
317
318static int __devinit wm8350_led_init(void)
319{
320 return platform_driver_register(&wm8350_led_driver);
321}
322module_init(wm8350_led_init);
323
324static void wm8350_led_exit(void)
325{
326 platform_driver_unregister(&wm8350_led_driver);
327}
328module_exit(wm8350_led_exit);
329
330MODULE_AUTHOR("Mark Brown");
331MODULE_DESCRIPTION("WM8350 LED driver");
332MODULE_LICENSE("GPL");
333MODULE_ALIAS("platform:wm8350-led");
diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c
index 3a273ccef3f2..f92595c8f165 100644
--- a/drivers/mfd/wm8350-core.c
+++ b/drivers/mfd/wm8350-core.c
@@ -1453,6 +1453,9 @@ void wm8350_device_exit(struct wm8350 *wm8350)
1453{ 1453{
1454 int i; 1454 int i;
1455 1455
1456 for (i = 0; i < ARRAY_SIZE(wm8350->pmic.led); i++)
1457 platform_device_unregister(wm8350->pmic.led[i].pdev);
1458
1456 for (i = 0; i < ARRAY_SIZE(wm8350->pmic.pdev); i++) 1459 for (i = 0; i < ARRAY_SIZE(wm8350->pmic.pdev); i++)
1457 platform_device_unregister(wm8350->pmic.pdev[i]); 1460 platform_device_unregister(wm8350->pmic.pdev[i]);
1458 1461
diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c
index c68c496b2c49..7aa35248181b 100644
--- a/drivers/regulator/wm8350-regulator.c
+++ b/drivers/regulator/wm8350-regulator.c
@@ -1412,6 +1412,97 @@ int wm8350_register_regulator(struct wm8350 *wm8350, int reg,
1412} 1412}
1413EXPORT_SYMBOL_GPL(wm8350_register_regulator); 1413EXPORT_SYMBOL_GPL(wm8350_register_regulator);
1414 1414
1415/**
1416 * wm8350_register_led - Register a WM8350 LED output
1417 *
1418 * @param wm8350 The WM8350 device to configure.
1419 * @param lednum LED device index to create.
1420 * @param dcdc The DCDC to use for the LED.
1421 * @param isink The ISINK to use for the LED.
1422 * @param pdata Configuration for the LED.
1423 *
1424 * The WM8350 supports the use of an ISINK together with a DCDC to
1425 * provide a power-efficient LED driver. This function registers the
1426 * regulators and instantiates the platform device for a LED. The
1427 * operating modes for the LED regulators must be configured using
1428 * wm8350_isink_set_flash(), wm8350_dcdc25_set_mode() and
1429 * wm8350_dcdc_set_slot() prior to calling this function.
1430 */
1431int wm8350_register_led(struct wm8350 *wm8350, int lednum, int dcdc, int isink,
1432 struct wm8350_led_platform_data *pdata)
1433{
1434 struct wm8350_led *led;
1435 struct platform_device *pdev;
1436 int ret;
1437
1438 if (lednum > ARRAY_SIZE(wm8350->pmic.led) || lednum < 0) {
1439 dev_err(wm8350->dev, "Invalid LED index %d\n", lednum);
1440 return -ENODEV;
1441 }
1442
1443 led = &wm8350->pmic.led[lednum];
1444
1445 if (led->pdev) {
1446 dev_err(wm8350->dev, "LED %d already allocated\n", lednum);
1447 return -EINVAL;
1448 }
1449
1450 pdev = platform_device_alloc("wm8350-led", lednum);
1451 if (pdev == NULL) {
1452 dev_err(wm8350->dev, "Failed to allocate LED %d\n", lednum);
1453 return -ENOMEM;
1454 }
1455
1456 led->isink_consumer.dev = &pdev->dev;
1457 led->isink_consumer.supply = "led_isink";
1458 led->isink_init.num_consumer_supplies = 1;
1459 led->isink_init.consumer_supplies = &led->isink_consumer;
1460 led->isink_init.constraints.min_uA = 0;
1461 led->isink_init.constraints.max_uA = pdata->max_uA;
1462 led->isink_init.constraints.valid_ops_mask = REGULATOR_CHANGE_CURRENT;
1463 led->isink_init.constraints.valid_modes_mask = REGULATOR_MODE_NORMAL;
1464 ret = wm8350_register_regulator(wm8350, isink, &led->isink_init);
1465 if (ret != 0) {
1466 platform_device_put(pdev);
1467 return ret;
1468 }
1469
1470 led->dcdc_consumer.dev = &pdev->dev;
1471 led->dcdc_consumer.supply = "led_vcc";
1472 led->dcdc_init.num_consumer_supplies = 1;
1473 led->dcdc_init.consumer_supplies = &led->dcdc_consumer;
1474 led->dcdc_init.constraints.valid_modes_mask = REGULATOR_MODE_NORMAL;
1475 ret = wm8350_register_regulator(wm8350, dcdc, &led->dcdc_init);
1476 if (ret != 0) {
1477 platform_device_put(pdev);
1478 return ret;
1479 }
1480
1481 switch (isink) {
1482 case WM8350_ISINK_A:
1483 wm8350->pmic.isink_A_dcdc = dcdc;
1484 break;
1485 case WM8350_ISINK_B:
1486 wm8350->pmic.isink_B_dcdc = dcdc;
1487 break;
1488 }
1489
1490 pdev->dev.platform_data = pdata;
1491 pdev->dev.parent = wm8350->dev;
1492 ret = platform_device_add(pdev);
1493 if (ret != 0) {
1494 dev_err(wm8350->dev, "Failed to register LED %d: %d\n",
1495 lednum, ret);
1496 platform_device_put(pdev);
1497 return ret;
1498 }
1499
1500 led->pdev = pdev;
1501
1502 return 0;
1503}
1504EXPORT_SYMBOL_GPL(wm8350_register_led);
1505
1415static struct platform_driver wm8350_regulator_driver = { 1506static struct platform_driver wm8350_regulator_driver = {
1416 .probe = wm8350_regulator_probe, 1507 .probe = wm8350_regulator_probe,
1417 .remove = wm8350_regulator_remove, 1508 .remove = wm8350_regulator_remove,
diff --git a/include/linux/mfd/wm8350/pmic.h b/include/linux/mfd/wm8350/pmic.h
index 96acbfc8aa12..be3264e286e0 100644
--- a/include/linux/mfd/wm8350/pmic.h
+++ b/include/linux/mfd/wm8350/pmic.h
@@ -13,6 +13,10 @@
13#ifndef __LINUX_MFD_WM8350_PMIC_H 13#ifndef __LINUX_MFD_WM8350_PMIC_H
14#define __LINUX_MFD_WM8350_PMIC_H 14#define __LINUX_MFD_WM8350_PMIC_H
15 15
16#include <linux/platform_device.h>
17#include <linux/leds.h>
18#include <linux/regulator/machine.h>
19
16/* 20/*
17 * Register values. 21 * Register values.
18 */ 22 */
@@ -700,6 +704,33 @@ struct wm8350;
700struct platform_device; 704struct platform_device;
701struct regulator_init_data; 705struct regulator_init_data;
702 706
707/*
708 * WM8350 LED platform data
709 */
710struct wm8350_led_platform_data {
711 const char *name;
712 const char *default_trigger;
713 int max_uA;
714};
715
716struct wm8350_led {
717 struct platform_device *pdev;
718 struct mutex mutex;
719 struct work_struct work;
720 spinlock_t value_lock;
721 enum led_brightness value;
722 struct led_classdev cdev;
723 int max_uA_index;
724 int enabled;
725
726 struct regulator *isink;
727 struct regulator_consumer_supply isink_consumer;
728 struct regulator_init_data isink_init;
729 struct regulator *dcdc;
730 struct regulator_consumer_supply dcdc_consumer;
731 struct regulator_init_data dcdc_init;
732};
733
703struct wm8350_pmic { 734struct wm8350_pmic {
704 /* Number of regulators of each type on this device */ 735 /* Number of regulators of each type on this device */
705 int max_dcdc; 736 int max_dcdc;
@@ -717,10 +748,15 @@ struct wm8350_pmic {
717 748
718 /* regulator devices */ 749 /* regulator devices */
719 struct platform_device *pdev[NUM_WM8350_REGULATORS]; 750 struct platform_device *pdev[NUM_WM8350_REGULATORS];
751
752 /* LED devices */
753 struct wm8350_led led[2];
720}; 754};
721 755
722int wm8350_register_regulator(struct wm8350 *wm8350, int reg, 756int wm8350_register_regulator(struct wm8350 *wm8350, int reg,
723 struct regulator_init_data *initdata); 757 struct regulator_init_data *initdata);
758int wm8350_register_led(struct wm8350 *wm8350, int lednum, int dcdc, int isink,
759 struct wm8350_led_platform_data *pdata);
724 760
725/* 761/*
726 * Additional DCDC control not supported via regulator API 762 * Additional DCDC control not supported via regulator API