diff options
author | Paul Mundt <lethal@linux-sh.org> | 2009-04-13 17:29:07 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2009-04-13 17:29:07 -0400 |
commit | f499cae1e59d75d5eb24c23d47cf8986e6032c6d (patch) | |
tree | 1af6235c18391212c40116eb90b01eae8938efee /drivers/leds | |
parent | fc3f55e672e1ed917dd9e215af81939cd3d717da (diff) | |
parent | 80a04d3f2f94fb68b5df05e3ac6697130bc3467a (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'drivers/leds')
-rw-r--r-- | drivers/leds/Kconfig | 75 | ||||
-rw-r--r-- | drivers/leds/Makefile | 7 | ||||
-rw-r--r-- | drivers/leds/led-class.c | 21 | ||||
-rw-r--r-- | drivers/leds/led-triggers.c | 10 | ||||
-rw-r--r-- | drivers/leds/leds-bd2802.c | 765 | ||||
-rw-r--r-- | drivers/leds/leds-dac124s085.c | 150 | ||||
-rw-r--r-- | drivers/leds/leds-gpio.c | 225 | ||||
-rw-r--r-- | drivers/leds/leds-h1940.c | 2 | ||||
-rw-r--r-- | drivers/leds/leds-pca9532.c | 2 | ||||
-rw-r--r-- | drivers/leds/leds-pwm.c | 153 | ||||
-rw-r--r-- | drivers/leds/leds-rb532.c | 77 | ||||
-rw-r--r-- | drivers/leds/leds-s3c24xx.c | 7 | ||||
-rw-r--r-- | drivers/leds/leds.h | 4 | ||||
-rw-r--r-- | drivers/leds/ledtrig-default-on.c | 2 | ||||
-rw-r--r-- | drivers/leds/ledtrig-gpio.c | 239 | ||||
-rw-r--r-- | drivers/leds/ledtrig-heartbeat.c | 4 | ||||
-rw-r--r-- | drivers/leds/ledtrig-ide-disk.c | 3 | ||||
-rw-r--r-- | drivers/leds/ledtrig-timer.c | 2 |
18 files changed, 1685 insertions, 63 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index d9db17624f12..9b60b6b684d9 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig | |||
@@ -31,6 +31,13 @@ config LEDS_LOCOMO | |||
31 | This option enables support for the LEDs on Sharp Locomo. | 31 | This option enables support for the LEDs on Sharp Locomo. |
32 | Zaurus models SL-5500 and SL-5600. | 32 | Zaurus models SL-5500 and SL-5600. |
33 | 33 | ||
34 | config LEDS_MIKROTIK_RB532 | ||
35 | tristate "LED Support for Mikrotik Routerboard 532" | ||
36 | depends on LEDS_CLASS && MIKROTIK_RB532 | ||
37 | help | ||
38 | This option enables support for the so called "User LED" of | ||
39 | Mikrotik's Routerboard 532. | ||
40 | |||
34 | config LEDS_S3C24XX | 41 | config LEDS_S3C24XX |
35 | tristate "LED Support for Samsung S3C24XX GPIO LEDs" | 42 | tristate "LED Support for Samsung S3C24XX GPIO LEDs" |
36 | depends on LEDS_CLASS && ARCH_S3C2410 | 43 | depends on LEDS_CLASS && ARCH_S3C2410 |
@@ -117,11 +124,40 @@ config LEDS_GPIO | |||
117 | help | 124 | help |
118 | This option enables support for the LEDs connected to GPIO | 125 | This option enables support for the LEDs connected to GPIO |
119 | outputs. To be useful the particular board must have LEDs | 126 | outputs. To be useful the particular board must have LEDs |
120 | and they must be connected to the GPIO lines. | 127 | and they must be connected to the GPIO lines. The LEDs must be |
128 | defined as platform devices and/or OpenFirmware platform devices. | ||
129 | The code to use these bindings can be selected below. | ||
130 | |||
131 | config LEDS_GPIO_PLATFORM | ||
132 | bool "Platform device bindings for GPIO LEDs" | ||
133 | depends on LEDS_GPIO | ||
134 | default y | ||
135 | help | ||
136 | Let the leds-gpio driver drive LEDs which have been defined as | ||
137 | platform devices. If you don't know what this means, say yes. | ||
138 | |||
139 | config LEDS_GPIO_OF | ||
140 | bool "OpenFirmware platform device bindings for GPIO LEDs" | ||
141 | depends on LEDS_GPIO && OF_DEVICE | ||
142 | default y | ||
143 | help | ||
144 | Let the leds-gpio driver drive LEDs which have been defined as | ||
145 | of_platform devices. For instance, LEDs which are listed in a "dts" | ||
146 | file. | ||
147 | |||
148 | config LEDS_LP5521 | ||
149 | tristate "LED Support for the LP5521 LEDs" | ||
150 | depends on LEDS_CLASS && I2C | ||
151 | help | ||
152 | If you say 'Y' here you get support for the National Semiconductor | ||
153 | LP5521 LED driver used in n8x0 boards. | ||
154 | |||
155 | This driver can be built as a module by choosing 'M'. The module | ||
156 | will be called leds-lp5521. | ||
121 | 157 | ||
122 | config LEDS_CLEVO_MAIL | 158 | config LEDS_CLEVO_MAIL |
123 | tristate "Mail LED on Clevo notebook (EXPERIMENTAL)" | 159 | tristate "Mail LED on Clevo notebook" |
124 | depends on LEDS_CLASS && X86 && SERIO_I8042 && DMI && EXPERIMENTAL | 160 | depends on LEDS_CLASS && X86 && SERIO_I8042 && DMI |
125 | help | 161 | help |
126 | This driver makes the mail LED accessible from userspace | 162 | This driver makes the mail LED accessible from userspace |
127 | programs through the leds subsystem. This LED have three | 163 | programs through the leds subsystem. This LED have three |
@@ -171,6 +207,26 @@ config LEDS_DA903X | |||
171 | This option enables support for on-chip LED drivers found | 207 | This option enables support for on-chip LED drivers found |
172 | on Dialog Semiconductor DA9030/DA9034 PMICs. | 208 | on Dialog Semiconductor DA9030/DA9034 PMICs. |
173 | 209 | ||
210 | config LEDS_DAC124S085 | ||
211 | tristate "LED Support for DAC124S085 SPI DAC" | ||
212 | depends on LEDS_CLASS && SPI | ||
213 | help | ||
214 | This option enables support for DAC124S085 SPI DAC from NatSemi, | ||
215 | which can be used to control up to four LEDs. | ||
216 | |||
217 | config LEDS_PWM | ||
218 | tristate "PWM driven LED Support" | ||
219 | depends on LEDS_CLASS && HAVE_PWM | ||
220 | help | ||
221 | This option enables support for pwm driven LEDs | ||
222 | |||
223 | config LEDS_BD2802 | ||
224 | tristate "LED driver for BD2802 RGB LED" | ||
225 | depends on LEDS_CLASS && I2C | ||
226 | help | ||
227 | This option enables support for BD2802GU RGB LED driver chips | ||
228 | accessed via the I2C bus. | ||
229 | |||
174 | comment "LED Triggers" | 230 | comment "LED Triggers" |
175 | 231 | ||
176 | config LEDS_TRIGGERS | 232 | config LEDS_TRIGGERS |
@@ -216,6 +272,19 @@ config LEDS_TRIGGER_BACKLIGHT | |||
216 | 272 | ||
217 | If unsure, say N. | 273 | If unsure, say N. |
218 | 274 | ||
275 | config LEDS_TRIGGER_GPIO | ||
276 | tristate "LED GPIO Trigger" | ||
277 | depends on LEDS_TRIGGERS | ||
278 | depends on GPIOLIB | ||
279 | help | ||
280 | This allows LEDs to be controlled by gpio events. It's good | ||
281 | when using gpios as switches and triggering the needed LEDs | ||
282 | from there. One use case is n810's keypad LEDs that could | ||
283 | be triggered by this trigger when user slides up to show | ||
284 | keypad. | ||
285 | |||
286 | If unsure, say N. | ||
287 | |||
219 | config LEDS_TRIGGER_DEFAULT_ON | 288 | config LEDS_TRIGGER_DEFAULT_ON |
220 | tristate "LED Default ON Trigger" | 289 | tristate "LED Default ON Trigger" |
221 | depends on LEDS_TRIGGERS | 290 | depends on LEDS_TRIGGERS |
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 9d76f0f160a4..2d41c4dcf92f 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile | |||
@@ -6,7 +6,9 @@ obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o | |||
6 | 6 | ||
7 | # LED Platform Drivers | 7 | # LED Platform Drivers |
8 | obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o | 8 | obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o |
9 | obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o | ||
9 | obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o | 10 | obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o |
11 | obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o | ||
10 | obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o | 12 | obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o |
11 | obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o | 13 | obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o |
12 | obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o | 14 | obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o |
@@ -24,10 +26,15 @@ obj-$(CONFIG_LEDS_FSG) += leds-fsg.o | |||
24 | obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o | 26 | obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o |
25 | obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o | 27 | obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o |
26 | obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o | 28 | obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o |
29 | obj-$(CONFIG_LEDS_PWM) += leds-pwm.o | ||
30 | |||
31 | # LED SPI Drivers | ||
32 | obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o | ||
27 | 33 | ||
28 | # LED Triggers | 34 | # LED Triggers |
29 | obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o | 35 | obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o |
30 | obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o | 36 | obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o |
31 | obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o | 37 | obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o |
32 | obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o | 38 | obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o |
39 | obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o | ||
33 | obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o | 40 | obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o |
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 52f82e3ea13a..f2cc13d76810 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c | |||
@@ -64,7 +64,16 @@ static ssize_t led_brightness_store(struct device *dev, | |||
64 | return ret; | 64 | return ret; |
65 | } | 65 | } |
66 | 66 | ||
67 | static ssize_t led_max_brightness_show(struct device *dev, | ||
68 | struct device_attribute *attr, char *buf) | ||
69 | { | ||
70 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
71 | |||
72 | return sprintf(buf, "%u\n", led_cdev->max_brightness); | ||
73 | } | ||
74 | |||
67 | static DEVICE_ATTR(brightness, 0644, led_brightness_show, led_brightness_store); | 75 | static DEVICE_ATTR(brightness, 0644, led_brightness_show, led_brightness_store); |
76 | static DEVICE_ATTR(max_brightness, 0444, led_max_brightness_show, NULL); | ||
68 | #ifdef CONFIG_LEDS_TRIGGERS | 77 | #ifdef CONFIG_LEDS_TRIGGERS |
69 | static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store); | 78 | static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store); |
70 | #endif | 79 | #endif |
@@ -138,6 +147,13 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) | |||
138 | list_add_tail(&led_cdev->node, &leds_list); | 147 | list_add_tail(&led_cdev->node, &leds_list); |
139 | up_write(&leds_list_lock); | 148 | up_write(&leds_list_lock); |
140 | 149 | ||
150 | if (!led_cdev->max_brightness) | ||
151 | led_cdev->max_brightness = LED_FULL; | ||
152 | |||
153 | rc = device_create_file(led_cdev->dev, &dev_attr_max_brightness); | ||
154 | if (rc) | ||
155 | goto err_out_attr_max; | ||
156 | |||
141 | led_update_brightness(led_cdev); | 157 | led_update_brightness(led_cdev); |
142 | 158 | ||
143 | #ifdef CONFIG_LEDS_TRIGGERS | 159 | #ifdef CONFIG_LEDS_TRIGGERS |
@@ -155,9 +171,11 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) | |||
155 | 171 | ||
156 | #ifdef CONFIG_LEDS_TRIGGERS | 172 | #ifdef CONFIG_LEDS_TRIGGERS |
157 | err_out_led_list: | 173 | err_out_led_list: |
174 | device_remove_file(led_cdev->dev, &dev_attr_max_brightness); | ||
175 | #endif | ||
176 | err_out_attr_max: | ||
158 | device_remove_file(led_cdev->dev, &dev_attr_brightness); | 177 | device_remove_file(led_cdev->dev, &dev_attr_brightness); |
159 | list_del(&led_cdev->node); | 178 | list_del(&led_cdev->node); |
160 | #endif | ||
161 | err_out: | 179 | err_out: |
162 | device_unregister(led_cdev->dev); | 180 | device_unregister(led_cdev->dev); |
163 | return rc; | 181 | return rc; |
@@ -172,6 +190,7 @@ EXPORT_SYMBOL_GPL(led_classdev_register); | |||
172 | */ | 190 | */ |
173 | void led_classdev_unregister(struct led_classdev *led_cdev) | 191 | void led_classdev_unregister(struct led_classdev *led_cdev) |
174 | { | 192 | { |
193 | device_remove_file(led_cdev->dev, &dev_attr_max_brightness); | ||
175 | device_remove_file(led_cdev->dev, &dev_attr_brightness); | 194 | device_remove_file(led_cdev->dev, &dev_attr_brightness); |
176 | #ifdef CONFIG_LEDS_TRIGGERS | 195 | #ifdef CONFIG_LEDS_TRIGGERS |
177 | device_remove_file(led_cdev->dev, &dev_attr_trigger); | 196 | device_remove_file(led_cdev->dev, &dev_attr_trigger); |
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index f910eaffe3a6..d8ddd9ef8994 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c | |||
@@ -156,12 +156,20 @@ EXPORT_SYMBOL_GPL(led_trigger_set_default); | |||
156 | int led_trigger_register(struct led_trigger *trigger) | 156 | int led_trigger_register(struct led_trigger *trigger) |
157 | { | 157 | { |
158 | struct led_classdev *led_cdev; | 158 | struct led_classdev *led_cdev; |
159 | struct led_trigger *trig; | ||
159 | 160 | ||
160 | rwlock_init(&trigger->leddev_list_lock); | 161 | rwlock_init(&trigger->leddev_list_lock); |
161 | INIT_LIST_HEAD(&trigger->led_cdevs); | 162 | INIT_LIST_HEAD(&trigger->led_cdevs); |
162 | 163 | ||
163 | /* Add to the list of led triggers */ | ||
164 | down_write(&triggers_list_lock); | 164 | down_write(&triggers_list_lock); |
165 | /* Make sure the trigger's name isn't already in use */ | ||
166 | list_for_each_entry(trig, &trigger_list, next_trig) { | ||
167 | if (!strcmp(trig->name, trigger->name)) { | ||
168 | up_write(&triggers_list_lock); | ||
169 | return -EEXIST; | ||
170 | } | ||
171 | } | ||
172 | /* Add to the list of led triggers */ | ||
165 | list_add_tail(&trigger->next_trig, &trigger_list); | 173 | list_add_tail(&trigger->next_trig, &trigger_list); |
166 | up_write(&triggers_list_lock); | 174 | up_write(&triggers_list_lock); |
167 | 175 | ||
diff --git a/drivers/leds/leds-bd2802.c b/drivers/leds/leds-bd2802.c new file mode 100644 index 000000000000..4149ecb3a9b2 --- /dev/null +++ b/drivers/leds/leds-bd2802.c | |||
@@ -0,0 +1,765 @@ | |||
1 | /* | ||
2 | * leds-bd2802.c - RGB LED Driver | ||
3 | * | ||
4 | * Copyright (C) 2009 Samsung Electronics | ||
5 | * Kim Kyuwon <q1.kim@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * Datasheet: http://www.rohm.com/products/databook/driver/pdf/bd2802gu-e.pdf | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/i2c.h> | ||
17 | #include <linux/gpio.h> | ||
18 | #include <linux/delay.h> | ||
19 | #include <linux/leds.h> | ||
20 | #include <linux/leds-bd2802.h> | ||
21 | |||
22 | |||
23 | #define LED_CTL(rgb2en, rgb1en) ((rgb2en) << 4 | ((rgb1en) << 0)) | ||
24 | |||
25 | #define BD2802_LED_OFFSET 0xa | ||
26 | #define BD2802_COLOR_OFFSET 0x3 | ||
27 | |||
28 | #define BD2802_REG_CLKSETUP 0x00 | ||
29 | #define BD2802_REG_CONTROL 0x01 | ||
30 | #define BD2802_REG_HOURSETUP 0x02 | ||
31 | #define BD2802_REG_CURRENT1SETUP 0x03 | ||
32 | #define BD2802_REG_CURRENT2SETUP 0x04 | ||
33 | #define BD2802_REG_WAVEPATTERN 0x05 | ||
34 | |||
35 | #define BD2802_CURRENT_032 0x10 /* 3.2mA */ | ||
36 | #define BD2802_CURRENT_000 0x00 /* 0.0mA */ | ||
37 | |||
38 | #define BD2802_PATTERN_FULL 0x07 | ||
39 | #define BD2802_PATTERN_HALF 0x03 | ||
40 | |||
41 | enum led_ids { | ||
42 | LED1, | ||
43 | LED2, | ||
44 | LED_NUM, | ||
45 | }; | ||
46 | |||
47 | enum led_colors { | ||
48 | RED, | ||
49 | GREEN, | ||
50 | BLUE, | ||
51 | }; | ||
52 | |||
53 | enum led_bits { | ||
54 | BD2802_OFF, | ||
55 | BD2802_BLINK, | ||
56 | BD2802_ON, | ||
57 | }; | ||
58 | |||
59 | /* | ||
60 | * State '0' : 'off' | ||
61 | * State '1' : 'blink' | ||
62 | * State '2' : 'on'. | ||
63 | */ | ||
64 | struct led_state { | ||
65 | unsigned r:2; | ||
66 | unsigned g:2; | ||
67 | unsigned b:2; | ||
68 | }; | ||
69 | |||
70 | struct bd2802_led { | ||
71 | struct bd2802_led_platform_data *pdata; | ||
72 | struct i2c_client *client; | ||
73 | struct rw_semaphore rwsem; | ||
74 | struct work_struct work; | ||
75 | |||
76 | struct led_state led[2]; | ||
77 | |||
78 | /* | ||
79 | * Making led_classdev as array is not recommended, because array | ||
80 | * members prevent using 'container_of' macro. So repetitive works | ||
81 | * are needed. | ||
82 | */ | ||
83 | struct led_classdev cdev_led1r; | ||
84 | struct led_classdev cdev_led1g; | ||
85 | struct led_classdev cdev_led1b; | ||
86 | struct led_classdev cdev_led2r; | ||
87 | struct led_classdev cdev_led2g; | ||
88 | struct led_classdev cdev_led2b; | ||
89 | |||
90 | /* | ||
91 | * Advanced Configuration Function(ADF) mode: | ||
92 | * In ADF mode, user can set registers of BD2802GU directly, | ||
93 | * therefore BD2802GU doesn't enter reset state. | ||
94 | */ | ||
95 | int adf_on; | ||
96 | |||
97 | enum led_ids led_id; | ||
98 | enum led_colors color; | ||
99 | enum led_bits state; | ||
100 | }; | ||
101 | |||
102 | |||
103 | /*--------------------------------------------------------------*/ | ||
104 | /* BD2802GU helper functions */ | ||
105 | /*--------------------------------------------------------------*/ | ||
106 | |||
107 | static inline int bd2802_is_rgb_off(struct bd2802_led *led, enum led_ids id, | ||
108 | enum led_colors color) | ||
109 | { | ||
110 | switch (color) { | ||
111 | case RED: | ||
112 | return !led->led[id].r; | ||
113 | case GREEN: | ||
114 | return !led->led[id].g; | ||
115 | case BLUE: | ||
116 | return !led->led[id].b; | ||
117 | default: | ||
118 | dev_err(&led->client->dev, "%s: Invalid color\n", __func__); | ||
119 | return -EINVAL; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | static inline int bd2802_is_led_off(struct bd2802_led *led, enum led_ids id) | ||
124 | { | ||
125 | if (led->led[id].r || led->led[id].g || led->led[id].b) | ||
126 | return 0; | ||
127 | |||
128 | return 1; | ||
129 | } | ||
130 | |||
131 | static inline int bd2802_is_all_off(struct bd2802_led *led) | ||
132 | { | ||
133 | int i; | ||
134 | |||
135 | for (i = 0; i < LED_NUM; i++) | ||
136 | if (!bd2802_is_led_off(led, i)) | ||
137 | return 0; | ||
138 | |||
139 | return 1; | ||
140 | } | ||
141 | |||
142 | static inline u8 bd2802_get_base_offset(enum led_ids id, enum led_colors color) | ||
143 | { | ||
144 | return id * BD2802_LED_OFFSET + color * BD2802_COLOR_OFFSET; | ||
145 | } | ||
146 | |||
147 | static inline u8 bd2802_get_reg_addr(enum led_ids id, enum led_colors color, | ||
148 | u8 reg_offset) | ||
149 | { | ||
150 | return reg_offset + bd2802_get_base_offset(id, color); | ||
151 | } | ||
152 | |||
153 | |||
154 | /*--------------------------------------------------------------*/ | ||
155 | /* BD2802GU core functions */ | ||
156 | /*--------------------------------------------------------------*/ | ||
157 | |||
158 | static int bd2802_write_byte(struct i2c_client *client, u8 reg, u8 val) | ||
159 | { | ||
160 | int ret = i2c_smbus_write_byte_data(client, reg, val); | ||
161 | if (ret >= 0) | ||
162 | return 0; | ||
163 | |||
164 | dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", | ||
165 | __func__, reg, val, ret); | ||
166 | |||
167 | return ret; | ||
168 | } | ||
169 | |||
170 | static void bd2802_update_state(struct bd2802_led *led, enum led_ids id, | ||
171 | enum led_colors color, enum led_bits led_bit) | ||
172 | { | ||
173 | int i; | ||
174 | u8 value; | ||
175 | |||
176 | for (i = 0; i < LED_NUM; i++) { | ||
177 | if (i == id) { | ||
178 | switch (color) { | ||
179 | case RED: | ||
180 | led->led[i].r = led_bit; | ||
181 | break; | ||
182 | case GREEN: | ||
183 | led->led[i].g = led_bit; | ||
184 | break; | ||
185 | case BLUE: | ||
186 | led->led[i].b = led_bit; | ||
187 | break; | ||
188 | default: | ||
189 | dev_err(&led->client->dev, | ||
190 | "%s: Invalid color\n", __func__); | ||
191 | return; | ||
192 | } | ||
193 | } | ||
194 | } | ||
195 | |||
196 | if (led_bit == BD2802_BLINK || led_bit == BD2802_ON) | ||
197 | return; | ||
198 | |||
199 | if (!bd2802_is_led_off(led, id)) | ||
200 | return; | ||
201 | |||
202 | if (bd2802_is_all_off(led) && !led->adf_on) { | ||
203 | gpio_set_value(led->pdata->reset_gpio, 0); | ||
204 | return; | ||
205 | } | ||
206 | |||
207 | /* | ||
208 | * In this case, other led is turned on, and current led is turned | ||
209 | * off. So set RGB LED Control register to stop the current RGB LED | ||
210 | */ | ||
211 | value = (id == LED1) ? LED_CTL(1, 0) : LED_CTL(0, 1); | ||
212 | bd2802_write_byte(led->client, BD2802_REG_CONTROL, value); | ||
213 | } | ||
214 | |||
215 | static void bd2802_configure(struct bd2802_led *led) | ||
216 | { | ||
217 | struct bd2802_led_platform_data *pdata = led->pdata; | ||
218 | u8 reg; | ||
219 | |||
220 | reg = bd2802_get_reg_addr(LED1, RED, BD2802_REG_HOURSETUP); | ||
221 | bd2802_write_byte(led->client, reg, pdata->rgb_time); | ||
222 | |||
223 | reg = bd2802_get_reg_addr(LED2, RED, BD2802_REG_HOURSETUP); | ||
224 | bd2802_write_byte(led->client, reg, pdata->rgb_time); | ||
225 | } | ||
226 | |||
227 | static void bd2802_reset_cancel(struct bd2802_led *led) | ||
228 | { | ||
229 | gpio_set_value(led->pdata->reset_gpio, 1); | ||
230 | udelay(100); | ||
231 | bd2802_configure(led); | ||
232 | } | ||
233 | |||
234 | static void bd2802_enable(struct bd2802_led *led, enum led_ids id) | ||
235 | { | ||
236 | enum led_ids other_led = (id == LED1) ? LED2 : LED1; | ||
237 | u8 value, other_led_on; | ||
238 | |||
239 | other_led_on = !bd2802_is_led_off(led, other_led); | ||
240 | if (id == LED1) | ||
241 | value = LED_CTL(other_led_on, 1); | ||
242 | else | ||
243 | value = LED_CTL(1 , other_led_on); | ||
244 | |||
245 | bd2802_write_byte(led->client, BD2802_REG_CONTROL, value); | ||
246 | } | ||
247 | |||
248 | static void bd2802_set_on(struct bd2802_led *led, enum led_ids id, | ||
249 | enum led_colors color) | ||
250 | { | ||
251 | u8 reg; | ||
252 | |||
253 | if (bd2802_is_all_off(led) && !led->adf_on) | ||
254 | bd2802_reset_cancel(led); | ||
255 | |||
256 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP); | ||
257 | bd2802_write_byte(led->client, reg, BD2802_CURRENT_032); | ||
258 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP); | ||
259 | bd2802_write_byte(led->client, reg, BD2802_CURRENT_000); | ||
260 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN); | ||
261 | bd2802_write_byte(led->client, reg, BD2802_PATTERN_FULL); | ||
262 | |||
263 | bd2802_enable(led, id); | ||
264 | bd2802_update_state(led, id, color, BD2802_ON); | ||
265 | } | ||
266 | |||
267 | static void bd2802_set_blink(struct bd2802_led *led, enum led_ids id, | ||
268 | enum led_colors color) | ||
269 | { | ||
270 | u8 reg; | ||
271 | |||
272 | if (bd2802_is_all_off(led) && !led->adf_on) | ||
273 | bd2802_reset_cancel(led); | ||
274 | |||
275 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP); | ||
276 | bd2802_write_byte(led->client, reg, BD2802_CURRENT_000); | ||
277 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP); | ||
278 | bd2802_write_byte(led->client, reg, BD2802_CURRENT_032); | ||
279 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN); | ||
280 | bd2802_write_byte(led->client, reg, BD2802_PATTERN_HALF); | ||
281 | |||
282 | bd2802_enable(led, id); | ||
283 | bd2802_update_state(led, id, color, BD2802_BLINK); | ||
284 | } | ||
285 | |||
286 | static void bd2802_turn_on(struct bd2802_led *led, enum led_ids id, | ||
287 | enum led_colors color, enum led_bits led_bit) | ||
288 | { | ||
289 | if (led_bit == BD2802_OFF) { | ||
290 | dev_err(&led->client->dev, | ||
291 | "Only 'blink' and 'on' are allowed\n"); | ||
292 | return; | ||
293 | } | ||
294 | |||
295 | if (led_bit == BD2802_BLINK) | ||
296 | bd2802_set_blink(led, id, color); | ||
297 | else | ||
298 | bd2802_set_on(led, id, color); | ||
299 | } | ||
300 | |||
301 | static void bd2802_turn_off(struct bd2802_led *led, enum led_ids id, | ||
302 | enum led_colors color) | ||
303 | { | ||
304 | u8 reg; | ||
305 | |||
306 | if (bd2802_is_rgb_off(led, id, color)) | ||
307 | return; | ||
308 | |||
309 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP); | ||
310 | bd2802_write_byte(led->client, reg, BD2802_CURRENT_000); | ||
311 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP); | ||
312 | bd2802_write_byte(led->client, reg, BD2802_CURRENT_000); | ||
313 | |||
314 | bd2802_update_state(led, id, color, BD2802_OFF); | ||
315 | } | ||
316 | |||
317 | static void bd2802_restore_state(struct bd2802_led *led) | ||
318 | { | ||
319 | int i; | ||
320 | |||
321 | for (i = 0; i < LED_NUM; i++) { | ||
322 | if (led->led[i].r) | ||
323 | bd2802_turn_on(led, i, RED, led->led[i].r); | ||
324 | if (led->led[i].g) | ||
325 | bd2802_turn_on(led, i, GREEN, led->led[i].g); | ||
326 | if (led->led[i].b) | ||
327 | bd2802_turn_on(led, i, BLUE, led->led[i].b); | ||
328 | } | ||
329 | } | ||
330 | |||
331 | #define BD2802_SET_REGISTER(reg_addr, reg_name) \ | ||
332 | static ssize_t bd2802_store_reg##reg_addr(struct device *dev, \ | ||
333 | struct device_attribute *attr, const char *buf, size_t count) \ | ||
334 | { \ | ||
335 | struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\ | ||
336 | unsigned long val; \ | ||
337 | int ret; \ | ||
338 | if (!count) \ | ||
339 | return -EINVAL; \ | ||
340 | ret = strict_strtoul(buf, 16, &val); \ | ||
341 | if (ret) \ | ||
342 | return ret; \ | ||
343 | down_write(&led->rwsem); \ | ||
344 | bd2802_write_byte(led->client, reg_addr, (u8) val); \ | ||
345 | up_write(&led->rwsem); \ | ||
346 | return count; \ | ||
347 | } \ | ||
348 | static struct device_attribute bd2802_reg##reg_addr##_attr = { \ | ||
349 | .attr = {.name = reg_name, .mode = 0644, .owner = THIS_MODULE}, \ | ||
350 | .store = bd2802_store_reg##reg_addr, \ | ||
351 | }; | ||
352 | |||
353 | BD2802_SET_REGISTER(0x00, "0x00"); | ||
354 | BD2802_SET_REGISTER(0x01, "0x01"); | ||
355 | BD2802_SET_REGISTER(0x02, "0x02"); | ||
356 | BD2802_SET_REGISTER(0x03, "0x03"); | ||
357 | BD2802_SET_REGISTER(0x04, "0x04"); | ||
358 | BD2802_SET_REGISTER(0x05, "0x05"); | ||
359 | BD2802_SET_REGISTER(0x06, "0x06"); | ||
360 | BD2802_SET_REGISTER(0x07, "0x07"); | ||
361 | BD2802_SET_REGISTER(0x08, "0x08"); | ||
362 | BD2802_SET_REGISTER(0x09, "0x09"); | ||
363 | BD2802_SET_REGISTER(0x0a, "0x0a"); | ||
364 | BD2802_SET_REGISTER(0x0b, "0x0b"); | ||
365 | BD2802_SET_REGISTER(0x0c, "0x0c"); | ||
366 | BD2802_SET_REGISTER(0x0d, "0x0d"); | ||
367 | BD2802_SET_REGISTER(0x0e, "0x0e"); | ||
368 | BD2802_SET_REGISTER(0x0f, "0x0f"); | ||
369 | BD2802_SET_REGISTER(0x10, "0x10"); | ||
370 | BD2802_SET_REGISTER(0x11, "0x11"); | ||
371 | BD2802_SET_REGISTER(0x12, "0x12"); | ||
372 | BD2802_SET_REGISTER(0x13, "0x13"); | ||
373 | BD2802_SET_REGISTER(0x14, "0x14"); | ||
374 | BD2802_SET_REGISTER(0x15, "0x15"); | ||
375 | |||
376 | static struct device_attribute *bd2802_addr_attributes[] = { | ||
377 | &bd2802_reg0x00_attr, | ||
378 | &bd2802_reg0x01_attr, | ||
379 | &bd2802_reg0x02_attr, | ||
380 | &bd2802_reg0x03_attr, | ||
381 | &bd2802_reg0x04_attr, | ||
382 | &bd2802_reg0x05_attr, | ||
383 | &bd2802_reg0x06_attr, | ||
384 | &bd2802_reg0x07_attr, | ||
385 | &bd2802_reg0x08_attr, | ||
386 | &bd2802_reg0x09_attr, | ||
387 | &bd2802_reg0x0a_attr, | ||
388 | &bd2802_reg0x0b_attr, | ||
389 | &bd2802_reg0x0c_attr, | ||
390 | &bd2802_reg0x0d_attr, | ||
391 | &bd2802_reg0x0e_attr, | ||
392 | &bd2802_reg0x0f_attr, | ||
393 | &bd2802_reg0x10_attr, | ||
394 | &bd2802_reg0x11_attr, | ||
395 | &bd2802_reg0x12_attr, | ||
396 | &bd2802_reg0x13_attr, | ||
397 | &bd2802_reg0x14_attr, | ||
398 | &bd2802_reg0x15_attr, | ||
399 | }; | ||
400 | |||
401 | static void bd2802_enable_adv_conf(struct bd2802_led *led) | ||
402 | { | ||
403 | int i, ret; | ||
404 | |||
405 | for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++) { | ||
406 | ret = device_create_file(&led->client->dev, | ||
407 | bd2802_addr_attributes[i]); | ||
408 | if (ret) { | ||
409 | dev_err(&led->client->dev, "failed to sysfs file %s\n", | ||
410 | bd2802_addr_attributes[i]->attr.name); | ||
411 | goto failed_remove_files; | ||
412 | } | ||
413 | } | ||
414 | |||
415 | if (bd2802_is_all_off(led)) | ||
416 | bd2802_reset_cancel(led); | ||
417 | |||
418 | led->adf_on = 1; | ||
419 | |||
420 | return; | ||
421 | |||
422 | failed_remove_files: | ||
423 | for (i--; i >= 0; i--) | ||
424 | device_remove_file(&led->client->dev, | ||
425 | bd2802_addr_attributes[i]); | ||
426 | } | ||
427 | |||
428 | static void bd2802_disable_adv_conf(struct bd2802_led *led) | ||
429 | { | ||
430 | int i; | ||
431 | |||
432 | for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++) | ||
433 | device_remove_file(&led->client->dev, | ||
434 | bd2802_addr_attributes[i]); | ||
435 | |||
436 | if (bd2802_is_all_off(led)) | ||
437 | gpio_set_value(led->pdata->reset_gpio, 0); | ||
438 | |||
439 | led->adf_on = 0; | ||
440 | } | ||
441 | |||
442 | static ssize_t bd2802_show_adv_conf(struct device *dev, | ||
443 | struct device_attribute *attr, char *buf) | ||
444 | { | ||
445 | struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev)); | ||
446 | ssize_t ret; | ||
447 | |||
448 | down_read(&led->rwsem); | ||
449 | if (led->adf_on) | ||
450 | ret = sprintf(buf, "on\n"); | ||
451 | else | ||
452 | ret = sprintf(buf, "off\n"); | ||
453 | up_read(&led->rwsem); | ||
454 | |||
455 | return ret; | ||
456 | } | ||
457 | |||
458 | static ssize_t bd2802_store_adv_conf(struct device *dev, | ||
459 | struct device_attribute *attr, const char *buf, size_t count) | ||
460 | { | ||
461 | struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev)); | ||
462 | |||
463 | if (!count) | ||
464 | return -EINVAL; | ||
465 | |||
466 | down_write(&led->rwsem); | ||
467 | if (!led->adf_on && !strncmp(buf, "on", 2)) | ||
468 | bd2802_enable_adv_conf(led); | ||
469 | else if (led->adf_on && !strncmp(buf, "off", 3)) | ||
470 | bd2802_disable_adv_conf(led); | ||
471 | up_write(&led->rwsem); | ||
472 | |||
473 | return count; | ||
474 | } | ||
475 | |||
476 | static struct device_attribute bd2802_adv_conf_attr = { | ||
477 | .attr = { | ||
478 | .name = "advanced_configuration", | ||
479 | .mode = 0644, | ||
480 | .owner = THIS_MODULE | ||
481 | }, | ||
482 | .show = bd2802_show_adv_conf, | ||
483 | .store = bd2802_store_adv_conf, | ||
484 | }; | ||
485 | |||
486 | static void bd2802_led_work(struct work_struct *work) | ||
487 | { | ||
488 | struct bd2802_led *led = container_of(work, struct bd2802_led, work); | ||
489 | |||
490 | if (led->state) | ||
491 | bd2802_turn_on(led, led->led_id, led->color, led->state); | ||
492 | else | ||
493 | bd2802_turn_off(led, led->led_id, led->color); | ||
494 | } | ||
495 | |||
496 | #define BD2802_CONTROL_RGBS(name, id, clr) \ | ||
497 | static void bd2802_set_##name##_brightness(struct led_classdev *led_cdev,\ | ||
498 | enum led_brightness value) \ | ||
499 | { \ | ||
500 | struct bd2802_led *led = \ | ||
501 | container_of(led_cdev, struct bd2802_led, cdev_##name); \ | ||
502 | led->led_id = id; \ | ||
503 | led->color = clr; \ | ||
504 | if (value == LED_OFF) \ | ||
505 | led->state = BD2802_OFF; \ | ||
506 | else \ | ||
507 | led->state = BD2802_ON; \ | ||
508 | schedule_work(&led->work); \ | ||
509 | } \ | ||
510 | static int bd2802_set_##name##_blink(struct led_classdev *led_cdev, \ | ||
511 | unsigned long *delay_on, unsigned long *delay_off) \ | ||
512 | { \ | ||
513 | struct bd2802_led *led = \ | ||
514 | container_of(led_cdev, struct bd2802_led, cdev_##name); \ | ||
515 | if (*delay_on == 0 || *delay_off == 0) \ | ||
516 | return -EINVAL; \ | ||
517 | led->led_id = id; \ | ||
518 | led->color = clr; \ | ||
519 | led->state = BD2802_BLINK; \ | ||
520 | schedule_work(&led->work); \ | ||
521 | return 0; \ | ||
522 | } | ||
523 | |||
524 | BD2802_CONTROL_RGBS(led1r, LED1, RED); | ||
525 | BD2802_CONTROL_RGBS(led1g, LED1, GREEN); | ||
526 | BD2802_CONTROL_RGBS(led1b, LED1, BLUE); | ||
527 | BD2802_CONTROL_RGBS(led2r, LED2, RED); | ||
528 | BD2802_CONTROL_RGBS(led2g, LED2, GREEN); | ||
529 | BD2802_CONTROL_RGBS(led2b, LED2, BLUE); | ||
530 | |||
531 | static int bd2802_register_led_classdev(struct bd2802_led *led) | ||
532 | { | ||
533 | int ret; | ||
534 | |||
535 | INIT_WORK(&led->work, bd2802_led_work); | ||
536 | |||
537 | led->cdev_led1r.name = "led1_R"; | ||
538 | led->cdev_led1r.brightness = LED_OFF; | ||
539 | led->cdev_led1r.brightness_set = bd2802_set_led1r_brightness; | ||
540 | led->cdev_led1r.blink_set = bd2802_set_led1r_blink; | ||
541 | led->cdev_led1r.flags |= LED_CORE_SUSPENDRESUME; | ||
542 | |||
543 | ret = led_classdev_register(&led->client->dev, &led->cdev_led1r); | ||
544 | if (ret < 0) { | ||
545 | dev_err(&led->client->dev, "couldn't register LED %s\n", | ||
546 | led->cdev_led1r.name); | ||
547 | goto failed_unregister_led1_R; | ||
548 | } | ||
549 | |||
550 | led->cdev_led1g.name = "led1_G"; | ||
551 | led->cdev_led1g.brightness = LED_OFF; | ||
552 | led->cdev_led1g.brightness_set = bd2802_set_led1g_brightness; | ||
553 | led->cdev_led1g.blink_set = bd2802_set_led1g_blink; | ||
554 | led->cdev_led1g.flags |= LED_CORE_SUSPENDRESUME; | ||
555 | |||
556 | ret = led_classdev_register(&led->client->dev, &led->cdev_led1g); | ||
557 | if (ret < 0) { | ||
558 | dev_err(&led->client->dev, "couldn't register LED %s\n", | ||
559 | led->cdev_led1g.name); | ||
560 | goto failed_unregister_led1_G; | ||
561 | } | ||
562 | |||
563 | led->cdev_led1b.name = "led1_B"; | ||
564 | led->cdev_led1b.brightness = LED_OFF; | ||
565 | led->cdev_led1b.brightness_set = bd2802_set_led1b_brightness; | ||
566 | led->cdev_led1b.blink_set = bd2802_set_led1b_blink; | ||
567 | led->cdev_led1b.flags |= LED_CORE_SUSPENDRESUME; | ||
568 | |||
569 | ret = led_classdev_register(&led->client->dev, &led->cdev_led1b); | ||
570 | if (ret < 0) { | ||
571 | dev_err(&led->client->dev, "couldn't register LED %s\n", | ||
572 | led->cdev_led1b.name); | ||
573 | goto failed_unregister_led1_B; | ||
574 | } | ||
575 | |||
576 | led->cdev_led2r.name = "led2_R"; | ||
577 | led->cdev_led2r.brightness = LED_OFF; | ||
578 | led->cdev_led2r.brightness_set = bd2802_set_led2r_brightness; | ||
579 | led->cdev_led2r.blink_set = bd2802_set_led2r_blink; | ||
580 | led->cdev_led2r.flags |= LED_CORE_SUSPENDRESUME; | ||
581 | |||
582 | ret = led_classdev_register(&led->client->dev, &led->cdev_led2r); | ||
583 | if (ret < 0) { | ||
584 | dev_err(&led->client->dev, "couldn't register LED %s\n", | ||
585 | led->cdev_led2r.name); | ||
586 | goto failed_unregister_led2_R; | ||
587 | } | ||
588 | |||
589 | led->cdev_led2g.name = "led2_G"; | ||
590 | led->cdev_led2g.brightness = LED_OFF; | ||
591 | led->cdev_led2g.brightness_set = bd2802_set_led2g_brightness; | ||
592 | led->cdev_led2g.blink_set = bd2802_set_led2g_blink; | ||
593 | led->cdev_led2g.flags |= LED_CORE_SUSPENDRESUME; | ||
594 | |||
595 | ret = led_classdev_register(&led->client->dev, &led->cdev_led2g); | ||
596 | if (ret < 0) { | ||
597 | dev_err(&led->client->dev, "couldn't register LED %s\n", | ||
598 | led->cdev_led2g.name); | ||
599 | goto failed_unregister_led2_G; | ||
600 | } | ||
601 | |||
602 | led->cdev_led2b.name = "led2_B"; | ||
603 | led->cdev_led2b.brightness = LED_OFF; | ||
604 | led->cdev_led2b.brightness_set = bd2802_set_led2b_brightness; | ||
605 | led->cdev_led2b.blink_set = bd2802_set_led2b_blink; | ||
606 | led->cdev_led2b.flags |= LED_CORE_SUSPENDRESUME; | ||
607 | |||
608 | ret = led_classdev_register(&led->client->dev, &led->cdev_led2b); | ||
609 | if (ret < 0) { | ||
610 | dev_err(&led->client->dev, "couldn't register LED %s\n", | ||
611 | led->cdev_led2b.name); | ||
612 | goto failed_unregister_led2_B; | ||
613 | } | ||
614 | |||
615 | return 0; | ||
616 | |||
617 | failed_unregister_led2_B: | ||
618 | led_classdev_unregister(&led->cdev_led2g); | ||
619 | failed_unregister_led2_G: | ||
620 | led_classdev_unregister(&led->cdev_led2r); | ||
621 | failed_unregister_led2_R: | ||
622 | led_classdev_unregister(&led->cdev_led1b); | ||
623 | failed_unregister_led1_B: | ||
624 | led_classdev_unregister(&led->cdev_led1g); | ||
625 | failed_unregister_led1_G: | ||
626 | led_classdev_unregister(&led->cdev_led1r); | ||
627 | failed_unregister_led1_R: | ||
628 | |||
629 | return ret; | ||
630 | } | ||
631 | |||
632 | static void bd2802_unregister_led_classdev(struct bd2802_led *led) | ||
633 | { | ||
634 | cancel_work_sync(&led->work); | ||
635 | led_classdev_unregister(&led->cdev_led1r); | ||
636 | } | ||
637 | |||
638 | static int __devinit bd2802_probe(struct i2c_client *client, | ||
639 | const struct i2c_device_id *id) | ||
640 | { | ||
641 | struct bd2802_led *led; | ||
642 | struct bd2802_led_platform_data *pdata; | ||
643 | int ret; | ||
644 | |||
645 | led = kzalloc(sizeof(struct bd2802_led), GFP_KERNEL); | ||
646 | if (!led) { | ||
647 | dev_err(&client->dev, "failed to allocate driver data\n"); | ||
648 | return -ENOMEM; | ||
649 | } | ||
650 | |||
651 | led->client = client; | ||
652 | pdata = led->pdata = client->dev.platform_data; | ||
653 | i2c_set_clientdata(client, led); | ||
654 | |||
655 | /* Configure RESET GPIO (L: RESET, H: RESET cancel) */ | ||
656 | gpio_request(pdata->reset_gpio, "RGB_RESETB"); | ||
657 | gpio_direction_output(pdata->reset_gpio, 1); | ||
658 | |||
659 | /* Tacss = min 0.1ms */ | ||
660 | udelay(100); | ||
661 | |||
662 | /* Detect BD2802GU */ | ||
663 | ret = bd2802_write_byte(client, BD2802_REG_CLKSETUP, 0x00); | ||
664 | if (ret < 0) { | ||
665 | dev_err(&client->dev, "failed to detect device\n"); | ||
666 | goto failed_free; | ||
667 | } else | ||
668 | dev_info(&client->dev, "return 0x%02x\n", ret); | ||
669 | |||
670 | /* To save the power, reset BD2802 after detecting */ | ||
671 | gpio_set_value(led->pdata->reset_gpio, 0); | ||
672 | |||
673 | init_rwsem(&led->rwsem); | ||
674 | |||
675 | ret = device_create_file(&client->dev, &bd2802_adv_conf_attr); | ||
676 | if (ret) { | ||
677 | dev_err(&client->dev, "failed to create sysfs file %s\n", | ||
678 | bd2802_adv_conf_attr.attr.name); | ||
679 | goto failed_free; | ||
680 | } | ||
681 | |||
682 | ret = bd2802_register_led_classdev(led); | ||
683 | if (ret < 0) | ||
684 | goto failed_unregister_dev_file; | ||
685 | |||
686 | return 0; | ||
687 | |||
688 | failed_unregister_dev_file: | ||
689 | device_remove_file(&client->dev, &bd2802_adv_conf_attr); | ||
690 | failed_free: | ||
691 | i2c_set_clientdata(client, NULL); | ||
692 | kfree(led); | ||
693 | |||
694 | return ret; | ||
695 | } | ||
696 | |||
697 | static int __exit bd2802_remove(struct i2c_client *client) | ||
698 | { | ||
699 | struct bd2802_led *led = i2c_get_clientdata(client); | ||
700 | |||
701 | bd2802_unregister_led_classdev(led); | ||
702 | gpio_set_value(led->pdata->reset_gpio, 0); | ||
703 | if (led->adf_on) | ||
704 | bd2802_disable_adv_conf(led); | ||
705 | device_remove_file(&client->dev, &bd2802_adv_conf_attr); | ||
706 | i2c_set_clientdata(client, NULL); | ||
707 | kfree(led); | ||
708 | |||
709 | return 0; | ||
710 | } | ||
711 | |||
712 | static int bd2802_suspend(struct i2c_client *client, pm_message_t mesg) | ||
713 | { | ||
714 | struct bd2802_led *led = i2c_get_clientdata(client); | ||
715 | |||
716 | gpio_set_value(led->pdata->reset_gpio, 0); | ||
717 | |||
718 | return 0; | ||
719 | } | ||
720 | |||
721 | static int bd2802_resume(struct i2c_client *client) | ||
722 | { | ||
723 | struct bd2802_led *led = i2c_get_clientdata(client); | ||
724 | |||
725 | if (!bd2802_is_all_off(led) || led->adf_on) { | ||
726 | gpio_set_value(led->pdata->reset_gpio, 1); | ||
727 | udelay(100); | ||
728 | bd2802_restore_state(led); | ||
729 | } | ||
730 | |||
731 | return 0; | ||
732 | } | ||
733 | |||
734 | static const struct i2c_device_id bd2802_id[] = { | ||
735 | { "BD2802", 0 }, | ||
736 | { } | ||
737 | }; | ||
738 | MODULE_DEVICE_TABLE(i2c, bd2802_id); | ||
739 | |||
740 | static struct i2c_driver bd2802_i2c_driver = { | ||
741 | .driver = { | ||
742 | .name = "BD2802", | ||
743 | }, | ||
744 | .probe = bd2802_probe, | ||
745 | .remove = __exit_p(bd2802_remove), | ||
746 | .suspend = bd2802_suspend, | ||
747 | .resume = bd2802_resume, | ||
748 | .id_table = bd2802_id, | ||
749 | }; | ||
750 | |||
751 | static int __init bd2802_init(void) | ||
752 | { | ||
753 | return i2c_add_driver(&bd2802_i2c_driver); | ||
754 | } | ||
755 | module_init(bd2802_init); | ||
756 | |||
757 | static void __exit bd2802_exit(void) | ||
758 | { | ||
759 | i2c_del_driver(&bd2802_i2c_driver); | ||
760 | } | ||
761 | module_exit(bd2802_exit); | ||
762 | |||
763 | MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>"); | ||
764 | MODULE_DESCRIPTION("BD2802 LED driver"); | ||
765 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/leds/leds-dac124s085.c b/drivers/leds/leds-dac124s085.c new file mode 100644 index 000000000000..098d9aae7259 --- /dev/null +++ b/drivers/leds/leds-dac124s085.c | |||
@@ -0,0 +1,150 @@ | |||
1 | /* | ||
2 | * Copyright 2008 | ||
3 | * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de> | ||
4 | * | ||
5 | * This file is subject to the terms and conditions of version 2 of | ||
6 | * the GNU General Public License. See the file COPYING in the main | ||
7 | * directory of this archive for more details. | ||
8 | * | ||
9 | * LED driver for the DAC124S085 SPI DAC | ||
10 | */ | ||
11 | |||
12 | #include <linux/gfp.h> | ||
13 | #include <linux/leds.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/mutex.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/spinlock.h> | ||
18 | #include <linux/workqueue.h> | ||
19 | #include <linux/spi/spi.h> | ||
20 | |||
21 | struct dac124s085_led { | ||
22 | struct led_classdev ldev; | ||
23 | struct spi_device *spi; | ||
24 | int id; | ||
25 | int brightness; | ||
26 | char name[sizeof("dac124s085-3")]; | ||
27 | |||
28 | struct mutex mutex; | ||
29 | struct work_struct work; | ||
30 | spinlock_t lock; | ||
31 | }; | ||
32 | |||
33 | struct dac124s085 { | ||
34 | struct dac124s085_led leds[4]; | ||
35 | }; | ||
36 | |||
37 | #define REG_WRITE (0 << 12) | ||
38 | #define REG_WRITE_UPDATE (1 << 12) | ||
39 | #define ALL_WRITE_UPDATE (2 << 12) | ||
40 | #define POWER_DOWN_OUTPUT (3 << 12) | ||
41 | |||
42 | static void dac124s085_led_work(struct work_struct *work) | ||
43 | { | ||
44 | struct dac124s085_led *led = container_of(work, struct dac124s085_led, | ||
45 | work); | ||
46 | u16 word; | ||
47 | |||
48 | mutex_lock(&led->mutex); | ||
49 | word = cpu_to_le16(((led->id) << 14) | REG_WRITE_UPDATE | | ||
50 | (led->brightness & 0xfff)); | ||
51 | spi_write(led->spi, (const u8 *)&word, sizeof(word)); | ||
52 | mutex_unlock(&led->mutex); | ||
53 | } | ||
54 | |||
55 | static void dac124s085_set_brightness(struct led_classdev *ldev, | ||
56 | enum led_brightness brightness) | ||
57 | { | ||
58 | struct dac124s085_led *led = container_of(ldev, struct dac124s085_led, | ||
59 | ldev); | ||
60 | |||
61 | spin_lock(&led->lock); | ||
62 | led->brightness = brightness; | ||
63 | schedule_work(&led->work); | ||
64 | spin_unlock(&led->lock); | ||
65 | } | ||
66 | |||
67 | static int dac124s085_probe(struct spi_device *spi) | ||
68 | { | ||
69 | struct dac124s085 *dac; | ||
70 | struct dac124s085_led *led; | ||
71 | int i, ret; | ||
72 | |||
73 | dac = kzalloc(sizeof(*dac), GFP_KERNEL); | ||
74 | if (!dac) | ||
75 | return -ENOMEM; | ||
76 | |||
77 | spi->bits_per_word = 16; | ||
78 | |||
79 | for (i = 0; i < ARRAY_SIZE(dac->leds); i++) { | ||
80 | led = dac->leds + i; | ||
81 | led->id = i; | ||
82 | led->brightness = LED_OFF; | ||
83 | led->spi = spi; | ||
84 | snprintf(led->name, sizeof(led->name), "dac124s085-%d", i); | ||
85 | spin_lock_init(&led->lock); | ||
86 | INIT_WORK(&led->work, dac124s085_led_work); | ||
87 | mutex_init(&led->mutex); | ||
88 | led->ldev.name = led->name; | ||
89 | led->ldev.brightness = LED_OFF; | ||
90 | led->ldev.max_brightness = 0xfff; | ||
91 | led->ldev.brightness_set = dac124s085_set_brightness; | ||
92 | ret = led_classdev_register(&spi->dev, &led->ldev); | ||
93 | if (ret < 0) | ||
94 | goto eledcr; | ||
95 | } | ||
96 | |||
97 | spi_set_drvdata(spi, dac); | ||
98 | |||
99 | return 0; | ||
100 | |||
101 | eledcr: | ||
102 | while (i--) | ||
103 | led_classdev_unregister(&dac->leds[i].ldev); | ||
104 | |||
105 | spi_set_drvdata(spi, NULL); | ||
106 | kfree(dac); | ||
107 | return ret; | ||
108 | } | ||
109 | |||
110 | static int dac124s085_remove(struct spi_device *spi) | ||
111 | { | ||
112 | struct dac124s085 *dac = spi_get_drvdata(spi); | ||
113 | int i; | ||
114 | |||
115 | for (i = 0; i < ARRAY_SIZE(dac->leds); i++) { | ||
116 | led_classdev_unregister(&dac->leds[i].ldev); | ||
117 | cancel_work_sync(&dac->leds[i].work); | ||
118 | } | ||
119 | |||
120 | spi_set_drvdata(spi, NULL); | ||
121 | kfree(dac); | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static struct spi_driver dac124s085_driver = { | ||
127 | .probe = dac124s085_probe, | ||
128 | .remove = dac124s085_remove, | ||
129 | .driver = { | ||
130 | .name = "dac124s085", | ||
131 | .owner = THIS_MODULE, | ||
132 | }, | ||
133 | }; | ||
134 | |||
135 | static int __init dac124s085_leds_init(void) | ||
136 | { | ||
137 | return spi_register_driver(&dac124s085_driver); | ||
138 | } | ||
139 | |||
140 | static void __exit dac124s085_leds_exit(void) | ||
141 | { | ||
142 | spi_unregister_driver(&dac124s085_driver); | ||
143 | } | ||
144 | |||
145 | module_init(dac124s085_leds_init); | ||
146 | module_exit(dac124s085_leds_exit); | ||
147 | |||
148 | MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>"); | ||
149 | MODULE_DESCRIPTION("DAC124S085 LED driver"); | ||
150 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 2e3df08b649b..d2109054de85 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c | |||
@@ -3,6 +3,7 @@ | |||
3 | * | 3 | * |
4 | * Copyright (C) 2007 8D Technologies inc. | 4 | * Copyright (C) 2007 8D Technologies inc. |
5 | * Raphael Assenat <raph@8d.com> | 5 | * Raphael Assenat <raph@8d.com> |
6 | * Copyright (C) 2008 Freescale Semiconductor, Inc. | ||
6 | * | 7 | * |
7 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2 as | 9 | * it under the terms of the GNU General Public License version 2 as |
@@ -71,11 +72,67 @@ static int gpio_blink_set(struct led_classdev *led_cdev, | |||
71 | return led_dat->platform_gpio_blink_set(led_dat->gpio, delay_on, delay_off); | 72 | return led_dat->platform_gpio_blink_set(led_dat->gpio, delay_on, delay_off); |
72 | } | 73 | } |
73 | 74 | ||
75 | static int __devinit create_gpio_led(const struct gpio_led *template, | ||
76 | struct gpio_led_data *led_dat, struct device *parent, | ||
77 | int (*blink_set)(unsigned, unsigned long *, unsigned long *)) | ||
78 | { | ||
79 | int ret; | ||
80 | |||
81 | /* skip leds that aren't available */ | ||
82 | if (!gpio_is_valid(template->gpio)) { | ||
83 | printk(KERN_INFO "Skipping unavilable LED gpio %d (%s)\n", | ||
84 | template->gpio, template->name); | ||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | ret = gpio_request(template->gpio, template->name); | ||
89 | if (ret < 0) | ||
90 | return ret; | ||
91 | |||
92 | led_dat->cdev.name = template->name; | ||
93 | led_dat->cdev.default_trigger = template->default_trigger; | ||
94 | led_dat->gpio = template->gpio; | ||
95 | led_dat->can_sleep = gpio_cansleep(template->gpio); | ||
96 | led_dat->active_low = template->active_low; | ||
97 | if (blink_set) { | ||
98 | led_dat->platform_gpio_blink_set = blink_set; | ||
99 | led_dat->cdev.blink_set = gpio_blink_set; | ||
100 | } | ||
101 | led_dat->cdev.brightness_set = gpio_led_set; | ||
102 | led_dat->cdev.brightness = LED_OFF; | ||
103 | if (!template->retain_state_suspended) | ||
104 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | ||
105 | |||
106 | ret = gpio_direction_output(led_dat->gpio, led_dat->active_low); | ||
107 | if (ret < 0) | ||
108 | goto err; | ||
109 | |||
110 | INIT_WORK(&led_dat->work, gpio_led_work); | ||
111 | |||
112 | ret = led_classdev_register(parent, &led_dat->cdev); | ||
113 | if (ret < 0) | ||
114 | goto err; | ||
115 | |||
116 | return 0; | ||
117 | err: | ||
118 | gpio_free(led_dat->gpio); | ||
119 | return ret; | ||
120 | } | ||
121 | |||
122 | static void delete_gpio_led(struct gpio_led_data *led) | ||
123 | { | ||
124 | if (!gpio_is_valid(led->gpio)) | ||
125 | return; | ||
126 | led_classdev_unregister(&led->cdev); | ||
127 | cancel_work_sync(&led->work); | ||
128 | gpio_free(led->gpio); | ||
129 | } | ||
130 | |||
131 | #ifdef CONFIG_LEDS_GPIO_PLATFORM | ||
74 | static int gpio_led_probe(struct platform_device *pdev) | 132 | static int gpio_led_probe(struct platform_device *pdev) |
75 | { | 133 | { |
76 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; | 134 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; |
77 | struct gpio_led *cur_led; | 135 | struct gpio_led_data *leds_data; |
78 | struct gpio_led_data *leds_data, *led_dat; | ||
79 | int i, ret = 0; | 136 | int i, ret = 0; |
80 | 137 | ||
81 | if (!pdata) | 138 | if (!pdata) |
@@ -87,35 +144,10 @@ static int gpio_led_probe(struct platform_device *pdev) | |||
87 | return -ENOMEM; | 144 | return -ENOMEM; |
88 | 145 | ||
89 | for (i = 0; i < pdata->num_leds; i++) { | 146 | for (i = 0; i < pdata->num_leds; i++) { |
90 | cur_led = &pdata->leds[i]; | 147 | ret = create_gpio_led(&pdata->leds[i], &leds_data[i], |
91 | led_dat = &leds_data[i]; | 148 | &pdev->dev, pdata->gpio_blink_set); |
92 | |||
93 | ret = gpio_request(cur_led->gpio, cur_led->name); | ||
94 | if (ret < 0) | 149 | if (ret < 0) |
95 | goto err; | 150 | goto err; |
96 | |||
97 | led_dat->cdev.name = cur_led->name; | ||
98 | led_dat->cdev.default_trigger = cur_led->default_trigger; | ||
99 | led_dat->gpio = cur_led->gpio; | ||
100 | led_dat->can_sleep = gpio_cansleep(cur_led->gpio); | ||
101 | led_dat->active_low = cur_led->active_low; | ||
102 | if (pdata->gpio_blink_set) { | ||
103 | led_dat->platform_gpio_blink_set = pdata->gpio_blink_set; | ||
104 | led_dat->cdev.blink_set = gpio_blink_set; | ||
105 | } | ||
106 | led_dat->cdev.brightness_set = gpio_led_set; | ||
107 | led_dat->cdev.brightness = LED_OFF; | ||
108 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | ||
109 | |||
110 | gpio_direction_output(led_dat->gpio, led_dat->active_low); | ||
111 | |||
112 | INIT_WORK(&led_dat->work, gpio_led_work); | ||
113 | |||
114 | ret = led_classdev_register(&pdev->dev, &led_dat->cdev); | ||
115 | if (ret < 0) { | ||
116 | gpio_free(led_dat->gpio); | ||
117 | goto err; | ||
118 | } | ||
119 | } | 151 | } |
120 | 152 | ||
121 | platform_set_drvdata(pdev, leds_data); | 153 | platform_set_drvdata(pdev, leds_data); |
@@ -123,13 +155,8 @@ static int gpio_led_probe(struct platform_device *pdev) | |||
123 | return 0; | 155 | return 0; |
124 | 156 | ||
125 | err: | 157 | err: |
126 | if (i > 0) { | 158 | for (i = i - 1; i >= 0; i--) |
127 | for (i = i - 1; i >= 0; i--) { | 159 | delete_gpio_led(&leds_data[i]); |
128 | led_classdev_unregister(&leds_data[i].cdev); | ||
129 | cancel_work_sync(&leds_data[i].work); | ||
130 | gpio_free(leds_data[i].gpio); | ||
131 | } | ||
132 | } | ||
133 | 160 | ||
134 | kfree(leds_data); | 161 | kfree(leds_data); |
135 | 162 | ||
@@ -144,11 +171,8 @@ static int __devexit gpio_led_remove(struct platform_device *pdev) | |||
144 | 171 | ||
145 | leds_data = platform_get_drvdata(pdev); | 172 | leds_data = platform_get_drvdata(pdev); |
146 | 173 | ||
147 | for (i = 0; i < pdata->num_leds; i++) { | 174 | for (i = 0; i < pdata->num_leds; i++) |
148 | led_classdev_unregister(&leds_data[i].cdev); | 175 | delete_gpio_led(&leds_data[i]); |
149 | cancel_work_sync(&leds_data[i].work); | ||
150 | gpio_free(leds_data[i].gpio); | ||
151 | } | ||
152 | 176 | ||
153 | kfree(leds_data); | 177 | kfree(leds_data); |
154 | 178 | ||
@@ -164,20 +188,133 @@ static struct platform_driver gpio_led_driver = { | |||
164 | }, | 188 | }, |
165 | }; | 189 | }; |
166 | 190 | ||
191 | MODULE_ALIAS("platform:leds-gpio"); | ||
192 | #endif /* CONFIG_LEDS_GPIO_PLATFORM */ | ||
193 | |||
194 | /* Code to create from OpenFirmware platform devices */ | ||
195 | #ifdef CONFIG_LEDS_GPIO_OF | ||
196 | #include <linux/of_platform.h> | ||
197 | #include <linux/of_gpio.h> | ||
198 | |||
199 | struct gpio_led_of_platform_data { | ||
200 | int num_leds; | ||
201 | struct gpio_led_data led_data[]; | ||
202 | }; | ||
203 | |||
204 | static int __devinit of_gpio_leds_probe(struct of_device *ofdev, | ||
205 | const struct of_device_id *match) | ||
206 | { | ||
207 | struct device_node *np = ofdev->node, *child; | ||
208 | struct gpio_led led; | ||
209 | struct gpio_led_of_platform_data *pdata; | ||
210 | int count = 0, ret; | ||
211 | |||
212 | /* count LEDs defined by this device, so we know how much to allocate */ | ||
213 | for_each_child_of_node(np, child) | ||
214 | count++; | ||
215 | if (!count) | ||
216 | return 0; /* or ENODEV? */ | ||
217 | |||
218 | pdata = kzalloc(sizeof(*pdata) + sizeof(struct gpio_led_data) * count, | ||
219 | GFP_KERNEL); | ||
220 | if (!pdata) | ||
221 | return -ENOMEM; | ||
222 | |||
223 | memset(&led, 0, sizeof(led)); | ||
224 | for_each_child_of_node(np, child) { | ||
225 | enum of_gpio_flags flags; | ||
226 | |||
227 | led.gpio = of_get_gpio_flags(child, 0, &flags); | ||
228 | led.active_low = flags & OF_GPIO_ACTIVE_LOW; | ||
229 | led.name = of_get_property(child, "label", NULL) ? : child->name; | ||
230 | led.default_trigger = | ||
231 | of_get_property(child, "linux,default-trigger", NULL); | ||
232 | |||
233 | ret = create_gpio_led(&led, &pdata->led_data[pdata->num_leds++], | ||
234 | &ofdev->dev, NULL); | ||
235 | if (ret < 0) { | ||
236 | of_node_put(child); | ||
237 | goto err; | ||
238 | } | ||
239 | } | ||
240 | |||
241 | dev_set_drvdata(&ofdev->dev, pdata); | ||
242 | |||
243 | return 0; | ||
244 | |||
245 | err: | ||
246 | for (count = pdata->num_leds - 2; count >= 0; count--) | ||
247 | delete_gpio_led(&pdata->led_data[count]); | ||
248 | |||
249 | kfree(pdata); | ||
250 | |||
251 | return ret; | ||
252 | } | ||
253 | |||
254 | static int __devexit of_gpio_leds_remove(struct of_device *ofdev) | ||
255 | { | ||
256 | struct gpio_led_of_platform_data *pdata = dev_get_drvdata(&ofdev->dev); | ||
257 | int i; | ||
258 | |||
259 | for (i = 0; i < pdata->num_leds; i++) | ||
260 | delete_gpio_led(&pdata->led_data[i]); | ||
261 | |||
262 | kfree(pdata); | ||
263 | |||
264 | dev_set_drvdata(&ofdev->dev, NULL); | ||
265 | |||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | static const struct of_device_id of_gpio_leds_match[] = { | ||
270 | { .compatible = "gpio-leds", }, | ||
271 | {}, | ||
272 | }; | ||
273 | |||
274 | static struct of_platform_driver of_gpio_leds_driver = { | ||
275 | .driver = { | ||
276 | .name = "of_gpio_leds", | ||
277 | .owner = THIS_MODULE, | ||
278 | }, | ||
279 | .match_table = of_gpio_leds_match, | ||
280 | .probe = of_gpio_leds_probe, | ||
281 | .remove = __devexit_p(of_gpio_leds_remove), | ||
282 | }; | ||
283 | #endif | ||
284 | |||
167 | static int __init gpio_led_init(void) | 285 | static int __init gpio_led_init(void) |
168 | { | 286 | { |
169 | return platform_driver_register(&gpio_led_driver); | 287 | int ret; |
288 | |||
289 | #ifdef CONFIG_LEDS_GPIO_PLATFORM | ||
290 | ret = platform_driver_register(&gpio_led_driver); | ||
291 | if (ret) | ||
292 | return ret; | ||
293 | #endif | ||
294 | #ifdef CONFIG_LEDS_GPIO_OF | ||
295 | ret = of_register_platform_driver(&of_gpio_leds_driver); | ||
296 | #endif | ||
297 | #ifdef CONFIG_LEDS_GPIO_PLATFORM | ||
298 | if (ret) | ||
299 | platform_driver_unregister(&gpio_led_driver); | ||
300 | #endif | ||
301 | |||
302 | return ret; | ||
170 | } | 303 | } |
171 | 304 | ||
172 | static void __exit gpio_led_exit(void) | 305 | static void __exit gpio_led_exit(void) |
173 | { | 306 | { |
307 | #ifdef CONFIG_LEDS_GPIO_PLATFORM | ||
174 | platform_driver_unregister(&gpio_led_driver); | 308 | platform_driver_unregister(&gpio_led_driver); |
309 | #endif | ||
310 | #ifdef CONFIG_LEDS_GPIO_OF | ||
311 | of_unregister_platform_driver(&of_gpio_leds_driver); | ||
312 | #endif | ||
175 | } | 313 | } |
176 | 314 | ||
177 | module_init(gpio_led_init); | 315 | module_init(gpio_led_init); |
178 | module_exit(gpio_led_exit); | 316 | module_exit(gpio_led_exit); |
179 | 317 | ||
180 | MODULE_AUTHOR("Raphael Assenat <raph@8d.com>"); | 318 | MODULE_AUTHOR("Raphael Assenat <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>"); |
181 | MODULE_DESCRIPTION("GPIO LED driver"); | 319 | MODULE_DESCRIPTION("GPIO LED driver"); |
182 | MODULE_LICENSE("GPL"); | 320 | MODULE_LICENSE("GPL"); |
183 | MODULE_ALIAS("platform:leds-gpio"); | ||
diff --git a/drivers/leds/leds-h1940.c b/drivers/leds/leds-h1940.c index 11b77a70bbcb..1aa46a390a0d 100644 --- a/drivers/leds/leds-h1940.c +++ b/drivers/leds/leds-h1940.c | |||
@@ -104,7 +104,7 @@ static struct led_classdev h1940_blueled = { | |||
104 | .default_trigger = "h1940-bluetooth", | 104 | .default_trigger = "h1940-bluetooth", |
105 | }; | 105 | }; |
106 | 106 | ||
107 | static int __init h1940leds_probe(struct platform_device *pdev) | 107 | static int __devinit h1940leds_probe(struct platform_device *pdev) |
108 | { | 108 | { |
109 | int ret; | 109 | int ret; |
110 | 110 | ||
diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index bd3b431c9710..3937244fdcab 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c | |||
@@ -169,7 +169,7 @@ static int pca9532_event(struct input_dev *dev, unsigned int type, | |||
169 | { | 169 | { |
170 | struct pca9532_data *data = input_get_drvdata(dev); | 170 | struct pca9532_data *data = input_get_drvdata(dev); |
171 | 171 | ||
172 | if (type != EV_SND && (code != SND_BELL || code != SND_TONE)) | 172 | if (!(type == EV_SND && (code == SND_BELL || code == SND_TONE))) |
173 | return -1; | 173 | return -1; |
174 | 174 | ||
175 | /* XXX: allow different kind of beeps with psc/pwm modifications */ | 175 | /* XXX: allow different kind of beeps with psc/pwm modifications */ |
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c new file mode 100644 index 000000000000..cdfdc8714e10 --- /dev/null +++ b/drivers/leds/leds-pwm.c | |||
@@ -0,0 +1,153 @@ | |||
1 | /* | ||
2 | * linux/drivers/leds-pwm.c | ||
3 | * | ||
4 | * simple PWM based LED control | ||
5 | * | ||
6 | * Copyright 2009 Luotao Fu @ Pengutronix (l.fu@pengutronix.de) | ||
7 | * | ||
8 | * based on leds-gpio.c by Raphael Assenat <raph@8d.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/fb.h> | ||
20 | #include <linux/leds.h> | ||
21 | #include <linux/err.h> | ||
22 | #include <linux/pwm.h> | ||
23 | #include <linux/leds_pwm.h> | ||
24 | |||
25 | struct led_pwm_data { | ||
26 | struct led_classdev cdev; | ||
27 | struct pwm_device *pwm; | ||
28 | unsigned int active_low; | ||
29 | unsigned int period; | ||
30 | unsigned int max_brightness; | ||
31 | }; | ||
32 | |||
33 | static void led_pwm_set(struct led_classdev *led_cdev, | ||
34 | enum led_brightness brightness) | ||
35 | { | ||
36 | struct led_pwm_data *led_dat = | ||
37 | container_of(led_cdev, struct led_pwm_data, cdev); | ||
38 | unsigned int max = led_dat->max_brightness; | ||
39 | unsigned int period = led_dat->period; | ||
40 | |||
41 | if (brightness == 0) { | ||
42 | pwm_config(led_dat->pwm, 0, period); | ||
43 | pwm_disable(led_dat->pwm); | ||
44 | } else { | ||
45 | pwm_config(led_dat->pwm, brightness * period / max, period); | ||
46 | pwm_enable(led_dat->pwm); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | static int led_pwm_probe(struct platform_device *pdev) | ||
51 | { | ||
52 | struct led_pwm_platform_data *pdata = pdev->dev.platform_data; | ||
53 | struct led_pwm *cur_led; | ||
54 | struct led_pwm_data *leds_data, *led_dat; | ||
55 | int i, ret = 0; | ||
56 | |||
57 | if (!pdata) | ||
58 | return -EBUSY; | ||
59 | |||
60 | leds_data = kzalloc(sizeof(struct led_pwm_data) * pdata->num_leds, | ||
61 | GFP_KERNEL); | ||
62 | if (!leds_data) | ||
63 | return -ENOMEM; | ||
64 | |||
65 | for (i = 0; i < pdata->num_leds; i++) { | ||
66 | cur_led = &pdata->leds[i]; | ||
67 | led_dat = &leds_data[i]; | ||
68 | |||
69 | led_dat->pwm = pwm_request(cur_led->pwm_id, | ||
70 | cur_led->name); | ||
71 | if (IS_ERR(led_dat->pwm)) { | ||
72 | dev_err(&pdev->dev, "unable to request PWM %d\n", | ||
73 | cur_led->pwm_id); | ||
74 | goto err; | ||
75 | } | ||
76 | |||
77 | led_dat->cdev.name = cur_led->name; | ||
78 | led_dat->cdev.default_trigger = cur_led->default_trigger; | ||
79 | led_dat->active_low = cur_led->active_low; | ||
80 | led_dat->max_brightness = cur_led->max_brightness; | ||
81 | led_dat->period = cur_led->pwm_period_ns; | ||
82 | led_dat->cdev.brightness_set = led_pwm_set; | ||
83 | led_dat->cdev.brightness = LED_OFF; | ||
84 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | ||
85 | |||
86 | ret = led_classdev_register(&pdev->dev, &led_dat->cdev); | ||
87 | if (ret < 0) { | ||
88 | pwm_free(led_dat->pwm); | ||
89 | goto err; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | platform_set_drvdata(pdev, leds_data); | ||
94 | |||
95 | return 0; | ||
96 | |||
97 | err: | ||
98 | if (i > 0) { | ||
99 | for (i = i - 1; i >= 0; i--) { | ||
100 | led_classdev_unregister(&leds_data[i].cdev); | ||
101 | pwm_free(leds_data[i].pwm); | ||
102 | } | ||
103 | } | ||
104 | |||
105 | kfree(leds_data); | ||
106 | |||
107 | return ret; | ||
108 | } | ||
109 | |||
110 | static int __devexit led_pwm_remove(struct platform_device *pdev) | ||
111 | { | ||
112 | int i; | ||
113 | struct led_pwm_platform_data *pdata = pdev->dev.platform_data; | ||
114 | struct led_pwm_data *leds_data; | ||
115 | |||
116 | leds_data = platform_get_drvdata(pdev); | ||
117 | |||
118 | for (i = 0; i < pdata->num_leds; i++) { | ||
119 | led_classdev_unregister(&leds_data[i].cdev); | ||
120 | pwm_free(leds_data[i].pwm); | ||
121 | } | ||
122 | |||
123 | kfree(leds_data); | ||
124 | |||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static struct platform_driver led_pwm_driver = { | ||
129 | .probe = led_pwm_probe, | ||
130 | .remove = __devexit_p(led_pwm_remove), | ||
131 | .driver = { | ||
132 | .name = "leds_pwm", | ||
133 | .owner = THIS_MODULE, | ||
134 | }, | ||
135 | }; | ||
136 | |||
137 | static int __init led_pwm_init(void) | ||
138 | { | ||
139 | return platform_driver_register(&led_pwm_driver); | ||
140 | } | ||
141 | |||
142 | static void __exit led_pwm_exit(void) | ||
143 | { | ||
144 | platform_driver_unregister(&led_pwm_driver); | ||
145 | } | ||
146 | |||
147 | module_init(led_pwm_init); | ||
148 | module_exit(led_pwm_exit); | ||
149 | |||
150 | MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>"); | ||
151 | MODULE_DESCRIPTION("PWM LED driver for PXA"); | ||
152 | MODULE_LICENSE("GPL"); | ||
153 | MODULE_ALIAS("platform:leds-pwm"); | ||
diff --git a/drivers/leds/leds-rb532.c b/drivers/leds/leds-rb532.c new file mode 100644 index 000000000000..c3525f37f73d --- /dev/null +++ b/drivers/leds/leds-rb532.c | |||
@@ -0,0 +1,77 @@ | |||
1 | /* | ||
2 | * LEDs driver for the "User LED" on Routerboard532 | ||
3 | * | ||
4 | * Copyright (C) 2009 Phil Sutter <n0-1@freewrt.org> | ||
5 | * | ||
6 | * Based on leds-cobalt-qube.c by Florian Fainelly and | ||
7 | * rb-diag.c (my own standalone driver for both LED and | ||
8 | * button of Routerboard532). | ||
9 | */ | ||
10 | |||
11 | #include <linux/leds.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/platform_device.h> | ||
14 | |||
15 | #include <asm/mach-rc32434/gpio.h> | ||
16 | #include <asm/mach-rc32434/rb.h> | ||
17 | |||
18 | static void rb532_led_set(struct led_classdev *cdev, | ||
19 | enum led_brightness brightness) | ||
20 | { | ||
21 | if (brightness) | ||
22 | set_latch_u5(LO_ULED, 0); | ||
23 | |||
24 | else | ||
25 | set_latch_u5(0, LO_ULED); | ||
26 | } | ||
27 | |||
28 | static enum led_brightness rb532_led_get(struct led_classdev *cdev) | ||
29 | { | ||
30 | return (get_latch_u5() & LO_ULED) ? LED_FULL : LED_OFF; | ||
31 | } | ||
32 | |||
33 | static struct led_classdev rb532_uled = { | ||
34 | .name = "uled", | ||
35 | .brightness_set = rb532_led_set, | ||
36 | .brightness_get = rb532_led_get, | ||
37 | .default_trigger = "nand-disk", | ||
38 | }; | ||
39 | |||
40 | static int __devinit rb532_led_probe(struct platform_device *pdev) | ||
41 | { | ||
42 | return led_classdev_register(&pdev->dev, &rb532_uled); | ||
43 | } | ||
44 | |||
45 | static int __devexit rb532_led_remove(struct platform_device *pdev) | ||
46 | { | ||
47 | led_classdev_unregister(&rb532_uled); | ||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | static struct platform_driver rb532_led_driver = { | ||
52 | .probe = rb532_led_probe, | ||
53 | .remove = __devexit_p(rb532_led_remove), | ||
54 | .driver = { | ||
55 | .name = "rb532-led", | ||
56 | .owner = THIS_MODULE, | ||
57 | }, | ||
58 | }; | ||
59 | |||
60 | static int __init rb532_led_init(void) | ||
61 | { | ||
62 | return platform_driver_register(&rb532_led_driver); | ||
63 | } | ||
64 | |||
65 | static void __exit rb532_led_exit(void) | ||
66 | { | ||
67 | platform_driver_unregister(&rb532_led_driver); | ||
68 | } | ||
69 | |||
70 | module_init(rb532_led_init); | ||
71 | module_exit(rb532_led_exit); | ||
72 | |||
73 | MODULE_ALIAS("platform:rb532-led"); | ||
74 | |||
75 | MODULE_LICENSE("GPL"); | ||
76 | MODULE_DESCRIPTION("User LED support for Routerboard532"); | ||
77 | MODULE_AUTHOR("Phil Sutter <n0-1@freewrt.org>"); | ||
diff --git a/drivers/leds/leds-s3c24xx.c b/drivers/leds/leds-s3c24xx.c index 4d81131542ae..aa2e7ae0cdae 100644 --- a/drivers/leds/leds-s3c24xx.c +++ b/drivers/leds/leds-s3c24xx.c | |||
@@ -102,14 +102,11 @@ static int s3c24xx_led_probe(struct platform_device *dev) | |||
102 | ret = led_classdev_register(&dev->dev, &led->cdev); | 102 | ret = led_classdev_register(&dev->dev, &led->cdev); |
103 | if (ret < 0) { | 103 | if (ret < 0) { |
104 | dev_err(&dev->dev, "led_classdev_register failed\n"); | 104 | dev_err(&dev->dev, "led_classdev_register failed\n"); |
105 | goto exit_err1; | 105 | kfree(led); |
106 | return ret; | ||
106 | } | 107 | } |
107 | 108 | ||
108 | return 0; | 109 | return 0; |
109 | |||
110 | exit_err1: | ||
111 | kfree(led); | ||
112 | return ret; | ||
113 | } | 110 | } |
114 | 111 | ||
115 | static struct platform_driver s3c24xx_led_driver = { | 112 | static struct platform_driver s3c24xx_led_driver = { |
diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h index 5edbf52c4fa7..2dd8ecbfdc31 100644 --- a/drivers/leds/leds.h +++ b/drivers/leds/leds.h | |||
@@ -20,8 +20,8 @@ | |||
20 | static inline void led_set_brightness(struct led_classdev *led_cdev, | 20 | static inline void led_set_brightness(struct led_classdev *led_cdev, |
21 | enum led_brightness value) | 21 | enum led_brightness value) |
22 | { | 22 | { |
23 | if (value > LED_FULL) | 23 | if (value > led_cdev->max_brightness) |
24 | value = LED_FULL; | 24 | value = led_cdev->max_brightness; |
25 | led_cdev->brightness = value; | 25 | led_cdev->brightness = value; |
26 | if (!(led_cdev->flags & LED_SUSPENDED)) | 26 | if (!(led_cdev->flags & LED_SUSPENDED)) |
27 | led_cdev->brightness_set(led_cdev, value); | 27 | led_cdev->brightness_set(led_cdev, value); |
diff --git a/drivers/leds/ledtrig-default-on.c b/drivers/leds/ledtrig-default-on.c index 92995e40cfa4..a4ef54b9d508 100644 --- a/drivers/leds/ledtrig-default-on.c +++ b/drivers/leds/ledtrig-default-on.c | |||
@@ -19,7 +19,7 @@ | |||
19 | 19 | ||
20 | static void defon_trig_activate(struct led_classdev *led_cdev) | 20 | static void defon_trig_activate(struct led_classdev *led_cdev) |
21 | { | 21 | { |
22 | led_set_brightness(led_cdev, LED_FULL); | 22 | led_set_brightness(led_cdev, led_cdev->max_brightness); |
23 | } | 23 | } |
24 | 24 | ||
25 | static struct led_trigger defon_led_trigger = { | 25 | static struct led_trigger defon_led_trigger = { |
diff --git a/drivers/leds/ledtrig-gpio.c b/drivers/leds/ledtrig-gpio.c new file mode 100644 index 000000000000..a247ae63374f --- /dev/null +++ b/drivers/leds/ledtrig-gpio.c | |||
@@ -0,0 +1,239 @@ | |||
1 | /* | ||
2 | * ledtrig-gio.c - LED Trigger Based on GPIO events | ||
3 | * | ||
4 | * Copyright 2009 Felipe Balbi <me@felipebalbi.com> | ||
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/module.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/gpio.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/workqueue.h> | ||
18 | #include <linux/leds.h> | ||
19 | #include "leds.h" | ||
20 | |||
21 | struct gpio_trig_data { | ||
22 | struct led_classdev *led; | ||
23 | struct work_struct work; | ||
24 | |||
25 | unsigned desired_brightness; /* desired brightness when led is on */ | ||
26 | unsigned inverted; /* true when gpio is inverted */ | ||
27 | unsigned gpio; /* gpio that triggers the leds */ | ||
28 | }; | ||
29 | |||
30 | static irqreturn_t gpio_trig_irq(int irq, void *_led) | ||
31 | { | ||
32 | struct led_classdev *led = _led; | ||
33 | struct gpio_trig_data *gpio_data = led->trigger_data; | ||
34 | |||
35 | /* just schedule_work since gpio_get_value can sleep */ | ||
36 | schedule_work(&gpio_data->work); | ||
37 | |||
38 | return IRQ_HANDLED; | ||
39 | }; | ||
40 | |||
41 | static void gpio_trig_work(struct work_struct *work) | ||
42 | { | ||
43 | struct gpio_trig_data *gpio_data = container_of(work, | ||
44 | struct gpio_trig_data, work); | ||
45 | int tmp; | ||
46 | |||
47 | if (!gpio_data->gpio) | ||
48 | return; | ||
49 | |||
50 | tmp = gpio_get_value(gpio_data->gpio); | ||
51 | if (gpio_data->inverted) | ||
52 | tmp = !tmp; | ||
53 | |||
54 | if (tmp) { | ||
55 | if (gpio_data->desired_brightness) | ||
56 | led_set_brightness(gpio_data->led, | ||
57 | gpio_data->desired_brightness); | ||
58 | else | ||
59 | led_set_brightness(gpio_data->led, LED_FULL); | ||
60 | } else { | ||
61 | led_set_brightness(gpio_data->led, LED_OFF); | ||
62 | } | ||
63 | } | ||
64 | |||
65 | static ssize_t gpio_trig_brightness_show(struct device *dev, | ||
66 | struct device_attribute *attr, char *buf) | ||
67 | { | ||
68 | struct led_classdev *led = dev_get_drvdata(dev); | ||
69 | struct gpio_trig_data *gpio_data = led->trigger_data; | ||
70 | |||
71 | return sprintf(buf, "%u\n", gpio_data->desired_brightness); | ||
72 | } | ||
73 | |||
74 | static ssize_t gpio_trig_brightness_store(struct device *dev, | ||
75 | struct device_attribute *attr, const char *buf, size_t n) | ||
76 | { | ||
77 | struct led_classdev *led = dev_get_drvdata(dev); | ||
78 | struct gpio_trig_data *gpio_data = led->trigger_data; | ||
79 | unsigned desired_brightness; | ||
80 | int ret; | ||
81 | |||
82 | ret = sscanf(buf, "%u", &desired_brightness); | ||
83 | if (ret < 1 || desired_brightness > 255) { | ||
84 | dev_err(dev, "invalid value\n"); | ||
85 | return -EINVAL; | ||
86 | } | ||
87 | |||
88 | gpio_data->desired_brightness = desired_brightness; | ||
89 | |||
90 | return n; | ||
91 | } | ||
92 | static DEVICE_ATTR(desired_brightness, 0644, gpio_trig_brightness_show, | ||
93 | gpio_trig_brightness_store); | ||
94 | |||
95 | static ssize_t gpio_trig_inverted_show(struct device *dev, | ||
96 | struct device_attribute *attr, char *buf) | ||
97 | { | ||
98 | struct led_classdev *led = dev_get_drvdata(dev); | ||
99 | struct gpio_trig_data *gpio_data = led->trigger_data; | ||
100 | |||
101 | return sprintf(buf, "%s\n", gpio_data->inverted ? "yes" : "no"); | ||
102 | } | ||
103 | |||
104 | static ssize_t gpio_trig_inverted_store(struct device *dev, | ||
105 | struct device_attribute *attr, const char *buf, size_t n) | ||
106 | { | ||
107 | struct led_classdev *led = dev_get_drvdata(dev); | ||
108 | struct gpio_trig_data *gpio_data = led->trigger_data; | ||
109 | unsigned inverted; | ||
110 | int ret; | ||
111 | |||
112 | ret = sscanf(buf, "%u", &inverted); | ||
113 | if (ret < 1) { | ||
114 | dev_err(dev, "invalid value\n"); | ||
115 | return -EINVAL; | ||
116 | } | ||
117 | |||
118 | gpio_data->inverted = !!inverted; | ||
119 | |||
120 | return n; | ||
121 | } | ||
122 | static DEVICE_ATTR(inverted, 0644, gpio_trig_inverted_show, | ||
123 | gpio_trig_inverted_store); | ||
124 | |||
125 | static ssize_t gpio_trig_gpio_show(struct device *dev, | ||
126 | struct device_attribute *attr, char *buf) | ||
127 | { | ||
128 | struct led_classdev *led = dev_get_drvdata(dev); | ||
129 | struct gpio_trig_data *gpio_data = led->trigger_data; | ||
130 | |||
131 | return sprintf(buf, "%u\n", gpio_data->gpio); | ||
132 | } | ||
133 | |||
134 | static ssize_t gpio_trig_gpio_store(struct device *dev, | ||
135 | struct device_attribute *attr, const char *buf, size_t n) | ||
136 | { | ||
137 | struct led_classdev *led = dev_get_drvdata(dev); | ||
138 | struct gpio_trig_data *gpio_data = led->trigger_data; | ||
139 | unsigned gpio; | ||
140 | int ret; | ||
141 | |||
142 | ret = sscanf(buf, "%u", &gpio); | ||
143 | if (ret < 1) { | ||
144 | dev_err(dev, "couldn't read gpio number\n"); | ||
145 | flush_work(&gpio_data->work); | ||
146 | return -EINVAL; | ||
147 | } | ||
148 | |||
149 | if (!gpio) { | ||
150 | free_irq(gpio_to_irq(gpio_data->gpio), led); | ||
151 | return n; | ||
152 | } | ||
153 | |||
154 | if (gpio_data->gpio > 0 && gpio_data->gpio != gpio) | ||
155 | free_irq(gpio_to_irq(gpio_data->gpio), led); | ||
156 | |||
157 | gpio_data->gpio = gpio; | ||
158 | ret = request_irq(gpio_to_irq(gpio), gpio_trig_irq, | ||
159 | IRQF_SHARED | IRQF_TRIGGER_RISING | ||
160 | | IRQF_TRIGGER_FALLING, "ledtrig-gpio", led); | ||
161 | if (ret) | ||
162 | dev_err(dev, "request_irq failed with error %d\n", ret); | ||
163 | |||
164 | return ret ? ret : n; | ||
165 | } | ||
166 | static DEVICE_ATTR(gpio, 0644, gpio_trig_gpio_show, gpio_trig_gpio_store); | ||
167 | |||
168 | static void gpio_trig_activate(struct led_classdev *led) | ||
169 | { | ||
170 | struct gpio_trig_data *gpio_data; | ||
171 | int ret; | ||
172 | |||
173 | gpio_data = kzalloc(sizeof(*gpio_data), GFP_KERNEL); | ||
174 | if (!gpio_data) | ||
175 | return; | ||
176 | |||
177 | ret = device_create_file(led->dev, &dev_attr_gpio); | ||
178 | if (ret) | ||
179 | goto err_gpio; | ||
180 | |||
181 | ret = device_create_file(led->dev, &dev_attr_inverted); | ||
182 | if (ret) | ||
183 | goto err_inverted; | ||
184 | |||
185 | ret = device_create_file(led->dev, &dev_attr_desired_brightness); | ||
186 | if (ret) | ||
187 | goto err_brightness; | ||
188 | |||
189 | gpio_data->led = led; | ||
190 | led->trigger_data = gpio_data; | ||
191 | INIT_WORK(&gpio_data->work, gpio_trig_work); | ||
192 | |||
193 | return; | ||
194 | |||
195 | err_brightness: | ||
196 | device_remove_file(led->dev, &dev_attr_inverted); | ||
197 | |||
198 | err_inverted: | ||
199 | device_remove_file(led->dev, &dev_attr_gpio); | ||
200 | |||
201 | err_gpio: | ||
202 | kfree(gpio_data); | ||
203 | } | ||
204 | |||
205 | static void gpio_trig_deactivate(struct led_classdev *led) | ||
206 | { | ||
207 | struct gpio_trig_data *gpio_data = led->trigger_data; | ||
208 | |||
209 | if (gpio_data) { | ||
210 | device_remove_file(led->dev, &dev_attr_gpio); | ||
211 | device_remove_file(led->dev, &dev_attr_inverted); | ||
212 | device_remove_file(led->dev, &dev_attr_desired_brightness); | ||
213 | flush_work(&gpio_data->work); | ||
214 | free_irq(gpio_to_irq(gpio_data->gpio),led); | ||
215 | kfree(gpio_data); | ||
216 | } | ||
217 | } | ||
218 | |||
219 | static struct led_trigger gpio_led_trigger = { | ||
220 | .name = "gpio", | ||
221 | .activate = gpio_trig_activate, | ||
222 | .deactivate = gpio_trig_deactivate, | ||
223 | }; | ||
224 | |||
225 | static int __init gpio_trig_init(void) | ||
226 | { | ||
227 | return led_trigger_register(&gpio_led_trigger); | ||
228 | } | ||
229 | module_init(gpio_trig_init); | ||
230 | |||
231 | static void __exit gpio_trig_exit(void) | ||
232 | { | ||
233 | led_trigger_unregister(&gpio_led_trigger); | ||
234 | } | ||
235 | module_exit(gpio_trig_exit); | ||
236 | |||
237 | MODULE_AUTHOR("Felipe Balbi <me@felipebalbi.com>"); | ||
238 | MODULE_DESCRIPTION("GPIO LED trigger"); | ||
239 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/leds/ledtrig-heartbeat.c b/drivers/leds/ledtrig-heartbeat.c index 4bf8cec8b8c1..c1c1ea6f817b 100644 --- a/drivers/leds/ledtrig-heartbeat.c +++ b/drivers/leds/ledtrig-heartbeat.c | |||
@@ -47,7 +47,7 @@ static void led_heartbeat_function(unsigned long data) | |||
47 | msecs_to_jiffies(heartbeat_data->period); | 47 | msecs_to_jiffies(heartbeat_data->period); |
48 | delay = msecs_to_jiffies(70); | 48 | delay = msecs_to_jiffies(70); |
49 | heartbeat_data->phase++; | 49 | heartbeat_data->phase++; |
50 | brightness = LED_FULL; | 50 | brightness = led_cdev->max_brightness; |
51 | break; | 51 | break; |
52 | case 1: | 52 | case 1: |
53 | delay = heartbeat_data->period / 4 - msecs_to_jiffies(70); | 53 | delay = heartbeat_data->period / 4 - msecs_to_jiffies(70); |
@@ -56,7 +56,7 @@ static void led_heartbeat_function(unsigned long data) | |||
56 | case 2: | 56 | case 2: |
57 | delay = msecs_to_jiffies(70); | 57 | delay = msecs_to_jiffies(70); |
58 | heartbeat_data->phase++; | 58 | heartbeat_data->phase++; |
59 | brightness = LED_FULL; | 59 | brightness = led_cdev->max_brightness; |
60 | break; | 60 | break; |
61 | default: | 61 | default: |
62 | delay = heartbeat_data->period - heartbeat_data->period / 4 - | 62 | delay = heartbeat_data->period - heartbeat_data->period / 4 - |
diff --git a/drivers/leds/ledtrig-ide-disk.c b/drivers/leds/ledtrig-ide-disk.c index 883a577b1b97..ec099fcbcb00 100644 --- a/drivers/leds/ledtrig-ide-disk.c +++ b/drivers/leds/ledtrig-ide-disk.c | |||
@@ -37,7 +37,8 @@ static void ledtrig_ide_timerfunc(unsigned long data) | |||
37 | { | 37 | { |
38 | if (ide_lastactivity != ide_activity) { | 38 | if (ide_lastactivity != ide_activity) { |
39 | ide_lastactivity = ide_activity; | 39 | ide_lastactivity = ide_activity; |
40 | led_trigger_event(ledtrig_ide, LED_FULL); | 40 | /* INT_MAX will set each LED to its maximum brightness */ |
41 | led_trigger_event(ledtrig_ide, INT_MAX); | ||
41 | mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10)); | 42 | mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10)); |
42 | } else { | 43 | } else { |
43 | led_trigger_event(ledtrig_ide, LED_OFF); | 44 | led_trigger_event(ledtrig_ide, LED_OFF); |
diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c index 3d6531396dda..3b83406de752 100644 --- a/drivers/leds/ledtrig-timer.c +++ b/drivers/leds/ledtrig-timer.c | |||
@@ -166,7 +166,7 @@ static void timer_trig_activate(struct led_classdev *led_cdev) | |||
166 | 166 | ||
167 | timer_data->brightness_on = led_get_brightness(led_cdev); | 167 | timer_data->brightness_on = led_get_brightness(led_cdev); |
168 | if (timer_data->brightness_on == LED_OFF) | 168 | if (timer_data->brightness_on == LED_OFF) |
169 | timer_data->brightness_on = LED_FULL; | 169 | timer_data->brightness_on = led_cdev->max_brightness; |
170 | led_cdev->trigger_data = timer_data; | 170 | led_cdev->trigger_data = timer_data; |
171 | 171 | ||
172 | init_timer(&timer_data->timer); | 172 | init_timer(&timer_data->timer); |