diff options
Diffstat (limited to 'drivers/leds')
-rw-r--r-- | drivers/leds/Kconfig | 13 | ||||
-rw-r--r-- | drivers/leds/Makefile | 1 | ||||
-rw-r--r-- | drivers/leds/ledtrig-gpio.c | 239 |
3 files changed, 253 insertions, 0 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index db5e6a64c7cf..9c131f30757c 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig | |||
@@ -255,6 +255,19 @@ config LEDS_TRIGGER_BACKLIGHT | |||
255 | 255 | ||
256 | If unsure, say N. | 256 | If unsure, say N. |
257 | 257 | ||
258 | config LEDS_TRIGGER_GPIO | ||
259 | tristate "LED GPIO Trigger" | ||
260 | depends on LEDS_TRIGGERS | ||
261 | depends on GPIOLIB | ||
262 | help | ||
263 | This allows LEDs to be controlled by gpio events. It's good | ||
264 | when using gpios as switches and triggering the needed LEDs | ||
265 | from there. One use case is n810's keypad LEDs that could | ||
266 | be triggered by this trigger when user slides up to show | ||
267 | keypad. | ||
268 | |||
269 | If unsure, say N. | ||
270 | |||
258 | config LEDS_TRIGGER_DEFAULT_ON | 271 | config LEDS_TRIGGER_DEFAULT_ON |
259 | tristate "LED Default ON Trigger" | 272 | tristate "LED Default ON Trigger" |
260 | depends on LEDS_TRIGGERS | 273 | depends on LEDS_TRIGGERS |
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 017f69aa2ecd..291aea22bf0c 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile | |||
@@ -35,4 +35,5 @@ obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o | |||
35 | obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o | 35 | obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o |
36 | obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o | 36 | obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o |
37 | obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o | 37 | obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o |
38 | obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o | ||
38 | obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o | 39 | obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o |
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"); | ||