diff options
-rw-r--r-- | Documentation/ABI/testing/sysfs-class-led-driver-lm3533 | 65 | ||||
-rw-r--r-- | drivers/leds/Kconfig | 13 | ||||
-rw-r--r-- | drivers/leds/Makefile | 1 | ||||
-rw-r--r-- | drivers/leds/leds-lm3533.c | 785 |
4 files changed, 864 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-led-driver-lm3533 b/Documentation/ABI/testing/sysfs-class-led-driver-lm3533 new file mode 100644 index 000000000000..620ebb3b9baa --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-led-driver-lm3533 | |||
@@ -0,0 +1,65 @@ | |||
1 | What: /sys/class/leds/<led>/als_channel | ||
2 | Date: May 2012 | ||
3 | KernelVersion: 3.5 | ||
4 | Contact: Johan Hovold <jhovold@gmail.com> | ||
5 | Description: | ||
6 | Set the ALS output channel to use as input in | ||
7 | ALS-current-control mode (1, 2), where | ||
8 | |||
9 | 1 - out_current1 | ||
10 | 2 - out_current2 | ||
11 | |||
12 | What: /sys/class/leds/<led>/als_en | ||
13 | Date: May 2012 | ||
14 | KernelVersion: 3.5 | ||
15 | Contact: Johan Hovold <jhovold@gmail.com> | ||
16 | Description: | ||
17 | Enable ALS-current-control mode (0, 1). | ||
18 | |||
19 | What: /sys/class/leds/<led>/falltime | ||
20 | What: /sys/class/leds/<led>/risetime | ||
21 | Date: April 2012 | ||
22 | KernelVersion: 3.5 | ||
23 | Contact: Johan Hovold <jhovold@gmail.com> | ||
24 | Description: | ||
25 | Set the pattern generator fall and rise times (0..7), where | ||
26 | |||
27 | 0 - 2048 us | ||
28 | 1 - 262 ms | ||
29 | 2 - 524 ms | ||
30 | 3 - 1.049 s | ||
31 | 4 - 2.097 s | ||
32 | 5 - 4.194 s | ||
33 | 6 - 8.389 s | ||
34 | 7 - 16.78 s | ||
35 | |||
36 | What: /sys/class/leds/<led>/id | ||
37 | Date: April 2012 | ||
38 | KernelVersion: 3.5 | ||
39 | Contact: Johan Hovold <jhovold@gmail.com> | ||
40 | Description: | ||
41 | Get the id of this led (0..3). | ||
42 | |||
43 | What: /sys/class/leds/<led>/linear | ||
44 | Date: April 2012 | ||
45 | KernelVersion: 3.5 | ||
46 | Contact: Johan Hovold <jhovold@gmail.com> | ||
47 | Description: | ||
48 | Set the brightness-mapping mode (0, 1), where | ||
49 | |||
50 | 0 - exponential mode | ||
51 | 1 - linear mode | ||
52 | |||
53 | What: /sys/class/leds/<led>/pwm | ||
54 | Date: April 2012 | ||
55 | KernelVersion: 3.5 | ||
56 | Contact: Johan Hovold <jhovold@gmail.com> | ||
57 | Description: | ||
58 | Set the PWM-input control mask (5 bits), where | ||
59 | |||
60 | bit 5 - PWM-input enabled in Zone 4 | ||
61 | bit 4 - PWM-input enabled in Zone 3 | ||
62 | bit 3 - PWM-input enabled in Zone 2 | ||
63 | bit 2 - PWM-input enabled in Zone 1 | ||
64 | bit 1 - PWM-input enabled in Zone 0 | ||
65 | bit 0 - PWM-input enabled | ||
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 6b2e1e4fdeb8..04cb8c88d74b 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig | |||
@@ -50,6 +50,19 @@ config LEDS_LM3530 | |||
50 | controlled manually or using PWM input or using ambient | 50 | controlled manually or using PWM input or using ambient |
51 | light automatically. | 51 | light automatically. |
52 | 52 | ||
53 | config LEDS_LM3533 | ||
54 | tristate "LED support for LM3533" | ||
55 | depends on LEDS_CLASS | ||
56 | depends on MFD_LM3533 | ||
57 | help | ||
58 | This option enables support for the LEDs on National Semiconductor / | ||
59 | TI LM3533 Lighting Power chips. | ||
60 | |||
61 | The LEDs can be controlled directly, through PWM input, or by the | ||
62 | ambient-light-sensor interface. The chip supports | ||
63 | hardware-accelerated blinking with maximum on and off periods of 9.8 | ||
64 | and 77 seconds respectively. | ||
65 | |||
53 | config LEDS_LOCOMO | 66 | config LEDS_LOCOMO |
54 | tristate "LED Support for Locomo device" | 67 | tristate "LED Support for Locomo device" |
55 | depends on LEDS_CLASS | 68 | depends on LEDS_CLASS |
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 4a4b96e8c3eb..f8958cd6cf6e 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile | |||
@@ -10,6 +10,7 @@ obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o | |||
10 | obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o | 10 | obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o |
11 | obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o | 11 | obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o |
12 | obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o | 12 | obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o |
13 | obj-$(CONFIG_LEDS_LM3533) += leds-lm3533.o | ||
13 | obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o | 14 | obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o |
14 | obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o | 15 | obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o |
15 | obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o | 16 | obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o |
diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c new file mode 100644 index 000000000000..f56b6e7ffdac --- /dev/null +++ b/drivers/leds/leds-lm3533.c | |||
@@ -0,0 +1,785 @@ | |||
1 | /* | ||
2 | * leds-lm3533.c -- LM3533 LED driver | ||
3 | * | ||
4 | * Copyright (C) 2011-2012 Texas Instruments | ||
5 | * | ||
6 | * Author: Johan Hovold <jhovold@gmail.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/leds.h> | ||
17 | #include <linux/mfd/core.h> | ||
18 | #include <linux/mutex.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/workqueue.h> | ||
22 | |||
23 | #include <linux/mfd/lm3533.h> | ||
24 | |||
25 | |||
26 | #define LM3533_LVCTRLBANK_MIN 2 | ||
27 | #define LM3533_LVCTRLBANK_MAX 5 | ||
28 | #define LM3533_LVCTRLBANK_COUNT 4 | ||
29 | #define LM3533_RISEFALLTIME_MAX 7 | ||
30 | #define LM3533_ALS_CHANNEL_LV_MIN 1 | ||
31 | #define LM3533_ALS_CHANNEL_LV_MAX 2 | ||
32 | |||
33 | #define LM3533_REG_CTRLBANK_BCONF_BASE 0x1b | ||
34 | #define LM3533_REG_PATTERN_ENABLE 0x28 | ||
35 | #define LM3533_REG_PATTERN_LOW_TIME_BASE 0x71 | ||
36 | #define LM3533_REG_PATTERN_HIGH_TIME_BASE 0x72 | ||
37 | #define LM3533_REG_PATTERN_RISETIME_BASE 0x74 | ||
38 | #define LM3533_REG_PATTERN_FALLTIME_BASE 0x75 | ||
39 | |||
40 | #define LM3533_REG_PATTERN_STEP 0x10 | ||
41 | |||
42 | #define LM3533_REG_CTRLBANK_BCONF_MAPPING_MASK 0x04 | ||
43 | #define LM3533_REG_CTRLBANK_BCONF_ALS_EN_MASK 0x02 | ||
44 | #define LM3533_REG_CTRLBANK_BCONF_ALS_CHANNEL_MASK 0x01 | ||
45 | |||
46 | #define LM3533_LED_FLAG_PATTERN_ENABLE 1 | ||
47 | |||
48 | |||
49 | struct lm3533_led { | ||
50 | struct lm3533 *lm3533; | ||
51 | struct lm3533_ctrlbank cb; | ||
52 | struct led_classdev cdev; | ||
53 | int id; | ||
54 | |||
55 | struct mutex mutex; | ||
56 | unsigned long flags; | ||
57 | |||
58 | struct work_struct work; | ||
59 | u8 new_brightness; | ||
60 | }; | ||
61 | |||
62 | |||
63 | static inline struct lm3533_led *to_lm3533_led(struct led_classdev *cdev) | ||
64 | { | ||
65 | return container_of(cdev, struct lm3533_led, cdev); | ||
66 | } | ||
67 | |||
68 | static inline int lm3533_led_get_ctrlbank_id(struct lm3533_led *led) | ||
69 | { | ||
70 | return led->id + 2; | ||
71 | } | ||
72 | |||
73 | static inline u8 lm3533_led_get_lv_reg(struct lm3533_led *led, u8 base) | ||
74 | { | ||
75 | return base + led->id; | ||
76 | } | ||
77 | |||
78 | static inline u8 lm3533_led_get_pattern(struct lm3533_led *led) | ||
79 | { | ||
80 | return led->id; | ||
81 | } | ||
82 | |||
83 | static inline u8 lm3533_led_get_pattern_reg(struct lm3533_led *led, | ||
84 | u8 base) | ||
85 | { | ||
86 | return base + lm3533_led_get_pattern(led) * LM3533_REG_PATTERN_STEP; | ||
87 | } | ||
88 | |||
89 | static int lm3533_led_pattern_enable(struct lm3533_led *led, int enable) | ||
90 | { | ||
91 | u8 mask; | ||
92 | u8 val; | ||
93 | int pattern; | ||
94 | int state; | ||
95 | int ret = 0; | ||
96 | |||
97 | dev_dbg(led->cdev.dev, "%s - %d\n", __func__, enable); | ||
98 | |||
99 | mutex_lock(&led->mutex); | ||
100 | |||
101 | state = test_bit(LM3533_LED_FLAG_PATTERN_ENABLE, &led->flags); | ||
102 | if ((enable && state) || (!enable && !state)) | ||
103 | goto out; | ||
104 | |||
105 | pattern = lm3533_led_get_pattern(led); | ||
106 | mask = 1 << (2 * pattern); | ||
107 | |||
108 | if (enable) | ||
109 | val = mask; | ||
110 | else | ||
111 | val = 0; | ||
112 | |||
113 | ret = lm3533_update(led->lm3533, LM3533_REG_PATTERN_ENABLE, val, mask); | ||
114 | if (ret) { | ||
115 | dev_err(led->cdev.dev, "failed to enable pattern %d (%d)\n", | ||
116 | pattern, enable); | ||
117 | goto out; | ||
118 | } | ||
119 | |||
120 | __change_bit(LM3533_LED_FLAG_PATTERN_ENABLE, &led->flags); | ||
121 | out: | ||
122 | mutex_unlock(&led->mutex); | ||
123 | |||
124 | return ret; | ||
125 | } | ||
126 | |||
127 | static void lm3533_led_work(struct work_struct *work) | ||
128 | { | ||
129 | struct lm3533_led *led = container_of(work, struct lm3533_led, work); | ||
130 | |||
131 | dev_dbg(led->cdev.dev, "%s - %u\n", __func__, led->new_brightness); | ||
132 | |||
133 | if (led->new_brightness == 0) | ||
134 | lm3533_led_pattern_enable(led, 0); /* disable blink */ | ||
135 | |||
136 | lm3533_ctrlbank_set_brightness(&led->cb, led->new_brightness); | ||
137 | } | ||
138 | |||
139 | static void lm3533_led_set(struct led_classdev *cdev, | ||
140 | enum led_brightness value) | ||
141 | { | ||
142 | struct lm3533_led *led = to_lm3533_led(cdev); | ||
143 | |||
144 | dev_dbg(led->cdev.dev, "%s - %d\n", __func__, value); | ||
145 | |||
146 | led->new_brightness = value; | ||
147 | schedule_work(&led->work); | ||
148 | } | ||
149 | |||
150 | static enum led_brightness lm3533_led_get(struct led_classdev *cdev) | ||
151 | { | ||
152 | struct lm3533_led *led = to_lm3533_led(cdev); | ||
153 | u8 val; | ||
154 | int ret; | ||
155 | |||
156 | ret = lm3533_ctrlbank_get_brightness(&led->cb, &val); | ||
157 | if (ret) | ||
158 | return ret; | ||
159 | |||
160 | dev_dbg(led->cdev.dev, "%s - %u\n", __func__, val); | ||
161 | |||
162 | return val; | ||
163 | } | ||
164 | |||
165 | /* Pattern generator defines (delays in us). */ | ||
166 | #define LM3533_LED_DELAY1_VMIN 0x00 | ||
167 | #define LM3533_LED_DELAY2_VMIN 0x3d | ||
168 | #define LM3533_LED_DELAY3_VMIN 0x80 | ||
169 | |||
170 | #define LM3533_LED_DELAY1_VMAX (LM3533_LED_DELAY2_VMIN - 1) | ||
171 | #define LM3533_LED_DELAY2_VMAX (LM3533_LED_DELAY3_VMIN - 1) | ||
172 | #define LM3533_LED_DELAY3_VMAX 0xff | ||
173 | |||
174 | #define LM3533_LED_DELAY1_TMIN 16384U | ||
175 | #define LM3533_LED_DELAY2_TMIN 1130496U | ||
176 | #define LM3533_LED_DELAY3_TMIN 10305536U | ||
177 | |||
178 | #define LM3533_LED_DELAY1_TMAX 999424U | ||
179 | #define LM3533_LED_DELAY2_TMAX 9781248U | ||
180 | #define LM3533_LED_DELAY3_TMAX 76890112U | ||
181 | |||
182 | /* t_step = (t_max - t_min) / (v_max - v_min) */ | ||
183 | #define LM3533_LED_DELAY1_TSTEP 16384 | ||
184 | #define LM3533_LED_DELAY2_TSTEP 131072 | ||
185 | #define LM3533_LED_DELAY3_TSTEP 524288 | ||
186 | |||
187 | /* Delay limits for hardware accelerated blinking (in ms). */ | ||
188 | #define LM3533_LED_DELAY_ON_MAX \ | ||
189 | ((LM3533_LED_DELAY2_TMAX + LM3533_LED_DELAY2_TSTEP / 2) / 1000) | ||
190 | #define LM3533_LED_DELAY_OFF_MAX \ | ||
191 | ((LM3533_LED_DELAY3_TMAX + LM3533_LED_DELAY3_TSTEP / 2) / 1000) | ||
192 | |||
193 | /* | ||
194 | * Returns linear map of *t from [t_min,t_max] to [v_min,v_max] with a step | ||
195 | * size of t_step, where | ||
196 | * | ||
197 | * t_step = (t_max - t_min) / (v_max - v_min) | ||
198 | * | ||
199 | * and updates *t to reflect the mapped value. | ||
200 | */ | ||
201 | static u8 time_to_val(unsigned *t, unsigned t_min, unsigned t_step, | ||
202 | u8 v_min, u8 v_max) | ||
203 | { | ||
204 | unsigned val; | ||
205 | |||
206 | val = (*t + t_step / 2 - t_min) / t_step + v_min; | ||
207 | |||
208 | *t = t_step * (val - v_min) + t_min; | ||
209 | |||
210 | return (u8)val; | ||
211 | } | ||
212 | |||
213 | /* | ||
214 | * Returns time code corresponding to *delay (in ms) and updates *delay to | ||
215 | * reflect actual hardware delay. | ||
216 | * | ||
217 | * Hardware supports 256 discrete delay times, divided into three groups with | ||
218 | * the following ranges and step-sizes: | ||
219 | * | ||
220 | * [ 16, 999] [0x00, 0x3e] step 16 ms | ||
221 | * [ 1130, 9781] [0x3d, 0x7f] step 131 ms | ||
222 | * [10306, 76890] [0x80, 0xff] step 524 ms | ||
223 | * | ||
224 | * Note that delay group 3 is only available for delay_off. | ||
225 | */ | ||
226 | static u8 lm3533_led_get_hw_delay(unsigned *delay) | ||
227 | { | ||
228 | unsigned t; | ||
229 | u8 val; | ||
230 | |||
231 | t = *delay * 1000; | ||
232 | |||
233 | if (t >= (LM3533_LED_DELAY2_TMAX + LM3533_LED_DELAY3_TMIN) / 2) { | ||
234 | t = clamp(t, LM3533_LED_DELAY3_TMIN, LM3533_LED_DELAY3_TMAX); | ||
235 | val = time_to_val(&t, LM3533_LED_DELAY3_TMIN, | ||
236 | LM3533_LED_DELAY3_TSTEP, | ||
237 | LM3533_LED_DELAY3_VMIN, | ||
238 | LM3533_LED_DELAY3_VMAX); | ||
239 | } else if (t >= (LM3533_LED_DELAY1_TMAX + LM3533_LED_DELAY2_TMIN) / 2) { | ||
240 | t = clamp(t, LM3533_LED_DELAY2_TMIN, LM3533_LED_DELAY2_TMAX); | ||
241 | val = time_to_val(&t, LM3533_LED_DELAY2_TMIN, | ||
242 | LM3533_LED_DELAY2_TSTEP, | ||
243 | LM3533_LED_DELAY2_VMIN, | ||
244 | LM3533_LED_DELAY2_VMAX); | ||
245 | } else { | ||
246 | t = clamp(t, LM3533_LED_DELAY1_TMIN, LM3533_LED_DELAY1_TMAX); | ||
247 | val = time_to_val(&t, LM3533_LED_DELAY1_TMIN, | ||
248 | LM3533_LED_DELAY1_TSTEP, | ||
249 | LM3533_LED_DELAY1_VMIN, | ||
250 | LM3533_LED_DELAY1_VMAX); | ||
251 | } | ||
252 | |||
253 | *delay = (t + 500) / 1000; | ||
254 | |||
255 | return val; | ||
256 | } | ||
257 | |||
258 | /* | ||
259 | * Set delay register base to *delay (in ms) and update *delay to reflect | ||
260 | * actual hardware delay used. | ||
261 | */ | ||
262 | static u8 lm3533_led_delay_set(struct lm3533_led *led, u8 base, | ||
263 | unsigned long *delay) | ||
264 | { | ||
265 | unsigned t; | ||
266 | u8 val; | ||
267 | u8 reg; | ||
268 | int ret; | ||
269 | |||
270 | t = (unsigned)*delay; | ||
271 | |||
272 | /* Delay group 3 is only available for low time (delay off). */ | ||
273 | if (base != LM3533_REG_PATTERN_LOW_TIME_BASE) | ||
274 | t = min(t, LM3533_LED_DELAY2_TMAX / 1000); | ||
275 | |||
276 | val = lm3533_led_get_hw_delay(&t); | ||
277 | |||
278 | dev_dbg(led->cdev.dev, "%s - %lu: %u (0x%02x)\n", __func__, | ||
279 | *delay, t, val); | ||
280 | reg = lm3533_led_get_pattern_reg(led, base); | ||
281 | ret = lm3533_write(led->lm3533, reg, val); | ||
282 | if (ret) | ||
283 | dev_err(led->cdev.dev, "failed to set delay (%02x)\n", reg); | ||
284 | |||
285 | *delay = t; | ||
286 | |||
287 | return ret; | ||
288 | } | ||
289 | |||
290 | static int lm3533_led_delay_on_set(struct lm3533_led *led, unsigned long *t) | ||
291 | { | ||
292 | return lm3533_led_delay_set(led, LM3533_REG_PATTERN_HIGH_TIME_BASE, t); | ||
293 | } | ||
294 | |||
295 | static int lm3533_led_delay_off_set(struct lm3533_led *led, unsigned long *t) | ||
296 | { | ||
297 | return lm3533_led_delay_set(led, LM3533_REG_PATTERN_LOW_TIME_BASE, t); | ||
298 | } | ||
299 | |||
300 | static int lm3533_led_blink_set(struct led_classdev *cdev, | ||
301 | unsigned long *delay_on, | ||
302 | unsigned long *delay_off) | ||
303 | { | ||
304 | struct lm3533_led *led = to_lm3533_led(cdev); | ||
305 | int ret; | ||
306 | |||
307 | dev_dbg(led->cdev.dev, "%s - on = %lu, off = %lu\n", __func__, | ||
308 | *delay_on, *delay_off); | ||
309 | |||
310 | if (*delay_on > LM3533_LED_DELAY_ON_MAX || | ||
311 | *delay_off > LM3533_LED_DELAY_OFF_MAX) | ||
312 | return -EINVAL; | ||
313 | |||
314 | if (*delay_on == 0 && *delay_off == 0) { | ||
315 | *delay_on = 500; | ||
316 | *delay_off = 500; | ||
317 | } | ||
318 | |||
319 | ret = lm3533_led_delay_on_set(led, delay_on); | ||
320 | if (ret) | ||
321 | return ret; | ||
322 | |||
323 | ret = lm3533_led_delay_off_set(led, delay_off); | ||
324 | if (ret) | ||
325 | return ret; | ||
326 | |||
327 | return lm3533_led_pattern_enable(led, 1); | ||
328 | } | ||
329 | |||
330 | static ssize_t show_id(struct device *dev, | ||
331 | struct device_attribute *attr, char *buf) | ||
332 | { | ||
333 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
334 | struct lm3533_led *led = to_lm3533_led(led_cdev); | ||
335 | |||
336 | return scnprintf(buf, PAGE_SIZE, "%d\n", led->id); | ||
337 | } | ||
338 | |||
339 | /* | ||
340 | * Pattern generator rise/fall times: | ||
341 | * | ||
342 | * 0 - 2048 us (default) | ||
343 | * 1 - 262 ms | ||
344 | * 2 - 524 ms | ||
345 | * 3 - 1.049 s | ||
346 | * 4 - 2.097 s | ||
347 | * 5 - 4.194 s | ||
348 | * 6 - 8.389 s | ||
349 | * 7 - 16.78 s | ||
350 | */ | ||
351 | static ssize_t show_risefalltime(struct device *dev, | ||
352 | struct device_attribute *attr, | ||
353 | char *buf, u8 base) | ||
354 | { | ||
355 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
356 | struct lm3533_led *led = to_lm3533_led(led_cdev); | ||
357 | ssize_t ret; | ||
358 | u8 reg; | ||
359 | u8 val; | ||
360 | |||
361 | reg = lm3533_led_get_pattern_reg(led, base); | ||
362 | ret = lm3533_read(led->lm3533, reg, &val); | ||
363 | if (ret) | ||
364 | return ret; | ||
365 | |||
366 | return scnprintf(buf, PAGE_SIZE, "%x\n", val); | ||
367 | } | ||
368 | |||
369 | static ssize_t show_risetime(struct device *dev, | ||
370 | struct device_attribute *attr, char *buf) | ||
371 | { | ||
372 | return show_risefalltime(dev, attr, buf, | ||
373 | LM3533_REG_PATTERN_RISETIME_BASE); | ||
374 | } | ||
375 | |||
376 | static ssize_t show_falltime(struct device *dev, | ||
377 | struct device_attribute *attr, char *buf) | ||
378 | { | ||
379 | return show_risefalltime(dev, attr, buf, | ||
380 | LM3533_REG_PATTERN_FALLTIME_BASE); | ||
381 | } | ||
382 | |||
383 | static ssize_t store_risefalltime(struct device *dev, | ||
384 | struct device_attribute *attr, | ||
385 | const char *buf, size_t len, u8 base) | ||
386 | { | ||
387 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
388 | struct lm3533_led *led = to_lm3533_led(led_cdev); | ||
389 | u8 val; | ||
390 | u8 reg; | ||
391 | int ret; | ||
392 | |||
393 | if (kstrtou8(buf, 0, &val) || val > LM3533_RISEFALLTIME_MAX) | ||
394 | return -EINVAL; | ||
395 | |||
396 | reg = lm3533_led_get_pattern_reg(led, base); | ||
397 | ret = lm3533_write(led->lm3533, reg, val); | ||
398 | if (ret) | ||
399 | return ret; | ||
400 | |||
401 | return len; | ||
402 | } | ||
403 | |||
404 | static ssize_t store_risetime(struct device *dev, | ||
405 | struct device_attribute *attr, | ||
406 | const char *buf, size_t len) | ||
407 | { | ||
408 | return store_risefalltime(dev, attr, buf, len, | ||
409 | LM3533_REG_PATTERN_RISETIME_BASE); | ||
410 | } | ||
411 | |||
412 | static ssize_t store_falltime(struct device *dev, | ||
413 | struct device_attribute *attr, | ||
414 | const char *buf, size_t len) | ||
415 | { | ||
416 | return store_risefalltime(dev, attr, buf, len, | ||
417 | LM3533_REG_PATTERN_FALLTIME_BASE); | ||
418 | } | ||
419 | |||
420 | static ssize_t show_als_channel(struct device *dev, | ||
421 | struct device_attribute *attr, char *buf) | ||
422 | { | ||
423 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
424 | struct lm3533_led *led = to_lm3533_led(led_cdev); | ||
425 | unsigned channel; | ||
426 | u8 reg; | ||
427 | u8 val; | ||
428 | int ret; | ||
429 | |||
430 | reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); | ||
431 | ret = lm3533_read(led->lm3533, reg, &val); | ||
432 | if (ret) | ||
433 | return ret; | ||
434 | |||
435 | channel = (val & LM3533_REG_CTRLBANK_BCONF_ALS_CHANNEL_MASK) + 1; | ||
436 | |||
437 | return scnprintf(buf, PAGE_SIZE, "%u\n", channel); | ||
438 | } | ||
439 | |||
440 | static ssize_t store_als_channel(struct device *dev, | ||
441 | struct device_attribute *attr, | ||
442 | const char *buf, size_t len) | ||
443 | { | ||
444 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
445 | struct lm3533_led *led = to_lm3533_led(led_cdev); | ||
446 | unsigned channel; | ||
447 | u8 reg; | ||
448 | u8 val; | ||
449 | u8 mask; | ||
450 | int ret; | ||
451 | |||
452 | if (kstrtouint(buf, 0, &channel)) | ||
453 | return -EINVAL; | ||
454 | |||
455 | if (channel < LM3533_ALS_CHANNEL_LV_MIN || | ||
456 | channel > LM3533_ALS_CHANNEL_LV_MAX) | ||
457 | return -EINVAL; | ||
458 | |||
459 | reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); | ||
460 | mask = LM3533_REG_CTRLBANK_BCONF_ALS_CHANNEL_MASK; | ||
461 | val = channel - 1; | ||
462 | |||
463 | ret = lm3533_update(led->lm3533, reg, val, mask); | ||
464 | if (ret) | ||
465 | return ret; | ||
466 | |||
467 | return len; | ||
468 | } | ||
469 | |||
470 | static ssize_t show_als_en(struct device *dev, | ||
471 | struct device_attribute *attr, char *buf) | ||
472 | { | ||
473 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
474 | struct lm3533_led *led = to_lm3533_led(led_cdev); | ||
475 | bool enable; | ||
476 | u8 reg; | ||
477 | u8 val; | ||
478 | int ret; | ||
479 | |||
480 | reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); | ||
481 | ret = lm3533_read(led->lm3533, reg, &val); | ||
482 | if (ret) | ||
483 | return ret; | ||
484 | |||
485 | enable = val & LM3533_REG_CTRLBANK_BCONF_ALS_EN_MASK; | ||
486 | |||
487 | return scnprintf(buf, PAGE_SIZE, "%d\n", enable); | ||
488 | } | ||
489 | |||
490 | static ssize_t store_als_en(struct device *dev, | ||
491 | struct device_attribute *attr, | ||
492 | const char *buf, size_t len) | ||
493 | { | ||
494 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
495 | struct lm3533_led *led = to_lm3533_led(led_cdev); | ||
496 | unsigned enable; | ||
497 | u8 reg; | ||
498 | u8 mask; | ||
499 | u8 val; | ||
500 | int ret; | ||
501 | |||
502 | if (kstrtouint(buf, 0, &enable)) | ||
503 | return -EINVAL; | ||
504 | |||
505 | reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); | ||
506 | mask = LM3533_REG_CTRLBANK_BCONF_ALS_EN_MASK; | ||
507 | |||
508 | if (enable) | ||
509 | val = mask; | ||
510 | else | ||
511 | val = 0; | ||
512 | |||
513 | ret = lm3533_update(led->lm3533, reg, val, mask); | ||
514 | if (ret) | ||
515 | return ret; | ||
516 | |||
517 | return len; | ||
518 | } | ||
519 | |||
520 | static ssize_t show_linear(struct device *dev, | ||
521 | struct device_attribute *attr, char *buf) | ||
522 | { | ||
523 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
524 | struct lm3533_led *led = to_lm3533_led(led_cdev); | ||
525 | u8 reg; | ||
526 | u8 val; | ||
527 | int linear; | ||
528 | int ret; | ||
529 | |||
530 | reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); | ||
531 | ret = lm3533_read(led->lm3533, reg, &val); | ||
532 | if (ret) | ||
533 | return ret; | ||
534 | |||
535 | if (val & LM3533_REG_CTRLBANK_BCONF_MAPPING_MASK) | ||
536 | linear = 1; | ||
537 | else | ||
538 | linear = 0; | ||
539 | |||
540 | return scnprintf(buf, PAGE_SIZE, "%x\n", linear); | ||
541 | } | ||
542 | |||
543 | static ssize_t store_linear(struct device *dev, | ||
544 | struct device_attribute *attr, | ||
545 | const char *buf, size_t len) | ||
546 | { | ||
547 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
548 | struct lm3533_led *led = to_lm3533_led(led_cdev); | ||
549 | unsigned long linear; | ||
550 | u8 reg; | ||
551 | u8 mask; | ||
552 | u8 val; | ||
553 | int ret; | ||
554 | |||
555 | if (kstrtoul(buf, 0, &linear)) | ||
556 | return -EINVAL; | ||
557 | |||
558 | reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); | ||
559 | mask = LM3533_REG_CTRLBANK_BCONF_MAPPING_MASK; | ||
560 | |||
561 | if (linear) | ||
562 | val = mask; | ||
563 | else | ||
564 | val = 0; | ||
565 | |||
566 | ret = lm3533_update(led->lm3533, reg, val, mask); | ||
567 | if (ret) | ||
568 | return ret; | ||
569 | |||
570 | return len; | ||
571 | } | ||
572 | |||
573 | static ssize_t show_pwm(struct device *dev, | ||
574 | struct device_attribute *attr, | ||
575 | char *buf) | ||
576 | { | ||
577 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
578 | struct lm3533_led *led = to_lm3533_led(led_cdev); | ||
579 | u8 val; | ||
580 | int ret; | ||
581 | |||
582 | ret = lm3533_ctrlbank_get_pwm(&led->cb, &val); | ||
583 | if (ret) | ||
584 | return ret; | ||
585 | |||
586 | return scnprintf(buf, PAGE_SIZE, "%u\n", val); | ||
587 | } | ||
588 | |||
589 | static ssize_t store_pwm(struct device *dev, | ||
590 | struct device_attribute *attr, | ||
591 | const char *buf, size_t len) | ||
592 | { | ||
593 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
594 | struct lm3533_led *led = to_lm3533_led(led_cdev); | ||
595 | u8 val; | ||
596 | int ret; | ||
597 | |||
598 | if (kstrtou8(buf, 0, &val)) | ||
599 | return -EINVAL; | ||
600 | |||
601 | ret = lm3533_ctrlbank_set_pwm(&led->cb, val); | ||
602 | if (ret) | ||
603 | return ret; | ||
604 | |||
605 | return len; | ||
606 | } | ||
607 | |||
608 | static LM3533_ATTR_RW(als_channel); | ||
609 | static LM3533_ATTR_RW(als_en); | ||
610 | static LM3533_ATTR_RW(falltime); | ||
611 | static LM3533_ATTR_RO(id); | ||
612 | static LM3533_ATTR_RW(linear); | ||
613 | static LM3533_ATTR_RW(pwm); | ||
614 | static LM3533_ATTR_RW(risetime); | ||
615 | |||
616 | static struct attribute *lm3533_led_attributes[] = { | ||
617 | &dev_attr_als_channel.attr, | ||
618 | &dev_attr_als_en.attr, | ||
619 | &dev_attr_falltime.attr, | ||
620 | &dev_attr_id.attr, | ||
621 | &dev_attr_linear.attr, | ||
622 | &dev_attr_pwm.attr, | ||
623 | &dev_attr_risetime.attr, | ||
624 | NULL, | ||
625 | }; | ||
626 | |||
627 | static umode_t lm3533_led_attr_is_visible(struct kobject *kobj, | ||
628 | struct attribute *attr, int n) | ||
629 | { | ||
630 | struct device *dev = container_of(kobj, struct device, kobj); | ||
631 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | ||
632 | struct lm3533_led *led = to_lm3533_led(led_cdev); | ||
633 | umode_t mode = attr->mode; | ||
634 | |||
635 | if (attr == &dev_attr_als_channel.attr || | ||
636 | attr == &dev_attr_als_en.attr) { | ||
637 | if (!led->lm3533->have_als) | ||
638 | mode = 0; | ||
639 | } | ||
640 | |||
641 | return mode; | ||
642 | }; | ||
643 | |||
644 | static struct attribute_group lm3533_led_attribute_group = { | ||
645 | .is_visible = lm3533_led_attr_is_visible, | ||
646 | .attrs = lm3533_led_attributes | ||
647 | }; | ||
648 | |||
649 | static int __devinit lm3533_led_setup(struct lm3533_led *led, | ||
650 | struct lm3533_led_platform_data *pdata) | ||
651 | { | ||
652 | int ret; | ||
653 | |||
654 | ret = lm3533_ctrlbank_set_max_current(&led->cb, pdata->max_current); | ||
655 | if (ret) | ||
656 | return ret; | ||
657 | |||
658 | return lm3533_ctrlbank_set_pwm(&led->cb, pdata->pwm); | ||
659 | } | ||
660 | |||
661 | static int __devinit lm3533_led_probe(struct platform_device *pdev) | ||
662 | { | ||
663 | struct lm3533 *lm3533; | ||
664 | struct lm3533_led_platform_data *pdata; | ||
665 | struct lm3533_led *led; | ||
666 | int ret; | ||
667 | |||
668 | dev_dbg(&pdev->dev, "%s\n", __func__); | ||
669 | |||
670 | lm3533 = dev_get_drvdata(pdev->dev.parent); | ||
671 | if (!lm3533) | ||
672 | return -EINVAL; | ||
673 | |||
674 | pdata = pdev->dev.platform_data; | ||
675 | if (!pdata) { | ||
676 | dev_err(&pdev->dev, "no platform data\n"); | ||
677 | return -EINVAL; | ||
678 | } | ||
679 | |||
680 | if (pdev->id < 0 || pdev->id >= LM3533_LVCTRLBANK_COUNT) { | ||
681 | dev_err(&pdev->dev, "illegal LED id %d\n", pdev->id); | ||
682 | return -EINVAL; | ||
683 | } | ||
684 | |||
685 | led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); | ||
686 | if (!led) | ||
687 | return -ENOMEM; | ||
688 | |||
689 | led->lm3533 = lm3533; | ||
690 | led->cdev.name = pdata->name; | ||
691 | led->cdev.default_trigger = pdata->default_trigger; | ||
692 | led->cdev.brightness_set = lm3533_led_set; | ||
693 | led->cdev.brightness_get = lm3533_led_get; | ||
694 | led->cdev.blink_set = lm3533_led_blink_set; | ||
695 | led->cdev.brightness = LED_OFF; | ||
696 | led->id = pdev->id; | ||
697 | |||
698 | mutex_init(&led->mutex); | ||
699 | INIT_WORK(&led->work, lm3533_led_work); | ||
700 | |||
701 | /* The class framework makes a callback to get brightness during | ||
702 | * registration so use parent device (for error reporting) until | ||
703 | * registered. | ||
704 | */ | ||
705 | led->cb.lm3533 = lm3533; | ||
706 | led->cb.id = lm3533_led_get_ctrlbank_id(led); | ||
707 | led->cb.dev = lm3533->dev; | ||
708 | |||
709 | platform_set_drvdata(pdev, led); | ||
710 | |||
711 | ret = led_classdev_register(pdev->dev.parent, &led->cdev); | ||
712 | if (ret) { | ||
713 | dev_err(&pdev->dev, "failed to register LED %d\n", pdev->id); | ||
714 | return ret; | ||
715 | } | ||
716 | |||
717 | led->cb.dev = led->cdev.dev; | ||
718 | |||
719 | ret = sysfs_create_group(&led->cdev.dev->kobj, | ||
720 | &lm3533_led_attribute_group); | ||
721 | if (ret < 0) { | ||
722 | dev_err(&pdev->dev, "failed to create sysfs attributes\n"); | ||
723 | goto err_unregister; | ||
724 | } | ||
725 | |||
726 | ret = lm3533_led_setup(led, pdata); | ||
727 | if (ret) | ||
728 | goto err_sysfs_remove; | ||
729 | |||
730 | ret = lm3533_ctrlbank_enable(&led->cb); | ||
731 | if (ret) | ||
732 | goto err_sysfs_remove; | ||
733 | |||
734 | return 0; | ||
735 | |||
736 | err_sysfs_remove: | ||
737 | sysfs_remove_group(&led->cdev.dev->kobj, &lm3533_led_attribute_group); | ||
738 | err_unregister: | ||
739 | led_classdev_unregister(&led->cdev); | ||
740 | flush_work_sync(&led->work); | ||
741 | |||
742 | return ret; | ||
743 | } | ||
744 | |||
745 | static int __devexit lm3533_led_remove(struct platform_device *pdev) | ||
746 | { | ||
747 | struct lm3533_led *led = platform_get_drvdata(pdev); | ||
748 | |||
749 | dev_dbg(&pdev->dev, "%s\n", __func__); | ||
750 | |||
751 | lm3533_ctrlbank_disable(&led->cb); | ||
752 | sysfs_remove_group(&led->cdev.dev->kobj, &lm3533_led_attribute_group); | ||
753 | led_classdev_unregister(&led->cdev); | ||
754 | flush_work_sync(&led->work); | ||
755 | |||
756 | return 0; | ||
757 | } | ||
758 | |||
759 | static void lm3533_led_shutdown(struct platform_device *pdev) | ||
760 | { | ||
761 | |||
762 | struct lm3533_led *led = platform_get_drvdata(pdev); | ||
763 | |||
764 | dev_dbg(&pdev->dev, "%s\n", __func__); | ||
765 | |||
766 | lm3533_ctrlbank_disable(&led->cb); | ||
767 | lm3533_led_set(&led->cdev, LED_OFF); /* disable blink */ | ||
768 | flush_work_sync(&led->work); | ||
769 | } | ||
770 | |||
771 | static struct platform_driver lm3533_led_driver = { | ||
772 | .driver = { | ||
773 | .name = "lm3533-leds", | ||
774 | .owner = THIS_MODULE, | ||
775 | }, | ||
776 | .probe = lm3533_led_probe, | ||
777 | .remove = __devexit_p(lm3533_led_remove), | ||
778 | .shutdown = lm3533_led_shutdown, | ||
779 | }; | ||
780 | module_platform_driver(lm3533_led_driver); | ||
781 | |||
782 | MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>"); | ||
783 | MODULE_DESCRIPTION("LM3533 LED driver"); | ||
784 | MODULE_LICENSE("GPL"); | ||
785 | MODULE_ALIAS("platform:lm3533-leds"); | ||