diff options
-rw-r--r-- | drivers/leds/Kconfig | 1 | ||||
-rw-r--r-- | drivers/leds/leds-lp55xx-common.c | 117 | ||||
-rw-r--r-- | drivers/leds/leds-lp55xx-common.h | 19 |
3 files changed, 137 insertions, 0 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 3d7822b3498f..fc680a9ed841 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig | |||
@@ -196,6 +196,7 @@ config LEDS_LP3944 | |||
196 | config LEDS_LP55XX_COMMON | 196 | config LEDS_LP55XX_COMMON |
197 | tristate "Common Driver for TI/National LP5521 and LP5523/55231" | 197 | tristate "Common Driver for TI/National LP5521 and LP5523/55231" |
198 | depends on LEDS_LP5521 || LEDS_LP5523 | 198 | depends on LEDS_LP5521 || LEDS_LP5523 |
199 | select FW_LOADER | ||
199 | help | 200 | help |
200 | This option supports common operations for LP5521 and LP5523/55231 | 201 | This option supports common operations for LP5521 and LP5523/55231 |
201 | devices. | 202 | devices. |
diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 98407ca45e4f..578902ab604f 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c | |||
@@ -13,6 +13,7 @@ | |||
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/delay.h> | 15 | #include <linux/delay.h> |
16 | #include <linux/firmware.h> | ||
16 | #include <linux/i2c.h> | 17 | #include <linux/i2c.h> |
17 | #include <linux/leds.h> | 18 | #include <linux/leds.h> |
18 | #include <linux/module.h> | 19 | #include <linux/module.h> |
@@ -197,7 +198,123 @@ static int lp55xx_init_led(struct lp55xx_led *led, | |||
197 | return 0; | 198 | return 0; |
198 | } | 199 | } |
199 | 200 | ||
201 | static void lp55xx_firmware_loaded(const struct firmware *fw, void *context) | ||
202 | { | ||
203 | struct lp55xx_chip *chip = context; | ||
204 | struct device *dev = &chip->cl->dev; | ||
205 | |||
206 | if (!fw) { | ||
207 | dev_err(dev, "firmware request failed\n"); | ||
208 | goto out; | ||
209 | } | ||
210 | |||
211 | /* handling firmware data is chip dependent */ | ||
212 | mutex_lock(&chip->lock); | ||
213 | |||
214 | chip->fw = fw; | ||
215 | if (chip->cfg->firmware_cb) | ||
216 | chip->cfg->firmware_cb(chip); | ||
217 | |||
218 | mutex_unlock(&chip->lock); | ||
219 | |||
220 | out: | ||
221 | /* firmware should be released for other channel use */ | ||
222 | release_firmware(chip->fw); | ||
223 | } | ||
224 | |||
225 | static int lp55xx_request_firmware(struct lp55xx_chip *chip) | ||
226 | { | ||
227 | const char *name = chip->cl->name; | ||
228 | struct device *dev = &chip->cl->dev; | ||
229 | |||
230 | return request_firmware_nowait(THIS_MODULE, true, name, dev, | ||
231 | GFP_KERNEL, chip, lp55xx_firmware_loaded); | ||
232 | } | ||
233 | |||
234 | static ssize_t lp55xx_show_engine_select(struct device *dev, | ||
235 | struct device_attribute *attr, | ||
236 | char *buf) | ||
237 | { | ||
238 | struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); | ||
239 | struct lp55xx_chip *chip = led->chip; | ||
240 | |||
241 | return sprintf(buf, "%d\n", chip->engine_idx); | ||
242 | } | ||
243 | |||
244 | static ssize_t lp55xx_store_engine_select(struct device *dev, | ||
245 | struct device_attribute *attr, | ||
246 | const char *buf, size_t len) | ||
247 | { | ||
248 | struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); | ||
249 | struct lp55xx_chip *chip = led->chip; | ||
250 | unsigned long val; | ||
251 | int ret; | ||
252 | |||
253 | if (kstrtoul(buf, 0, &val)) | ||
254 | return -EINVAL; | ||
255 | |||
256 | /* select the engine to be run */ | ||
257 | |||
258 | switch (val) { | ||
259 | case LP55XX_ENGINE_1: | ||
260 | case LP55XX_ENGINE_2: | ||
261 | case LP55XX_ENGINE_3: | ||
262 | mutex_lock(&chip->lock); | ||
263 | chip->engine_idx = val; | ||
264 | ret = lp55xx_request_firmware(chip); | ||
265 | mutex_unlock(&chip->lock); | ||
266 | break; | ||
267 | default: | ||
268 | dev_err(dev, "%lu: invalid engine index. (1, 2, 3)\n", val); | ||
269 | return -EINVAL; | ||
270 | } | ||
271 | |||
272 | if (ret) { | ||
273 | dev_err(dev, "request firmware err: %d\n", ret); | ||
274 | return ret; | ||
275 | } | ||
276 | |||
277 | return len; | ||
278 | } | ||
279 | |||
280 | static inline void lp55xx_run_engine(struct lp55xx_chip *chip, bool start) | ||
281 | { | ||
282 | if (chip->cfg->run_engine) | ||
283 | chip->cfg->run_engine(chip, start); | ||
284 | } | ||
285 | |||
286 | static ssize_t lp55xx_store_engine_run(struct device *dev, | ||
287 | struct device_attribute *attr, | ||
288 | const char *buf, size_t len) | ||
289 | { | ||
290 | struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); | ||
291 | struct lp55xx_chip *chip = led->chip; | ||
292 | unsigned long val; | ||
293 | |||
294 | if (kstrtoul(buf, 0, &val)) | ||
295 | return -EINVAL; | ||
296 | |||
297 | /* run or stop the selected engine */ | ||
298 | |||
299 | if (val <= 0) { | ||
300 | lp55xx_run_engine(chip, false); | ||
301 | return len; | ||
302 | } | ||
303 | |||
304 | mutex_lock(&chip->lock); | ||
305 | lp55xx_run_engine(chip, true); | ||
306 | mutex_unlock(&chip->lock); | ||
307 | |||
308 | return len; | ||
309 | } | ||
310 | |||
311 | static DEVICE_ATTR(select_engine, S_IRUGO | S_IWUSR, | ||
312 | lp55xx_show_engine_select, lp55xx_store_engine_select); | ||
313 | static DEVICE_ATTR(run_engine, S_IWUSR, NULL, lp55xx_store_engine_run); | ||
314 | |||
200 | static struct attribute *lp55xx_engine_attributes[] = { | 315 | static struct attribute *lp55xx_engine_attributes[] = { |
316 | &dev_attr_select_engine.attr, | ||
317 | &dev_attr_run_engine.attr, | ||
201 | NULL, | 318 | NULL, |
202 | }; | 319 | }; |
203 | 320 | ||
diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index d0be837643f0..8473abf9830c 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h | |||
@@ -15,6 +15,13 @@ | |||
15 | #ifndef _LEDS_LP55XX_COMMON_H | 15 | #ifndef _LEDS_LP55XX_COMMON_H |
16 | #define _LEDS_LP55XX_COMMON_H | 16 | #define _LEDS_LP55XX_COMMON_H |
17 | 17 | ||
18 | enum lp55xx_engine_index { | ||
19 | LP55XX_ENGINE_INVALID, | ||
20 | LP55XX_ENGINE_1, | ||
21 | LP55XX_ENGINE_2, | ||
22 | LP55XX_ENGINE_3, | ||
23 | }; | ||
24 | |||
18 | struct lp55xx_led; | 25 | struct lp55xx_led; |
19 | struct lp55xx_chip; | 26 | struct lp55xx_chip; |
20 | 27 | ||
@@ -36,6 +43,8 @@ struct lp55xx_reg { | |||
36 | * @post_init_device : Chip specific initialization code | 43 | * @post_init_device : Chip specific initialization code |
37 | * @brightness_work_fn : Brightness work function | 44 | * @brightness_work_fn : Brightness work function |
38 | * @set_led_current : LED current set function | 45 | * @set_led_current : LED current set function |
46 | * @firmware_cb : Call function when the firmware is loaded | ||
47 | * @run_engine : Run internal engine for pattern | ||
39 | */ | 48 | */ |
40 | struct lp55xx_device_config { | 49 | struct lp55xx_device_config { |
41 | const struct lp55xx_reg reset; | 50 | const struct lp55xx_reg reset; |
@@ -50,6 +59,12 @@ struct lp55xx_device_config { | |||
50 | 59 | ||
51 | /* current setting function */ | 60 | /* current setting function */ |
52 | void (*set_led_current) (struct lp55xx_led *led, u8 led_current); | 61 | void (*set_led_current) (struct lp55xx_led *led, u8 led_current); |
62 | |||
63 | /* access program memory when the firmware is loaded */ | ||
64 | void (*firmware_cb)(struct lp55xx_chip *chip); | ||
65 | |||
66 | /* used for running firmware LED patterns */ | ||
67 | void (*run_engine) (struct lp55xx_chip *chip, bool start); | ||
53 | }; | 68 | }; |
54 | 69 | ||
55 | /* | 70 | /* |
@@ -59,6 +74,8 @@ struct lp55xx_device_config { | |||
59 | * @lock : Lock for user-space interface | 74 | * @lock : Lock for user-space interface |
60 | * @num_leds : Number of registered LEDs | 75 | * @num_leds : Number of registered LEDs |
61 | * @cfg : Device specific configuration data | 76 | * @cfg : Device specific configuration data |
77 | * @engine_idx : Selected engine number | ||
78 | * @fw : Firmware data for running a LED pattern | ||
62 | */ | 79 | */ |
63 | struct lp55xx_chip { | 80 | struct lp55xx_chip { |
64 | struct i2c_client *cl; | 81 | struct i2c_client *cl; |
@@ -66,6 +83,8 @@ struct lp55xx_chip { | |||
66 | struct mutex lock; /* lock for user-space interface */ | 83 | struct mutex lock; /* lock for user-space interface */ |
67 | int num_leds; | 84 | int num_leds; |
68 | struct lp55xx_device_config *cfg; | 85 | struct lp55xx_device_config *cfg; |
86 | enum lp55xx_engine_index engine_idx; | ||
87 | const struct firmware *fw; | ||
69 | }; | 88 | }; |
70 | 89 | ||
71 | /* | 90 | /* |