summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
config LEDS_LP55XX_COMMON
tristate "Common Driver for TI/National LP5521 and LP5523/55231"
depends on LEDS_LP5521 || LEDS_LP5523
+ select FW_LOADER
help
This option supports common operations for LP5521 and LP5523/55231
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 @@
*/
#include <linux/delay.h>
+#include <linux/firmware.h>
#include <linux/i2c.h>
#include <linux/leds.h>
#include <linux/module.h>
@@ -197,7 +198,123 @@ static int lp55xx_init_led(struct lp55xx_led *led,
return 0;
}
+static void lp55xx_firmware_loaded(const struct firmware *fw, void *context)
+{
+ struct lp55xx_chip *chip = context;
+ struct device *dev = &chip->cl->dev;
+
+ if (!fw) {
+ dev_err(dev, "firmware request failed\n");
+ goto out;
+ }
+
+ /* handling firmware data is chip dependent */
+ mutex_lock(&chip->lock);
+
+ chip->fw = fw;
+ if (chip->cfg->firmware_cb)
+ chip->cfg->firmware_cb(chip);
+
+ mutex_unlock(&chip->lock);
+
+out:
+ /* firmware should be released for other channel use */
+ release_firmware(chip->fw);
+}
+
+static int lp55xx_request_firmware(struct lp55xx_chip *chip)
+{
+ const char *name = chip->cl->name;
+ struct device *dev = &chip->cl->dev;
+
+ return request_firmware_nowait(THIS_MODULE, true, name, dev,
+ GFP_KERNEL, chip, lp55xx_firmware_loaded);
+}
+
+static ssize_t lp55xx_show_engine_select(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+
+ return sprintf(buf, "%d\n", chip->engine_idx);
+}
+
+static ssize_t lp55xx_store_engine_select(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+ unsigned long val;
+ int ret;
+
+ if (kstrtoul(buf, 0, &val))
+ return -EINVAL;
+
+ /* select the engine to be run */
+
+ switch (val) {
+ case LP55XX_ENGINE_1:
+ case LP55XX_ENGINE_2:
+ case LP55XX_ENGINE_3:
+ mutex_lock(&chip->lock);
+ chip->engine_idx = val;
+ ret = lp55xx_request_firmware(chip);
+ mutex_unlock(&chip->lock);
+ break;
+ default:
+ dev_err(dev, "%lu: invalid engine index. (1, 2, 3)\n", val);
+ return -EINVAL;
+ }
+
+ if (ret) {
+ dev_err(dev, "request firmware err: %d\n", ret);
+ return ret;
+ }
+
+ return len;
+}
+
+static inline void lp55xx_run_engine(struct lp55xx_chip *chip, bool start)
+{
+ if (chip->cfg->run_engine)
+ chip->cfg->run_engine(chip, start);
+}
+
+static ssize_t lp55xx_store_engine_run(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
+ struct lp55xx_chip *chip = led->chip;
+ unsigned long val;
+
+ if (kstrtoul(buf, 0, &val))
+ return -EINVAL;
+
+ /* run or stop the selected engine */
+
+ if (val <= 0) {
+ lp55xx_run_engine(chip, false);
+ return len;
+ }
+
+ mutex_lock(&chip->lock);
+ lp55xx_run_engine(chip, true);
+ mutex_unlock(&chip->lock);
+
+ return len;
+}
+
+static DEVICE_ATTR(select_engine, S_IRUGO | S_IWUSR,
+ lp55xx_show_engine_select, lp55xx_store_engine_select);
+static DEVICE_ATTR(run_engine, S_IWUSR, NULL, lp55xx_store_engine_run);
+
static struct attribute *lp55xx_engine_attributes[] = {
+ &dev_attr_select_engine.attr,
+ &dev_attr_run_engine.attr,
NULL,
};
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 @@
#ifndef _LEDS_LP55XX_COMMON_H
#define _LEDS_LP55XX_COMMON_H
+enum lp55xx_engine_index {
+ LP55XX_ENGINE_INVALID,
+ LP55XX_ENGINE_1,
+ LP55XX_ENGINE_2,
+ LP55XX_ENGINE_3,
+};
+
struct lp55xx_led;
struct lp55xx_chip;
@@ -36,6 +43,8 @@ struct lp55xx_reg {
* @post_init_device : Chip specific initialization code
* @brightness_work_fn : Brightness work function
* @set_led_current : LED current set function
+ * @firmware_cb : Call function when the firmware is loaded
+ * @run_engine : Run internal engine for pattern
*/
struct lp55xx_device_config {
const struct lp55xx_reg reset;
@@ -50,6 +59,12 @@ struct lp55xx_device_config {
/* current setting function */
void (*set_led_current) (struct lp55xx_led *led, u8 led_current);
+
+ /* access program memory when the firmware is loaded */
+ void (*firmware_cb)(struct lp55xx_chip *chip);
+
+ /* used for running firmware LED patterns */
+ void (*run_engine) (struct lp55xx_chip *chip, bool start);
};
/*
@@ -59,6 +74,8 @@ struct lp55xx_device_config {
* @lock : Lock for user-space interface
* @num_leds : Number of registered LEDs
* @cfg : Device specific configuration data
+ * @engine_idx : Selected engine number
+ * @fw : Firmware data for running a LED pattern
*/
struct lp55xx_chip {
struct i2c_client *cl;
@@ -66,6 +83,8 @@ struct lp55xx_chip {
struct mutex lock; /* lock for user-space interface */
int num_leds;
struct lp55xx_device_config *cfg;
+ enum lp55xx_engine_index engine_idx;
+ const struct firmware *fw;
};
/*