aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/leds
diff options
context:
space:
mode:
authorMilo(Woogyom) Kim <milo.kim@ti.com>2013-02-05 05:17:20 -0500
committerBryan Wu <cooloney@gmail.com>2013-02-06 18:59:28 -0500
commit10c06d178df11b0b2b746321a80ea14241997127 (patch)
tree8e71a6746099a416d3aa4b2f2e8b9053a5c2c66c /drivers/leds
parentb3b6f8119d752c969c6394314dc7ab80e6611111 (diff)
leds-lp55xx: support firmware interface
This patch provides additional device attributes which enable loading the firmware. ('select_engine' and 'run_engine') To run a LED pattern, two parts of driver should be enabled. Common features : lp55xx-common =============================== Firmware interface for loading LED patterns Chip specific features : leds-lp5521, leds-lp5523 ================================================= Register addresses for loading firmware data Register addresses for running selected engine Pattern programming sequence ============================ LP55xx chips have three program engines. To load and run a LED pattern, the programming sequence is as follows. (1) Select an engine number (1/2/3) (2) Set engine mode to load (3) Write pattern data into selected area (4) Set engine mode to run This sequence is almost same as the firmware interface. (1) Select an engine number : 'select_engine' dev attribute (2) Mode change to load : 'loading' of firmware class (3) Write pattern data into selected area : 'data' of firmware class (4) Mode change to run : 'run_engine' dev attribute (1) and (4) are device specific features which provide callback functions (2) and (3) are common features. For example, echo 1 or 2 or 3 > /sys/bus/i2c/devices/xxxx/select_engine echo 1 > /sys/class/firmware/lp5521/loading echo "4000600040FF6000" > /sys/class/firmware/lp5521/data echo 0 > /sys/class/firmware/lp5521/loading echo 1 > /sys/bus/i2c/devices/xxxx/run_engine As soon as 'loading' is set to 0, registered callback is called. Inside the callback, the selected engine is loaded and memory is updated. To run programmed pattern, 'run_engine' attribute should be enabled. Device specific data structure ============================== o Firmware callback load selected engine and update program memory o Run engine change the engine mode o 'engine_idx' and firmware data, 'fw' Those are used in the driver internally with callback functions Signed-off-by: Milo(Woogyom) Kim <milo.kim@ti.com> Signed-off-by: Bryan Wu <cooloney@gmail.com>
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/Kconfig1
-rw-r--r--drivers/leds/leds-lp55xx-common.c117
-rw-r--r--drivers/leds/leds-lp55xx-common.h19
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
196config LEDS_LP55XX_COMMON 196config 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
201static 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
220out:
221 /* firmware should be released for other channel use */
222 release_firmware(chip->fw);
223}
224
225static 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
234static 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
244static 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
280static 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
286static 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
311static DEVICE_ATTR(select_engine, S_IRUGO | S_IWUSR,
312 lp55xx_show_engine_select, lp55xx_store_engine_select);
313static DEVICE_ATTR(run_engine, S_IWUSR, NULL, lp55xx_store_engine_run);
314
200static struct attribute *lp55xx_engine_attributes[] = { 315static 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
18enum lp55xx_engine_index {
19 LP55XX_ENGINE_INVALID,
20 LP55XX_ENGINE_1,
21 LP55XX_ENGINE_2,
22 LP55XX_ENGINE_3,
23};
24
18struct lp55xx_led; 25struct lp55xx_led;
19struct lp55xx_chip; 26struct 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 */
40struct lp55xx_device_config { 49struct 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 */
63struct lp55xx_chip { 80struct 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/*