]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/leds/leds-lp55xx-common.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph...
[karo-tx-linux.git] / drivers / leds / leds-lp55xx-common.c
index 98407ca45e4fd94d8d147dab1bb5505d6554e371..d9eb8415742386ce4ad9eec1dade07ef809a59de 100644 (file)
@@ -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,
 };
 
@@ -361,12 +478,9 @@ void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip)
 {
        int i;
        struct lp55xx_led *each;
-       struct kobject *kobj;
 
        for (i = 0; i < chip->num_leds; i++) {
                each = led + i;
-               kobj = &led->cdev.dev->kobj;
-               sysfs_remove_group(kobj, &lp55xx_led_attr_group);
                led_classdev_unregister(&each->cdev);
                flush_work(&each->brightness_work);
        }
@@ -376,11 +490,34 @@ EXPORT_SYMBOL_GPL(lp55xx_unregister_leds);
 int lp55xx_register_sysfs(struct lp55xx_chip *chip)
 {
        struct device *dev = &chip->cl->dev;
+       struct lp55xx_device_config *cfg = chip->cfg;
+       int ret;
 
-       return sysfs_create_group(&dev->kobj, &lp55xx_engine_attr_group);
+       if (!cfg->run_engine || !cfg->firmware_cb)
+               goto dev_specific_attrs;
+
+       ret = sysfs_create_group(&dev->kobj, &lp55xx_engine_attr_group);
+       if (ret)
+               return ret;
+
+dev_specific_attrs:
+       return cfg->dev_attr_group ?
+               sysfs_create_group(&dev->kobj, cfg->dev_attr_group) : 0;
 }
 EXPORT_SYMBOL_GPL(lp55xx_register_sysfs);
 
+void lp55xx_unregister_sysfs(struct lp55xx_chip *chip)
+{
+       struct device *dev = &chip->cl->dev;
+       struct lp55xx_device_config *cfg = chip->cfg;
+
+       if (cfg->dev_attr_group)
+               sysfs_remove_group(&dev->kobj, cfg->dev_attr_group);
+
+       sysfs_remove_group(&dev->kobj, &lp55xx_engine_attr_group);
+}
+EXPORT_SYMBOL_GPL(lp55xx_unregister_sysfs);
+
 MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");
 MODULE_DESCRIPTION("LP55xx Common Driver");
 MODULE_LICENSE("GPL");