diff options
-rw-r--r-- | drivers/leds/Kconfig | 15 | ||||
-rw-r--r-- | drivers/leds/Makefile | 2 | ||||
-rw-r--r-- | drivers/leds/led-class.c | 24 | ||||
-rw-r--r-- | drivers/leds/leds-alix2.c | 181 | ||||
-rw-r--r-- | drivers/leds/leds-ams-delta.c | 33 | ||||
-rw-r--r-- | drivers/leds/leds-clevo-mail.c | 21 | ||||
-rw-r--r-- | drivers/leds/leds-fsg.c | 37 | ||||
-rw-r--r-- | drivers/leds/leds-gpio.c | 36 | ||||
-rw-r--r-- | drivers/leds/leds-hp-disk.c | 20 | ||||
-rw-r--r-- | drivers/leds/leds-hp6xx.c | 22 | ||||
-rw-r--r-- | drivers/leds/leds-net48xx.c | 21 | ||||
-rw-r--r-- | drivers/leds/leds-pca9532.c | 77 | ||||
-rw-r--r-- | drivers/leds/leds-s3c24xx.c | 25 | ||||
-rw-r--r-- | drivers/leds/leds-wm8350.c | 311 | ||||
-rw-r--r-- | drivers/leds/leds-wrap.c | 27 | ||||
-rw-r--r-- | drivers/leds/ledtrig-timer.c | 5 | ||||
-rw-r--r-- | drivers/mfd/wm8350-core.c | 3 | ||||
-rw-r--r-- | drivers/regulator/wm8350-regulator.c | 91 | ||||
-rw-r--r-- | include/linux/leds-pca9532.h | 2 | ||||
-rw-r--r-- | include/linux/leds.h | 5 | ||||
-rw-r--r-- | include/linux/mfd/wm8350/pmic.h | 36 |
21 files changed, 749 insertions, 245 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index e7fb7d2fcbfc..a4a1ae214630 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig | |||
@@ -63,6 +63,12 @@ config LEDS_WRAP | |||
63 | help | 63 | help |
64 | This option enables support for the PCEngines WRAP programmable LEDs. | 64 | This option enables support for the PCEngines WRAP programmable LEDs. |
65 | 65 | ||
66 | config LEDS_ALIX2 | ||
67 | tristate "LED Support for ALIX.2 and ALIX.3 series" | ||
68 | depends on LEDS_CLASS && X86 && EXPERIMENTAL | ||
69 | help | ||
70 | This option enables support for the PCEngines ALIX.2 and ALIX.3 LEDs. | ||
71 | |||
66 | config LEDS_H1940 | 72 | config LEDS_H1940 |
67 | tristate "LED Support for iPAQ H1940 device" | 73 | tristate "LED Support for iPAQ H1940 device" |
68 | depends on LEDS_CLASS && ARCH_H1940 | 74 | depends on LEDS_CLASS && ARCH_H1940 |
@@ -77,7 +83,7 @@ config LEDS_COBALT_QUBE | |||
77 | 83 | ||
78 | config LEDS_COBALT_RAQ | 84 | config LEDS_COBALT_RAQ |
79 | bool "LED Support for the Cobalt Raq series" | 85 | bool "LED Support for the Cobalt Raq series" |
80 | depends on LEDS_CLASS && MIPS_COBALT | 86 | depends on LEDS_CLASS=y && MIPS_COBALT |
81 | select LEDS_TRIGGERS | 87 | select LEDS_TRIGGERS |
82 | help | 88 | help |
83 | This option enables support for the Cobalt Raq series LEDs. | 89 | This option enables support for the Cobalt Raq series LEDs. |
@@ -158,6 +164,13 @@ config LEDS_PCA955X | |||
158 | LED driver chips accessed via the I2C bus. Supported | 164 | LED driver chips accessed via the I2C bus. Supported |
159 | devices include PCA9550, PCA9551, PCA9552, and PCA9553. | 165 | devices include PCA9550, PCA9551, PCA9552, and PCA9553. |
160 | 166 | ||
167 | config 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 | |||
161 | config LEDS_DA903X | 174 | config LEDS_DA903X |
162 | tristate "LED Support for DA9030/DA9034 PMIC" | 175 | tristate "LED Support for DA9030/DA9034 PMIC" |
163 | depends on LEDS_CLASS && PMIC_DA903X | 176 | depends on LEDS_CLASS && PMIC_DA903X |
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index e1967a29850e..bc247cb02e82 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile | |||
@@ -11,6 +11,7 @@ obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o | |||
11 | obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o | 11 | obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o |
12 | obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o | 12 | obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o |
13 | obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o | 13 | obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o |
14 | obj-$(CONFIG_LEDS_ALIX2) += leds-alix2.o | ||
14 | obj-$(CONFIG_LEDS_H1940) += leds-h1940.o | 15 | obj-$(CONFIG_LEDS_H1940) += leds-h1940.o |
15 | obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o | 16 | obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o |
16 | obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o | 17 | obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o |
@@ -23,6 +24,7 @@ obj-$(CONFIG_LEDS_FSG) += leds-fsg.o | |||
23 | obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o | 24 | obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o |
24 | obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o | 25 | obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o |
25 | obj-$(CONFIG_LEDS_HP_DISK) += leds-hp-disk.o | 26 | obj-$(CONFIG_LEDS_HP_DISK) += leds-hp-disk.o |
27 | obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o | ||
26 | 28 | ||
27 | # LED Triggers | 29 | # LED Triggers |
28 | obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o | 30 | obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o |
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 6c4a326176d7..52f82e3ea13a 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c | |||
@@ -91,9 +91,29 @@ void led_classdev_resume(struct led_classdev *led_cdev) | |||
91 | } | 91 | } |
92 | EXPORT_SYMBOL_GPL(led_classdev_resume); | 92 | EXPORT_SYMBOL_GPL(led_classdev_resume); |
93 | 93 | ||
94 | static int led_suspend(struct device *dev, pm_message_t state) | ||
95 | { | ||
96 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
97 | |||
98 | if (led_cdev->flags & LED_CORE_SUSPENDRESUME) | ||
99 | led_classdev_suspend(led_cdev); | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static int led_resume(struct device *dev) | ||
105 | { | ||
106 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
107 | |||
108 | if (led_cdev->flags & LED_CORE_SUSPENDRESUME) | ||
109 | led_classdev_resume(led_cdev); | ||
110 | |||
111 | return 0; | ||
112 | } | ||
113 | |||
94 | /** | 114 | /** |
95 | * led_classdev_register - register a new object of led_classdev class. | 115 | * led_classdev_register - register a new object of led_classdev class. |
96 | * @dev: The device to register. | 116 | * @parent: The device to register. |
97 | * @led_cdev: the led_classdev structure for this device. | 117 | * @led_cdev: the led_classdev structure for this device. |
98 | */ | 118 | */ |
99 | int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) | 119 | int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) |
@@ -174,6 +194,8 @@ static int __init leds_init(void) | |||
174 | leds_class = class_create(THIS_MODULE, "leds"); | 194 | leds_class = class_create(THIS_MODULE, "leds"); |
175 | if (IS_ERR(leds_class)) | 195 | if (IS_ERR(leds_class)) |
176 | return PTR_ERR(leds_class); | 196 | return PTR_ERR(leds_class); |
197 | leds_class->suspend = led_suspend; | ||
198 | leds_class->resume = led_resume; | ||
177 | return 0; | 199 | return 0; |
178 | } | 200 | } |
179 | 201 | ||
diff --git a/drivers/leds/leds-alix2.c b/drivers/leds/leds-alix2.c new file mode 100644 index 000000000000..ddbd7730dfc8 --- /dev/null +++ b/drivers/leds/leds-alix2.c | |||
@@ -0,0 +1,181 @@ | |||
1 | /* | ||
2 | * LEDs driver for PCEngines ALIX.2 and ALIX.3 | ||
3 | * | ||
4 | * Copyright (C) 2008 Constantin Baranov <const@mimas.ru> | ||
5 | */ | ||
6 | |||
7 | #include <linux/err.h> | ||
8 | #include <linux/io.h> | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/leds.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/platform_device.h> | ||
13 | #include <linux/string.h> | ||
14 | |||
15 | static int force = 0; | ||
16 | module_param(force, bool, 0444); | ||
17 | MODULE_PARM_DESC(force, "Assume system has ALIX.2 style LEDs"); | ||
18 | |||
19 | struct alix_led { | ||
20 | struct led_classdev cdev; | ||
21 | unsigned short port; | ||
22 | unsigned int on_value; | ||
23 | unsigned int off_value; | ||
24 | }; | ||
25 | |||
26 | static void alix_led_set(struct led_classdev *led_cdev, | ||
27 | enum led_brightness brightness) | ||
28 | { | ||
29 | struct alix_led *led_dev = | ||
30 | container_of(led_cdev, struct alix_led, cdev); | ||
31 | |||
32 | if (brightness) | ||
33 | outl(led_dev->on_value, led_dev->port); | ||
34 | else | ||
35 | outl(led_dev->off_value, led_dev->port); | ||
36 | } | ||
37 | |||
38 | static struct alix_led alix_leds[] = { | ||
39 | { | ||
40 | .cdev = { | ||
41 | .name = "alix:1", | ||
42 | .brightness_set = alix_led_set, | ||
43 | }, | ||
44 | .port = 0x6100, | ||
45 | .on_value = 1 << 22, | ||
46 | .off_value = 1 << 6, | ||
47 | }, | ||
48 | { | ||
49 | .cdev = { | ||
50 | .name = "alix:2", | ||
51 | .brightness_set = alix_led_set, | ||
52 | }, | ||
53 | .port = 0x6180, | ||
54 | .on_value = 1 << 25, | ||
55 | .off_value = 1 << 9, | ||
56 | }, | ||
57 | { | ||
58 | .cdev = { | ||
59 | .name = "alix:3", | ||
60 | .brightness_set = alix_led_set, | ||
61 | }, | ||
62 | .port = 0x6180, | ||
63 | .on_value = 1 << 27, | ||
64 | .off_value = 1 << 11, | ||
65 | }, | ||
66 | }; | ||
67 | |||
68 | static int __init alix_led_probe(struct platform_device *pdev) | ||
69 | { | ||
70 | int i; | ||
71 | int ret; | ||
72 | |||
73 | for (i = 0; i < ARRAY_SIZE(alix_leds); i++) { | ||
74 | alix_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME; | ||
75 | ret = led_classdev_register(&pdev->dev, &alix_leds[i].cdev); | ||
76 | if (ret < 0) | ||
77 | goto fail; | ||
78 | } | ||
79 | return 0; | ||
80 | |||
81 | fail: | ||
82 | while (--i >= 0) | ||
83 | led_classdev_unregister(&alix_leds[i].cdev); | ||
84 | return ret; | ||
85 | } | ||
86 | |||
87 | static int alix_led_remove(struct platform_device *pdev) | ||
88 | { | ||
89 | int i; | ||
90 | |||
91 | for (i = 0; i < ARRAY_SIZE(alix_leds); i++) | ||
92 | led_classdev_unregister(&alix_leds[i].cdev); | ||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | static struct platform_driver alix_led_driver = { | ||
97 | .remove = alix_led_remove, | ||
98 | .driver = { | ||
99 | .name = KBUILD_MODNAME, | ||
100 | .owner = THIS_MODULE, | ||
101 | }, | ||
102 | }; | ||
103 | |||
104 | static int __init alix_present(void) | ||
105 | { | ||
106 | const unsigned long bios_phys = 0x000f0000; | ||
107 | const size_t bios_len = 0x00010000; | ||
108 | const char alix_sig[] = "PC Engines ALIX."; | ||
109 | const size_t alix_sig_len = sizeof(alix_sig) - 1; | ||
110 | |||
111 | const char *bios_virt; | ||
112 | const char *scan_end; | ||
113 | const char *p; | ||
114 | int ret = 0; | ||
115 | |||
116 | if (force) { | ||
117 | printk(KERN_NOTICE "%s: forced to skip BIOS test, " | ||
118 | "assume system has ALIX.2 style LEDs\n", | ||
119 | KBUILD_MODNAME); | ||
120 | ret = 1; | ||
121 | goto out; | ||
122 | } | ||
123 | |||
124 | bios_virt = phys_to_virt(bios_phys); | ||
125 | scan_end = bios_virt + bios_len - (alix_sig_len + 2); | ||
126 | for (p = bios_virt; p < scan_end; p++) { | ||
127 | const char *tail; | ||
128 | |||
129 | if (memcmp(p, alix_sig, alix_sig_len) != 0) { | ||
130 | continue; | ||
131 | } | ||
132 | |||
133 | tail = p + alix_sig_len; | ||
134 | if ((tail[0] == '2' || tail[0] == '3') && tail[1] == '\0') { | ||
135 | printk(KERN_INFO | ||
136 | "%s: system is recognized as \"%s\"\n", | ||
137 | KBUILD_MODNAME, p); | ||
138 | ret = 1; | ||
139 | break; | ||
140 | } | ||
141 | } | ||
142 | |||
143 | out: | ||
144 | return ret; | ||
145 | } | ||
146 | |||
147 | static struct platform_device *pdev; | ||
148 | |||
149 | static int __init alix_led_init(void) | ||
150 | { | ||
151 | int ret; | ||
152 | |||
153 | if (!alix_present()) { | ||
154 | ret = -ENODEV; | ||
155 | goto out; | ||
156 | } | ||
157 | |||
158 | pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0); | ||
159 | if (!IS_ERR(pdev)) { | ||
160 | ret = platform_driver_probe(&alix_led_driver, alix_led_probe); | ||
161 | if (ret) | ||
162 | platform_device_unregister(pdev); | ||
163 | } else | ||
164 | ret = PTR_ERR(pdev); | ||
165 | |||
166 | out: | ||
167 | return ret; | ||
168 | } | ||
169 | |||
170 | static void __exit alix_led_exit(void) | ||
171 | { | ||
172 | platform_device_unregister(pdev); | ||
173 | platform_driver_unregister(&alix_led_driver); | ||
174 | } | ||
175 | |||
176 | module_init(alix_led_init); | ||
177 | module_exit(alix_led_exit); | ||
178 | |||
179 | MODULE_AUTHOR("Constantin Baranov <const@mimas.ru>"); | ||
180 | MODULE_DESCRIPTION("PCEngines ALIX.2 and ALIX.3 LED driver"); | ||
181 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/leds/leds-ams-delta.c b/drivers/leds/leds-ams-delta.c index 1bd590bb3a6e..446050759b4d 100644 --- a/drivers/leds/leds-ams-delta.c +++ b/drivers/leds/leds-ams-delta.c | |||
@@ -79,37 +79,12 @@ static struct ams_delta_led ams_delta_leds[] = { | |||
79 | }, | 79 | }, |
80 | }; | 80 | }; |
81 | 81 | ||
82 | #ifdef CONFIG_PM | ||
83 | static int ams_delta_led_suspend(struct platform_device *dev, | ||
84 | pm_message_t state) | ||
85 | { | ||
86 | int i; | ||
87 | |||
88 | for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++) | ||
89 | led_classdev_suspend(&ams_delta_leds[i].cdev); | ||
90 | |||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | static int ams_delta_led_resume(struct platform_device *dev) | ||
95 | { | ||
96 | int i; | ||
97 | |||
98 | for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++) | ||
99 | led_classdev_resume(&ams_delta_leds[i].cdev); | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | #else | ||
104 | #define ams_delta_led_suspend NULL | ||
105 | #define ams_delta_led_resume NULL | ||
106 | #endif | ||
107 | |||
108 | static int ams_delta_led_probe(struct platform_device *pdev) | 82 | static int ams_delta_led_probe(struct platform_device *pdev) |
109 | { | 83 | { |
110 | int i, ret; | 84 | int i, ret; |
111 | 85 | ||
112 | for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++) { | 86 | for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++) { |
87 | ams_delta_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME; | ||
113 | ret = led_classdev_register(&pdev->dev, | 88 | ret = led_classdev_register(&pdev->dev, |
114 | &ams_delta_leds[i].cdev); | 89 | &ams_delta_leds[i].cdev); |
115 | if (ret < 0) | 90 | if (ret < 0) |
@@ -127,7 +102,7 @@ static int ams_delta_led_remove(struct platform_device *pdev) | |||
127 | { | 102 | { |
128 | int i; | 103 | int i; |
129 | 104 | ||
130 | for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i--) | 105 | for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++) |
131 | led_classdev_unregister(&ams_delta_leds[i].cdev); | 106 | led_classdev_unregister(&ams_delta_leds[i].cdev); |
132 | 107 | ||
133 | return 0; | 108 | return 0; |
@@ -136,8 +111,6 @@ static int ams_delta_led_remove(struct platform_device *pdev) | |||
136 | static struct platform_driver ams_delta_led_driver = { | 111 | static struct platform_driver ams_delta_led_driver = { |
137 | .probe = ams_delta_led_probe, | 112 | .probe = ams_delta_led_probe, |
138 | .remove = ams_delta_led_remove, | 113 | .remove = ams_delta_led_remove, |
139 | .suspend = ams_delta_led_suspend, | ||
140 | .resume = ams_delta_led_resume, | ||
141 | .driver = { | 114 | .driver = { |
142 | .name = "ams-delta-led", | 115 | .name = "ams-delta-led", |
143 | .owner = THIS_MODULE, | 116 | .owner = THIS_MODULE, |
@@ -151,7 +124,7 @@ static int __init ams_delta_led_init(void) | |||
151 | 124 | ||
152 | static void __exit ams_delta_led_exit(void) | 125 | static void __exit ams_delta_led_exit(void) |
153 | { | 126 | { |
154 | return platform_driver_unregister(&ams_delta_led_driver); | 127 | platform_driver_unregister(&ams_delta_led_driver); |
155 | } | 128 | } |
156 | 129 | ||
157 | module_init(ams_delta_led_init); | 130 | module_init(ams_delta_led_init); |
diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c index eb3415e88f43..1813c84ea5fc 100644 --- a/drivers/leds/leds-clevo-mail.c +++ b/drivers/leds/leds-clevo-mail.c | |||
@@ -142,6 +142,7 @@ static struct led_classdev clevo_mail_led = { | |||
142 | .name = "clevo::mail", | 142 | .name = "clevo::mail", |
143 | .brightness_set = clevo_mail_led_set, | 143 | .brightness_set = clevo_mail_led_set, |
144 | .blink_set = clevo_mail_led_blink, | 144 | .blink_set = clevo_mail_led_blink, |
145 | .flags = LED_CORE_SUSPENDRESUME, | ||
145 | }; | 146 | }; |
146 | 147 | ||
147 | static int __init clevo_mail_led_probe(struct platform_device *pdev) | 148 | static int __init clevo_mail_led_probe(struct platform_device *pdev) |
@@ -155,29 +156,9 @@ static int clevo_mail_led_remove(struct platform_device *pdev) | |||
155 | return 0; | 156 | return 0; |
156 | } | 157 | } |
157 | 158 | ||
158 | #ifdef CONFIG_PM | ||
159 | static int clevo_mail_led_suspend(struct platform_device *dev, | ||
160 | pm_message_t state) | ||
161 | { | ||
162 | led_classdev_suspend(&clevo_mail_led); | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | static int clevo_mail_led_resume(struct platform_device *dev) | ||
167 | { | ||
168 | led_classdev_resume(&clevo_mail_led); | ||
169 | return 0; | ||
170 | } | ||
171 | #else | ||
172 | #define clevo_mail_led_suspend NULL | ||
173 | #define clevo_mail_led_resume NULL | ||
174 | #endif | ||
175 | |||
176 | static struct platform_driver clevo_mail_led_driver = { | 159 | static struct platform_driver clevo_mail_led_driver = { |
177 | .probe = clevo_mail_led_probe, | 160 | .probe = clevo_mail_led_probe, |
178 | .remove = clevo_mail_led_remove, | 161 | .remove = clevo_mail_led_remove, |
179 | .suspend = clevo_mail_led_suspend, | ||
180 | .resume = clevo_mail_led_resume, | ||
181 | .driver = { | 162 | .driver = { |
182 | .name = KBUILD_MODNAME, | 163 | .name = KBUILD_MODNAME, |
183 | .owner = THIS_MODULE, | 164 | .owner = THIS_MODULE, |
diff --git a/drivers/leds/leds-fsg.c b/drivers/leds/leds-fsg.c index 34935155c1c0..5f7c9c5c09b1 100644 --- a/drivers/leds/leds-fsg.c +++ b/drivers/leds/leds-fsg.c | |||
@@ -99,64 +99,43 @@ static void fsg_led_ring_set(struct led_classdev *led_cdev, | |||
99 | } | 99 | } |
100 | 100 | ||
101 | 101 | ||
102 | |||
103 | static struct led_classdev fsg_wlan_led = { | 102 | static struct led_classdev fsg_wlan_led = { |
104 | .name = "fsg:blue:wlan", | 103 | .name = "fsg:blue:wlan", |
105 | .brightness_set = fsg_led_wlan_set, | 104 | .brightness_set = fsg_led_wlan_set, |
105 | .flags = LED_CORE_SUSPENDRESUME, | ||
106 | }; | 106 | }; |
107 | 107 | ||
108 | static struct led_classdev fsg_wan_led = { | 108 | static struct led_classdev fsg_wan_led = { |
109 | .name = "fsg:blue:wan", | 109 | .name = "fsg:blue:wan", |
110 | .brightness_set = fsg_led_wan_set, | 110 | .brightness_set = fsg_led_wan_set, |
111 | .flags = LED_CORE_SUSPENDRESUME, | ||
111 | }; | 112 | }; |
112 | 113 | ||
113 | static struct led_classdev fsg_sata_led = { | 114 | static struct led_classdev fsg_sata_led = { |
114 | .name = "fsg:blue:sata", | 115 | .name = "fsg:blue:sata", |
115 | .brightness_set = fsg_led_sata_set, | 116 | .brightness_set = fsg_led_sata_set, |
117 | .flags = LED_CORE_SUSPENDRESUME, | ||
116 | }; | 118 | }; |
117 | 119 | ||
118 | static struct led_classdev fsg_usb_led = { | 120 | static struct led_classdev fsg_usb_led = { |
119 | .name = "fsg:blue:usb", | 121 | .name = "fsg:blue:usb", |
120 | .brightness_set = fsg_led_usb_set, | 122 | .brightness_set = fsg_led_usb_set, |
123 | .flags = LED_CORE_SUSPENDRESUME, | ||
121 | }; | 124 | }; |
122 | 125 | ||
123 | static struct led_classdev fsg_sync_led = { | 126 | static struct led_classdev fsg_sync_led = { |
124 | .name = "fsg:blue:sync", | 127 | .name = "fsg:blue:sync", |
125 | .brightness_set = fsg_led_sync_set, | 128 | .brightness_set = fsg_led_sync_set, |
129 | .flags = LED_CORE_SUSPENDRESUME, | ||
126 | }; | 130 | }; |
127 | 131 | ||
128 | static struct led_classdev fsg_ring_led = { | 132 | static struct led_classdev fsg_ring_led = { |
129 | .name = "fsg:blue:ring", | 133 | .name = "fsg:blue:ring", |
130 | .brightness_set = fsg_led_ring_set, | 134 | .brightness_set = fsg_led_ring_set, |
135 | .flags = LED_CORE_SUSPENDRESUME, | ||
131 | }; | 136 | }; |
132 | 137 | ||
133 | 138 | ||
134 | |||
135 | #ifdef CONFIG_PM | ||
136 | static int fsg_led_suspend(struct platform_device *dev, pm_message_t state) | ||
137 | { | ||
138 | led_classdev_suspend(&fsg_wlan_led); | ||
139 | led_classdev_suspend(&fsg_wan_led); | ||
140 | led_classdev_suspend(&fsg_sata_led); | ||
141 | led_classdev_suspend(&fsg_usb_led); | ||
142 | led_classdev_suspend(&fsg_sync_led); | ||
143 | led_classdev_suspend(&fsg_ring_led); | ||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | static int fsg_led_resume(struct platform_device *dev) | ||
148 | { | ||
149 | led_classdev_resume(&fsg_wlan_led); | ||
150 | led_classdev_resume(&fsg_wan_led); | ||
151 | led_classdev_resume(&fsg_sata_led); | ||
152 | led_classdev_resume(&fsg_usb_led); | ||
153 | led_classdev_resume(&fsg_sync_led); | ||
154 | led_classdev_resume(&fsg_ring_led); | ||
155 | return 0; | ||
156 | } | ||
157 | #endif | ||
158 | |||
159 | |||
160 | static int fsg_led_probe(struct platform_device *pdev) | 139 | static int fsg_led_probe(struct platform_device *pdev) |
161 | { | 140 | { |
162 | int ret; | 141 | int ret; |
@@ -232,10 +211,6 @@ static int fsg_led_remove(struct platform_device *pdev) | |||
232 | static struct platform_driver fsg_led_driver = { | 211 | static struct platform_driver fsg_led_driver = { |
233 | .probe = fsg_led_probe, | 212 | .probe = fsg_led_probe, |
234 | .remove = fsg_led_remove, | 213 | .remove = fsg_led_remove, |
235 | #ifdef CONFIG_PM | ||
236 | .suspend = fsg_led_suspend, | ||
237 | .resume = fsg_led_resume, | ||
238 | #endif | ||
239 | .driver = { | 214 | .driver = { |
240 | .name = "fsg-led", | 215 | .name = "fsg-led", |
241 | }, | 216 | }, |
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index b13bd2950e95..2e3df08b649b 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c | |||
@@ -105,6 +105,7 @@ static int gpio_led_probe(struct platform_device *pdev) | |||
105 | } | 105 | } |
106 | led_dat->cdev.brightness_set = gpio_led_set; | 106 | led_dat->cdev.brightness_set = gpio_led_set; |
107 | led_dat->cdev.brightness = LED_OFF; | 107 | led_dat->cdev.brightness = LED_OFF; |
108 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | ||
108 | 109 | ||
109 | gpio_direction_output(led_dat->gpio, led_dat->active_low); | 110 | gpio_direction_output(led_dat->gpio, led_dat->active_low); |
110 | 111 | ||
@@ -154,44 +155,9 @@ static int __devexit gpio_led_remove(struct platform_device *pdev) | |||
154 | return 0; | 155 | return 0; |
155 | } | 156 | } |
156 | 157 | ||
157 | #ifdef CONFIG_PM | ||
158 | static int gpio_led_suspend(struct platform_device *pdev, pm_message_t state) | ||
159 | { | ||
160 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; | ||
161 | struct gpio_led_data *leds_data; | ||
162 | int i; | ||
163 | |||
164 | leds_data = platform_get_drvdata(pdev); | ||
165 | |||
166 | for (i = 0; i < pdata->num_leds; i++) | ||
167 | led_classdev_suspend(&leds_data[i].cdev); | ||
168 | |||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | static int gpio_led_resume(struct platform_device *pdev) | ||
173 | { | ||
174 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; | ||
175 | struct gpio_led_data *leds_data; | ||
176 | int i; | ||
177 | |||
178 | leds_data = platform_get_drvdata(pdev); | ||
179 | |||
180 | for (i = 0; i < pdata->num_leds; i++) | ||
181 | led_classdev_resume(&leds_data[i].cdev); | ||
182 | |||
183 | return 0; | ||
184 | } | ||
185 | #else | ||
186 | #define gpio_led_suspend NULL | ||
187 | #define gpio_led_resume NULL | ||
188 | #endif | ||
189 | |||
190 | static struct platform_driver gpio_led_driver = { | 158 | static struct platform_driver gpio_led_driver = { |
191 | .probe = gpio_led_probe, | 159 | .probe = gpio_led_probe, |
192 | .remove = __devexit_p(gpio_led_remove), | 160 | .remove = __devexit_p(gpio_led_remove), |
193 | .suspend = gpio_led_suspend, | ||
194 | .resume = gpio_led_resume, | ||
195 | .driver = { | 161 | .driver = { |
196 | .name = "leds-gpio", | 162 | .name = "leds-gpio", |
197 | .owner = THIS_MODULE, | 163 | .owner = THIS_MODULE, |
diff --git a/drivers/leds/leds-hp-disk.c b/drivers/leds/leds-hp-disk.c index 44fa757d8254..d786adc8c5e3 100644 --- a/drivers/leds/leds-hp-disk.c +++ b/drivers/leds/leds-hp-disk.c | |||
@@ -68,25 +68,9 @@ static struct led_classdev hpled_led = { | |||
68 | .name = "hp:red:hddprotection", | 68 | .name = "hp:red:hddprotection", |
69 | .default_trigger = "heartbeat", | 69 | .default_trigger = "heartbeat", |
70 | .brightness_set = hpled_set, | 70 | .brightness_set = hpled_set, |
71 | .flags = LED_CORE_SUSPENDRESUME, | ||
71 | }; | 72 | }; |
72 | 73 | ||
73 | #ifdef CONFIG_PM | ||
74 | static int hpled_suspend(struct acpi_device *dev, pm_message_t state) | ||
75 | { | ||
76 | led_classdev_suspend(&hpled_led); | ||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static int hpled_resume(struct acpi_device *dev) | ||
81 | { | ||
82 | led_classdev_resume(&hpled_led); | ||
83 | return 0; | ||
84 | } | ||
85 | #else | ||
86 | #define hpled_suspend NULL | ||
87 | #define hpled_resume NULL | ||
88 | #endif | ||
89 | |||
90 | static int hpled_add(struct acpi_device *device) | 74 | static int hpled_add(struct acpi_device *device) |
91 | { | 75 | { |
92 | int ret; | 76 | int ret; |
@@ -121,8 +105,6 @@ static struct acpi_driver leds_hp_driver = { | |||
121 | .ops = { | 105 | .ops = { |
122 | .add = hpled_add, | 106 | .add = hpled_add, |
123 | .remove = hpled_remove, | 107 | .remove = hpled_remove, |
124 | .suspend = hpled_suspend, | ||
125 | .resume = hpled_resume, | ||
126 | } | 108 | } |
127 | }; | 109 | }; |
128 | 110 | ||
diff --git a/drivers/leds/leds-hp6xx.c b/drivers/leds/leds-hp6xx.c index e8fb1baf8a50..e4ce1fd46338 100644 --- a/drivers/leds/leds-hp6xx.c +++ b/drivers/leds/leds-hp6xx.c | |||
@@ -45,30 +45,16 @@ static struct led_classdev hp6xx_red_led = { | |||
45 | .name = "hp6xx:red", | 45 | .name = "hp6xx:red", |
46 | .default_trigger = "hp6xx-charge", | 46 | .default_trigger = "hp6xx-charge", |
47 | .brightness_set = hp6xxled_red_set, | 47 | .brightness_set = hp6xxled_red_set, |
48 | .flags = LED_CORE_SUSPENDRESUME, | ||
48 | }; | 49 | }; |
49 | 50 | ||
50 | static struct led_classdev hp6xx_green_led = { | 51 | static struct led_classdev hp6xx_green_led = { |
51 | .name = "hp6xx:green", | 52 | .name = "hp6xx:green", |
52 | .default_trigger = "ide-disk", | 53 | .default_trigger = "ide-disk", |
53 | .brightness_set = hp6xxled_green_set, | 54 | .brightness_set = hp6xxled_green_set, |
55 | .flags = LED_CORE_SUSPENDRESUME, | ||
54 | }; | 56 | }; |
55 | 57 | ||
56 | #ifdef CONFIG_PM | ||
57 | static int hp6xxled_suspend(struct platform_device *dev, pm_message_t state) | ||
58 | { | ||
59 | led_classdev_suspend(&hp6xx_red_led); | ||
60 | led_classdev_suspend(&hp6xx_green_led); | ||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | static int hp6xxled_resume(struct platform_device *dev) | ||
65 | { | ||
66 | led_classdev_resume(&hp6xx_red_led); | ||
67 | led_classdev_resume(&hp6xx_green_led); | ||
68 | return 0; | ||
69 | } | ||
70 | #endif | ||
71 | |||
72 | static int hp6xxled_probe(struct platform_device *pdev) | 58 | static int hp6xxled_probe(struct platform_device *pdev) |
73 | { | 59 | { |
74 | int ret; | 60 | int ret; |
@@ -98,10 +84,6 @@ MODULE_ALIAS("platform:hp6xx-led"); | |||
98 | static struct platform_driver hp6xxled_driver = { | 84 | static struct platform_driver hp6xxled_driver = { |
99 | .probe = hp6xxled_probe, | 85 | .probe = hp6xxled_probe, |
100 | .remove = hp6xxled_remove, | 86 | .remove = hp6xxled_remove, |
101 | #ifdef CONFIG_PM | ||
102 | .suspend = hp6xxled_suspend, | ||
103 | .resume = hp6xxled_resume, | ||
104 | #endif | ||
105 | .driver = { | 87 | .driver = { |
106 | .name = "hp6xx-led", | 88 | .name = "hp6xx-led", |
107 | .owner = THIS_MODULE, | 89 | .owner = THIS_MODULE, |
diff --git a/drivers/leds/leds-net48xx.c b/drivers/leds/leds-net48xx.c index 054360473c94..93987a12da49 100644 --- a/drivers/leds/leds-net48xx.c +++ b/drivers/leds/leds-net48xx.c | |||
@@ -33,26 +33,9 @@ static void net48xx_error_led_set(struct led_classdev *led_cdev, | |||
33 | static struct led_classdev net48xx_error_led = { | 33 | static struct led_classdev net48xx_error_led = { |
34 | .name = "net48xx::error", | 34 | .name = "net48xx::error", |
35 | .brightness_set = net48xx_error_led_set, | 35 | .brightness_set = net48xx_error_led_set, |
36 | .flags = LED_CORE_SUSPENDRESUME, | ||
36 | }; | 37 | }; |
37 | 38 | ||
38 | #ifdef CONFIG_PM | ||
39 | static int net48xx_led_suspend(struct platform_device *dev, | ||
40 | pm_message_t state) | ||
41 | { | ||
42 | led_classdev_suspend(&net48xx_error_led); | ||
43 | return 0; | ||
44 | } | ||
45 | |||
46 | static int net48xx_led_resume(struct platform_device *dev) | ||
47 | { | ||
48 | led_classdev_resume(&net48xx_error_led); | ||
49 | return 0; | ||
50 | } | ||
51 | #else | ||
52 | #define net48xx_led_suspend NULL | ||
53 | #define net48xx_led_resume NULL | ||
54 | #endif | ||
55 | |||
56 | static int net48xx_led_probe(struct platform_device *pdev) | 39 | static int net48xx_led_probe(struct platform_device *pdev) |
57 | { | 40 | { |
58 | return led_classdev_register(&pdev->dev, &net48xx_error_led); | 41 | return led_classdev_register(&pdev->dev, &net48xx_error_led); |
@@ -67,8 +50,6 @@ static int net48xx_led_remove(struct platform_device *pdev) | |||
67 | static struct platform_driver net48xx_led_driver = { | 50 | static struct platform_driver net48xx_led_driver = { |
68 | .probe = net48xx_led_probe, | 51 | .probe = net48xx_led_probe, |
69 | .remove = net48xx_led_remove, | 52 | .remove = net48xx_led_remove, |
70 | .suspend = net48xx_led_suspend, | ||
71 | .resume = net48xx_led_resume, | ||
72 | .driver = { | 53 | .driver = { |
73 | .name = DRVNAME, | 54 | .name = DRVNAME, |
74 | .owner = THIS_MODULE, | 55 | .owner = THIS_MODULE, |
diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index 4064d4f6b33b..76ec7498e2d5 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/leds.h> | 16 | #include <linux/leds.h> |
17 | #include <linux/input.h> | 17 | #include <linux/input.h> |
18 | #include <linux/mutex.h> | 18 | #include <linux/mutex.h> |
19 | #include <linux/workqueue.h> | ||
19 | #include <linux/leds-pca9532.h> | 20 | #include <linux/leds-pca9532.h> |
20 | 21 | ||
21 | static const unsigned short normal_i2c[] = { /*0x60,*/ I2C_CLIENT_END}; | 22 | static const unsigned short normal_i2c[] = { /*0x60,*/ I2C_CLIENT_END}; |
@@ -34,6 +35,7 @@ struct pca9532_data { | |||
34 | struct pca9532_led leds[16]; | 35 | struct pca9532_led leds[16]; |
35 | struct mutex update_lock; | 36 | struct mutex update_lock; |
36 | struct input_dev *idev; | 37 | struct input_dev *idev; |
38 | struct work_struct work; | ||
37 | u8 pwm[2]; | 39 | u8 pwm[2]; |
38 | u8 psc[2]; | 40 | u8 psc[2]; |
39 | }; | 41 | }; |
@@ -63,7 +65,7 @@ static struct i2c_driver pca9532_driver = { | |||
63 | * as a compromise we average one pwm to the values requested by all | 65 | * as a compromise we average one pwm to the values requested by all |
64 | * leds that are not ON/OFF. | 66 | * leds that are not ON/OFF. |
65 | * */ | 67 | * */ |
66 | static int pca9532_setpwm(struct i2c_client *client, int pwm, int blink, | 68 | static int pca9532_calcpwm(struct i2c_client *client, int pwm, int blink, |
67 | enum led_brightness value) | 69 | enum led_brightness value) |
68 | { | 70 | { |
69 | int a = 0, b = 0, i = 0; | 71 | int a = 0, b = 0, i = 0; |
@@ -84,11 +86,17 @@ static int pca9532_setpwm(struct i2c_client *client, int pwm, int blink, | |||
84 | b = b/a; | 86 | b = b/a; |
85 | if (b > 0xFF) | 87 | if (b > 0xFF) |
86 | return -EINVAL; | 88 | return -EINVAL; |
87 | mutex_lock(&data->update_lock); | ||
88 | data->pwm[pwm] = b; | 89 | data->pwm[pwm] = b; |
90 | data->psc[pwm] = blink; | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | static int pca9532_setpwm(struct i2c_client *client, int pwm) | ||
95 | { | ||
96 | struct pca9532_data *data = i2c_get_clientdata(client); | ||
97 | mutex_lock(&data->update_lock); | ||
89 | i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm), | 98 | i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(pwm), |
90 | data->pwm[pwm]); | 99 | data->pwm[pwm]); |
91 | data->psc[pwm] = blink; | ||
92 | i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm), | 100 | i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(pwm), |
93 | data->psc[pwm]); | 101 | data->psc[pwm]); |
94 | mutex_unlock(&data->update_lock); | 102 | mutex_unlock(&data->update_lock); |
@@ -124,11 +132,11 @@ static void pca9532_set_brightness(struct led_classdev *led_cdev, | |||
124 | led->state = PCA9532_ON; | 132 | led->state = PCA9532_ON; |
125 | else { | 133 | else { |
126 | led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */ | 134 | led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */ |
127 | err = pca9532_setpwm(led->client, 0, 0, value); | 135 | err = pca9532_calcpwm(led->client, 0, 0, value); |
128 | if (err) | 136 | if (err) |
129 | return; /* XXX: led api doesn't allow error code? */ | 137 | return; /* XXX: led api doesn't allow error code? */ |
130 | } | 138 | } |
131 | pca9532_setled(led); | 139 | schedule_work(&led->work); |
132 | } | 140 | } |
133 | 141 | ||
134 | static int pca9532_set_blink(struct led_classdev *led_cdev, | 142 | static int pca9532_set_blink(struct led_classdev *led_cdev, |
@@ -137,6 +145,7 @@ static int pca9532_set_blink(struct led_classdev *led_cdev, | |||
137 | struct pca9532_led *led = ldev_to_led(led_cdev); | 145 | struct pca9532_led *led = ldev_to_led(led_cdev); |
138 | struct i2c_client *client = led->client; | 146 | struct i2c_client *client = led->client; |
139 | int psc; | 147 | int psc; |
148 | int err = 0; | ||
140 | 149 | ||
141 | if (*delay_on == 0 && *delay_off == 0) { | 150 | if (*delay_on == 0 && *delay_off == 0) { |
142 | /* led subsystem ask us for a blink rate */ | 151 | /* led subsystem ask us for a blink rate */ |
@@ -148,11 +157,15 @@ static int pca9532_set_blink(struct led_classdev *led_cdev, | |||
148 | 157 | ||
149 | /* Thecus specific: only use PSC/PWM 0 */ | 158 | /* Thecus specific: only use PSC/PWM 0 */ |
150 | psc = (*delay_on * 152-1)/1000; | 159 | psc = (*delay_on * 152-1)/1000; |
151 | return pca9532_setpwm(client, 0, psc, led_cdev->brightness); | 160 | err = pca9532_calcpwm(client, 0, psc, led_cdev->brightness); |
161 | if (err) | ||
162 | return err; | ||
163 | schedule_work(&led->work); | ||
164 | return 0; | ||
152 | } | 165 | } |
153 | 166 | ||
154 | int pca9532_event(struct input_dev *dev, unsigned int type, unsigned int code, | 167 | static int pca9532_event(struct input_dev *dev, unsigned int type, |
155 | int value) | 168 | unsigned int code, int value) |
156 | { | 169 | { |
157 | struct pca9532_data *data = input_get_drvdata(dev); | 170 | struct pca9532_data *data = input_get_drvdata(dev); |
158 | 171 | ||
@@ -165,13 +178,28 @@ int pca9532_event(struct input_dev *dev, unsigned int type, unsigned int code, | |||
165 | else | 178 | else |
166 | data->pwm[1] = 0; | 179 | data->pwm[1] = 0; |
167 | 180 | ||
168 | dev_info(&dev->dev, "setting beep to %d \n", data->pwm[1]); | 181 | schedule_work(&data->work); |
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static void pca9532_input_work(struct work_struct *work) | ||
187 | { | ||
188 | struct pca9532_data *data; | ||
189 | data = container_of(work, struct pca9532_data, work); | ||
169 | mutex_lock(&data->update_lock); | 190 | mutex_lock(&data->update_lock); |
170 | i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1), | 191 | i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(1), |
171 | data->pwm[1]); | 192 | data->pwm[1]); |
172 | mutex_unlock(&data->update_lock); | 193 | mutex_unlock(&data->update_lock); |
194 | } | ||
173 | 195 | ||
174 | return 0; | 196 | static void pca9532_led_work(struct work_struct *work) |
197 | { | ||
198 | struct pca9532_led *led; | ||
199 | led = container_of(work, struct pca9532_led, work); | ||
200 | if (led->state == PCA9532_PWM0) | ||
201 | pca9532_setpwm(led->client, 0); | ||
202 | pca9532_setled(led); | ||
175 | } | 203 | } |
176 | 204 | ||
177 | static int pca9532_configure(struct i2c_client *client, | 205 | static int pca9532_configure(struct i2c_client *client, |
@@ -204,8 +232,9 @@ static int pca9532_configure(struct i2c_client *client, | |||
204 | led->ldev.brightness = LED_OFF; | 232 | led->ldev.brightness = LED_OFF; |
205 | led->ldev.brightness_set = pca9532_set_brightness; | 233 | led->ldev.brightness_set = pca9532_set_brightness; |
206 | led->ldev.blink_set = pca9532_set_blink; | 234 | led->ldev.blink_set = pca9532_set_blink; |
207 | if (led_classdev_register(&client->dev, | 235 | INIT_WORK(&led->work, pca9532_led_work); |
208 | &led->ldev) < 0) { | 236 | err = led_classdev_register(&client->dev, &led->ldev); |
237 | if (err < 0) { | ||
209 | dev_err(&client->dev, | 238 | dev_err(&client->dev, |
210 | "couldn't register LED %s\n", | 239 | "couldn't register LED %s\n", |
211 | led->name); | 240 | led->name); |
@@ -233,9 +262,11 @@ static int pca9532_configure(struct i2c_client *client, | |||
233 | BIT_MASK(SND_TONE); | 262 | BIT_MASK(SND_TONE); |
234 | data->idev->event = pca9532_event; | 263 | data->idev->event = pca9532_event; |
235 | input_set_drvdata(data->idev, data); | 264 | input_set_drvdata(data->idev, data); |
265 | INIT_WORK(&data->work, pca9532_input_work); | ||
236 | err = input_register_device(data->idev); | 266 | err = input_register_device(data->idev); |
237 | if (err) { | 267 | if (err) { |
238 | input_free_device(data->idev); | 268 | input_free_device(data->idev); |
269 | cancel_work_sync(&data->work); | ||
239 | data->idev = NULL; | 270 | data->idev = NULL; |
240 | goto exit; | 271 | goto exit; |
241 | } | 272 | } |
@@ -252,18 +283,19 @@ exit: | |||
252 | break; | 283 | break; |
253 | case PCA9532_TYPE_LED: | 284 | case PCA9532_TYPE_LED: |
254 | led_classdev_unregister(&data->leds[i].ldev); | 285 | led_classdev_unregister(&data->leds[i].ldev); |
286 | cancel_work_sync(&data->leds[i].work); | ||
255 | break; | 287 | break; |
256 | case PCA9532_TYPE_N2100_BEEP: | 288 | case PCA9532_TYPE_N2100_BEEP: |
257 | if (data->idev != NULL) { | 289 | if (data->idev != NULL) { |
258 | input_unregister_device(data->idev); | 290 | input_unregister_device(data->idev); |
259 | input_free_device(data->idev); | 291 | input_free_device(data->idev); |
292 | cancel_work_sync(&data->work); | ||
260 | data->idev = NULL; | 293 | data->idev = NULL; |
261 | } | 294 | } |
262 | break; | 295 | break; |
263 | } | 296 | } |
264 | 297 | ||
265 | return err; | 298 | return err; |
266 | |||
267 | } | 299 | } |
268 | 300 | ||
269 | static int pca9532_probe(struct i2c_client *client, | 301 | static int pca9532_probe(struct i2c_client *client, |
@@ -271,12 +303,16 @@ static int pca9532_probe(struct i2c_client *client, | |||
271 | { | 303 | { |
272 | struct pca9532_data *data = i2c_get_clientdata(client); | 304 | struct pca9532_data *data = i2c_get_clientdata(client); |
273 | struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data; | 305 | struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data; |
306 | int err; | ||
307 | |||
308 | if (!pca9532_pdata) | ||
309 | return -EIO; | ||
274 | 310 | ||
275 | if (!i2c_check_functionality(client->adapter, | 311 | if (!i2c_check_functionality(client->adapter, |
276 | I2C_FUNC_SMBUS_BYTE_DATA)) | 312 | I2C_FUNC_SMBUS_BYTE_DATA)) |
277 | return -EIO; | 313 | return -EIO; |
278 | 314 | ||
279 | data = kzalloc(sizeof(struct pca9532_data), GFP_KERNEL); | 315 | data = kzalloc(sizeof(*data), GFP_KERNEL); |
280 | if (!data) | 316 | if (!data) |
281 | return -ENOMEM; | 317 | return -ENOMEM; |
282 | 318 | ||
@@ -285,12 +321,13 @@ static int pca9532_probe(struct i2c_client *client, | |||
285 | data->client = client; | 321 | data->client = client; |
286 | mutex_init(&data->update_lock); | 322 | mutex_init(&data->update_lock); |
287 | 323 | ||
288 | if (pca9532_pdata == NULL) | 324 | err = pca9532_configure(client, data, pca9532_pdata); |
289 | return -EIO; | 325 | if (err) { |
290 | 326 | kfree(data); | |
291 | pca9532_configure(client, data, pca9532_pdata); | 327 | i2c_set_clientdata(client, NULL); |
292 | return 0; | 328 | } |
293 | 329 | ||
330 | return err; | ||
294 | } | 331 | } |
295 | 332 | ||
296 | static int pca9532_remove(struct i2c_client *client) | 333 | static int pca9532_remove(struct i2c_client *client) |
@@ -303,11 +340,13 @@ static int pca9532_remove(struct i2c_client *client) | |||
303 | break; | 340 | break; |
304 | case PCA9532_TYPE_LED: | 341 | case PCA9532_TYPE_LED: |
305 | led_classdev_unregister(&data->leds[i].ldev); | 342 | led_classdev_unregister(&data->leds[i].ldev); |
343 | cancel_work_sync(&data->leds[i].work); | ||
306 | break; | 344 | break; |
307 | case PCA9532_TYPE_N2100_BEEP: | 345 | case PCA9532_TYPE_N2100_BEEP: |
308 | if (data->idev != NULL) { | 346 | if (data->idev != NULL) { |
309 | input_unregister_device(data->idev); | 347 | input_unregister_device(data->idev); |
310 | input_free_device(data->idev); | 348 | input_free_device(data->idev); |
349 | cancel_work_sync(&data->work); | ||
311 | data->idev = NULL; | 350 | data->idev = NULL; |
312 | } | 351 | } |
313 | break; | 352 | break; |
diff --git a/drivers/leds/leds-s3c24xx.c b/drivers/leds/leds-s3c24xx.c index 25a07f2643ad..4d81131542ae 100644 --- a/drivers/leds/leds-s3c24xx.c +++ b/drivers/leds/leds-s3c24xx.c | |||
@@ -82,6 +82,7 @@ static int s3c24xx_led_probe(struct platform_device *dev) | |||
82 | led->cdev.brightness_set = s3c24xx_led_set; | 82 | led->cdev.brightness_set = s3c24xx_led_set; |
83 | led->cdev.default_trigger = pdata->def_trigger; | 83 | led->cdev.default_trigger = pdata->def_trigger; |
84 | led->cdev.name = pdata->name; | 84 | led->cdev.name = pdata->name; |
85 | led->cdev.flags |= LED_CORE_SUSPENDRESUME; | ||
85 | 86 | ||
86 | led->pdata = pdata; | 87 | led->pdata = pdata; |
87 | 88 | ||
@@ -111,33 +112,9 @@ static int s3c24xx_led_probe(struct platform_device *dev) | |||
111 | return ret; | 112 | return ret; |
112 | } | 113 | } |
113 | 114 | ||
114 | |||
115 | #ifdef CONFIG_PM | ||
116 | static int s3c24xx_led_suspend(struct platform_device *dev, pm_message_t state) | ||
117 | { | ||
118 | struct s3c24xx_gpio_led *led = pdev_to_gpio(dev); | ||
119 | |||
120 | led_classdev_suspend(&led->cdev); | ||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | static int s3c24xx_led_resume(struct platform_device *dev) | ||
125 | { | ||
126 | struct s3c24xx_gpio_led *led = pdev_to_gpio(dev); | ||
127 | |||
128 | led_classdev_resume(&led->cdev); | ||
129 | return 0; | ||
130 | } | ||
131 | #else | ||
132 | #define s3c24xx_led_suspend NULL | ||
133 | #define s3c24xx_led_resume NULL | ||
134 | #endif | ||
135 | |||
136 | static struct platform_driver s3c24xx_led_driver = { | 115 | static struct platform_driver s3c24xx_led_driver = { |
137 | .probe = s3c24xx_led_probe, | 116 | .probe = s3c24xx_led_probe, |
138 | .remove = s3c24xx_led_remove, | 117 | .remove = s3c24xx_led_remove, |
139 | .suspend = s3c24xx_led_suspend, | ||
140 | .resume = s3c24xx_led_resume, | ||
141 | .driver = { | 118 | .driver = { |
142 | .name = "s3c24xx_led", | 119 | .name = "s3c24xx_led", |
143 | .owner = THIS_MODULE, | 120 | .owner = THIS_MODULE, |
diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c new file mode 100644 index 000000000000..38c6bcb07e6c --- /dev/null +++ b/drivers/leds/leds-wm8350.c | |||
@@ -0,0 +1,311 @@ | |||
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 */ | ||
21 | static 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 | |||
91 | static 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 | |||
114 | static 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 | |||
137 | static 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 | |||
171 | out: | ||
172 | mutex_unlock(&led->mutex); | ||
173 | } | ||
174 | |||
175 | static 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 | static void wm8350_led_shutdown(struct platform_device *pdev) | ||
188 | { | ||
189 | struct wm8350_led *led = platform_get_drvdata(pdev); | ||
190 | |||
191 | mutex_lock(&led->mutex); | ||
192 | led->value = LED_OFF; | ||
193 | wm8350_led_disable(led); | ||
194 | mutex_unlock(&led->mutex); | ||
195 | } | ||
196 | |||
197 | static int wm8350_led_probe(struct platform_device *pdev) | ||
198 | { | ||
199 | struct regulator *isink, *dcdc; | ||
200 | struct wm8350_led *led; | ||
201 | struct wm8350_led_platform_data *pdata = pdev->dev.platform_data; | ||
202 | int ret, i; | ||
203 | |||
204 | if (pdata == NULL) { | ||
205 | dev_err(&pdev->dev, "no platform data\n"); | ||
206 | return -ENODEV; | ||
207 | } | ||
208 | |||
209 | if (pdata->max_uA < isink_cur[0]) { | ||
210 | dev_err(&pdev->dev, "Invalid maximum current %duA\n", | ||
211 | pdata->max_uA); | ||
212 | return -EINVAL; | ||
213 | } | ||
214 | |||
215 | isink = regulator_get(&pdev->dev, "led_isink"); | ||
216 | if (IS_ERR(isink)) { | ||
217 | printk(KERN_ERR "%s: cant get ISINK\n", __func__); | ||
218 | return PTR_ERR(isink); | ||
219 | } | ||
220 | |||
221 | dcdc = regulator_get(&pdev->dev, "led_vcc"); | ||
222 | if (IS_ERR(dcdc)) { | ||
223 | printk(KERN_ERR "%s: cant get DCDC\n", __func__); | ||
224 | ret = PTR_ERR(dcdc); | ||
225 | goto err_isink; | ||
226 | } | ||
227 | |||
228 | led = kzalloc(sizeof(*led), GFP_KERNEL); | ||
229 | if (led == NULL) { | ||
230 | ret = -ENOMEM; | ||
231 | goto err_dcdc; | ||
232 | } | ||
233 | |||
234 | led->cdev.brightness_set = wm8350_led_set; | ||
235 | led->cdev.default_trigger = pdata->default_trigger; | ||
236 | led->cdev.name = pdata->name; | ||
237 | led->cdev.flags |= LED_CORE_SUSPENDRESUME; | ||
238 | led->enabled = regulator_is_enabled(isink); | ||
239 | led->isink = isink; | ||
240 | led->dcdc = dcdc; | ||
241 | |||
242 | for (i = 0; i < ARRAY_SIZE(isink_cur) - 1; i++) | ||
243 | if (isink_cur[i] >= pdata->max_uA) | ||
244 | break; | ||
245 | led->max_uA_index = i; | ||
246 | if (pdata->max_uA != isink_cur[i]) | ||
247 | dev_warn(&pdev->dev, | ||
248 | "Maximum current %duA is not directly supported," | ||
249 | " check platform data\n", | ||
250 | pdata->max_uA); | ||
251 | |||
252 | spin_lock_init(&led->value_lock); | ||
253 | mutex_init(&led->mutex); | ||
254 | INIT_WORK(&led->work, led_work); | ||
255 | led->value = LED_OFF; | ||
256 | platform_set_drvdata(pdev, led); | ||
257 | |||
258 | ret = led_classdev_register(&pdev->dev, &led->cdev); | ||
259 | if (ret < 0) | ||
260 | goto err_led; | ||
261 | |||
262 | return 0; | ||
263 | |||
264 | err_led: | ||
265 | kfree(led); | ||
266 | err_dcdc: | ||
267 | regulator_put(dcdc); | ||
268 | err_isink: | ||
269 | regulator_put(isink); | ||
270 | return ret; | ||
271 | } | ||
272 | |||
273 | static int wm8350_led_remove(struct platform_device *pdev) | ||
274 | { | ||
275 | struct wm8350_led *led = platform_get_drvdata(pdev); | ||
276 | |||
277 | led_classdev_unregister(&led->cdev); | ||
278 | flush_scheduled_work(); | ||
279 | wm8350_led_disable(led); | ||
280 | regulator_put(led->dcdc); | ||
281 | regulator_put(led->isink); | ||
282 | kfree(led); | ||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | static struct platform_driver wm8350_led_driver = { | ||
287 | .driver = { | ||
288 | .name = "wm8350-led", | ||
289 | .owner = THIS_MODULE, | ||
290 | }, | ||
291 | .probe = wm8350_led_probe, | ||
292 | .remove = wm8350_led_remove, | ||
293 | .shutdown = wm8350_led_shutdown, | ||
294 | }; | ||
295 | |||
296 | static int __devinit wm8350_led_init(void) | ||
297 | { | ||
298 | return platform_driver_register(&wm8350_led_driver); | ||
299 | } | ||
300 | module_init(wm8350_led_init); | ||
301 | |||
302 | static void wm8350_led_exit(void) | ||
303 | { | ||
304 | platform_driver_unregister(&wm8350_led_driver); | ||
305 | } | ||
306 | module_exit(wm8350_led_exit); | ||
307 | |||
308 | MODULE_AUTHOR("Mark Brown"); | ||
309 | MODULE_DESCRIPTION("WM8350 LED driver"); | ||
310 | MODULE_LICENSE("GPL"); | ||
311 | MODULE_ALIAS("platform:wm8350-led"); | ||
diff --git a/drivers/leds/leds-wrap.c b/drivers/leds/leds-wrap.c index 2f3aa87f2a1f..2982c86ac4cf 100644 --- a/drivers/leds/leds-wrap.c +++ b/drivers/leds/leds-wrap.c | |||
@@ -56,40 +56,21 @@ static struct led_classdev wrap_power_led = { | |||
56 | .name = "wrap::power", | 56 | .name = "wrap::power", |
57 | .brightness_set = wrap_power_led_set, | 57 | .brightness_set = wrap_power_led_set, |
58 | .default_trigger = "default-on", | 58 | .default_trigger = "default-on", |
59 | .flags = LED_CORE_SUSPENDRESUME, | ||
59 | }; | 60 | }; |
60 | 61 | ||
61 | static struct led_classdev wrap_error_led = { | 62 | static struct led_classdev wrap_error_led = { |
62 | .name = "wrap::error", | 63 | .name = "wrap::error", |
63 | .brightness_set = wrap_error_led_set, | 64 | .brightness_set = wrap_error_led_set, |
65 | .flags = LED_CORE_SUSPENDRESUME, | ||
64 | }; | 66 | }; |
65 | 67 | ||
66 | static struct led_classdev wrap_extra_led = { | 68 | static struct led_classdev wrap_extra_led = { |
67 | .name = "wrap::extra", | 69 | .name = "wrap::extra", |
68 | .brightness_set = wrap_extra_led_set, | 70 | .brightness_set = wrap_extra_led_set, |
71 | .flags = LED_CORE_SUSPENDRESUME, | ||
69 | }; | 72 | }; |
70 | 73 | ||
71 | #ifdef CONFIG_PM | ||
72 | static int wrap_led_suspend(struct platform_device *dev, | ||
73 | pm_message_t state) | ||
74 | { | ||
75 | led_classdev_suspend(&wrap_power_led); | ||
76 | led_classdev_suspend(&wrap_error_led); | ||
77 | led_classdev_suspend(&wrap_extra_led); | ||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | static int wrap_led_resume(struct platform_device *dev) | ||
82 | { | ||
83 | led_classdev_resume(&wrap_power_led); | ||
84 | led_classdev_resume(&wrap_error_led); | ||
85 | led_classdev_resume(&wrap_extra_led); | ||
86 | return 0; | ||
87 | } | ||
88 | #else | ||
89 | #define wrap_led_suspend NULL | ||
90 | #define wrap_led_resume NULL | ||
91 | #endif | ||
92 | |||
93 | static int wrap_led_probe(struct platform_device *pdev) | 74 | static int wrap_led_probe(struct platform_device *pdev) |
94 | { | 75 | { |
95 | int ret; | 76 | int ret; |
@@ -127,8 +108,6 @@ static int wrap_led_remove(struct platform_device *pdev) | |||
127 | static struct platform_driver wrap_led_driver = { | 108 | static struct platform_driver wrap_led_driver = { |
128 | .probe = wrap_led_probe, | 109 | .probe = wrap_led_probe, |
129 | .remove = wrap_led_remove, | 110 | .remove = wrap_led_remove, |
130 | .suspend = wrap_led_suspend, | ||
131 | .resume = wrap_led_resume, | ||
132 | .driver = { | 111 | .driver = { |
133 | .name = DRVNAME, | 112 | .name = DRVNAME, |
134 | .owner = THIS_MODULE, | 113 | .owner = THIS_MODULE, |
diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c index db681962d7bb..3d6531396dda 100644 --- a/drivers/leds/ledtrig-timer.c +++ b/drivers/leds/ledtrig-timer.c | |||
@@ -199,6 +199,7 @@ err_out: | |||
199 | static void timer_trig_deactivate(struct led_classdev *led_cdev) | 199 | static void timer_trig_deactivate(struct led_classdev *led_cdev) |
200 | { | 200 | { |
201 | struct timer_trig_data *timer_data = led_cdev->trigger_data; | 201 | struct timer_trig_data *timer_data = led_cdev->trigger_data; |
202 | unsigned long on = 0, off = 0; | ||
202 | 203 | ||
203 | if (timer_data) { | 204 | if (timer_data) { |
204 | device_remove_file(led_cdev->dev, &dev_attr_delay_on); | 205 | device_remove_file(led_cdev->dev, &dev_attr_delay_on); |
@@ -206,6 +207,10 @@ static void timer_trig_deactivate(struct led_classdev *led_cdev) | |||
206 | del_timer_sync(&timer_data->timer); | 207 | del_timer_sync(&timer_data->timer); |
207 | kfree(timer_data); | 208 | kfree(timer_data); |
208 | } | 209 | } |
210 | |||
211 | /* If there is hardware support for blinking, stop it */ | ||
212 | if (led_cdev->blink_set) | ||
213 | led_cdev->blink_set(led_cdev, &on, &off); | ||
209 | } | 214 | } |
210 | 215 | ||
211 | static struct led_trigger timer_led_trigger = { | 216 | static struct led_trigger timer_led_trigger = { |
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 | } |
1413 | EXPORT_SYMBOL_GPL(wm8350_register_regulator); | 1413 | EXPORT_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 | */ | ||
1431 | int 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 | } | ||
1504 | EXPORT_SYMBOL_GPL(wm8350_register_led); | ||
1505 | |||
1415 | static struct platform_driver wm8350_regulator_driver = { | 1506 | static 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/leds-pca9532.h b/include/linux/leds-pca9532.h index 81b4207deb95..96eea90f01a8 100644 --- a/include/linux/leds-pca9532.h +++ b/include/linux/leds-pca9532.h | |||
@@ -15,6 +15,7 @@ | |||
15 | #define __LINUX_PCA9532_H | 15 | #define __LINUX_PCA9532_H |
16 | 16 | ||
17 | #include <linux/leds.h> | 17 | #include <linux/leds.h> |
18 | #include <linux/workqueue.h> | ||
18 | 19 | ||
19 | enum pca9532_state { | 20 | enum pca9532_state { |
20 | PCA9532_OFF = 0x0, | 21 | PCA9532_OFF = 0x0, |
@@ -31,6 +32,7 @@ struct pca9532_led { | |||
31 | struct i2c_client *client; | 32 | struct i2c_client *client; |
32 | char *name; | 33 | char *name; |
33 | struct led_classdev ldev; | 34 | struct led_classdev ldev; |
35 | struct work_struct work; | ||
34 | enum pca9532_type type; | 36 | enum pca9532_type type; |
35 | enum pca9532_state state; | 37 | enum pca9532_state state; |
36 | }; | 38 | }; |
diff --git a/include/linux/leds.h b/include/linux/leds.h index d3a73f5a48c3..24489da701e3 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h | |||
@@ -32,7 +32,10 @@ struct led_classdev { | |||
32 | int brightness; | 32 | int brightness; |
33 | int flags; | 33 | int flags; |
34 | 34 | ||
35 | /* Lower 16 bits reflect status */ | ||
35 | #define LED_SUSPENDED (1 << 0) | 36 | #define LED_SUSPENDED (1 << 0) |
37 | /* Upper 16 bits reflect control information */ | ||
38 | #define LED_CORE_SUSPENDRESUME (1 << 16) | ||
36 | 39 | ||
37 | /* Set LED brightness level */ | 40 | /* Set LED brightness level */ |
38 | /* Must not sleep, use a workqueue if needed */ | 41 | /* Must not sleep, use a workqueue if needed */ |
@@ -62,7 +65,7 @@ struct led_classdev { | |||
62 | 65 | ||
63 | extern int led_classdev_register(struct device *parent, | 66 | extern int led_classdev_register(struct device *parent, |
64 | struct led_classdev *led_cdev); | 67 | struct led_classdev *led_cdev); |
65 | extern void led_classdev_unregister(struct led_classdev *lcd); | 68 | extern void led_classdev_unregister(struct led_classdev *led_cdev); |
66 | extern void led_classdev_suspend(struct led_classdev *led_cdev); | 69 | extern void led_classdev_suspend(struct led_classdev *led_cdev); |
67 | extern void led_classdev_resume(struct led_classdev *led_cdev); | 70 | extern void led_classdev_resume(struct led_classdev *led_cdev); |
68 | 71 | ||
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; | |||
700 | struct platform_device; | 704 | struct platform_device; |
701 | struct regulator_init_data; | 705 | struct regulator_init_data; |
702 | 706 | ||
707 | /* | ||
708 | * WM8350 LED platform data | ||
709 | */ | ||
710 | struct wm8350_led_platform_data { | ||
711 | const char *name; | ||
712 | const char *default_trigger; | ||
713 | int max_uA; | ||
714 | }; | ||
715 | |||
716 | struct 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 | |||
703 | struct wm8350_pmic { | 734 | struct 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 | ||
722 | int wm8350_register_regulator(struct wm8350 *wm8350, int reg, | 756 | int wm8350_register_regulator(struct wm8350 *wm8350, int reg, |
723 | struct regulator_init_data *initdata); | 757 | struct regulator_init_data *initdata); |
758 | int 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 |