summaryrefslogtreecommitdiff
path: root/drivers/leds/flashlight.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/leds/flashlight.c')
-rw-r--r--drivers/leds/flashlight.c539
1 files changed, 539 insertions, 0 deletions
diff --git a/drivers/leds/flashlight.c b/drivers/leds/flashlight.c
new file mode 100644
index 00000000..2b47da60
--- /dev/null
+++ b/drivers/leds/flashlight.c
@@ -0,0 +1,539 @@
+/* drivers/leds/flashlight.c
+ * Flashlight Class Device Driver
+ *
+ * Copyright (C) 2013 Richtek Technology Corp.
+ * Author: Patrick Chang <patrick_chang@richtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/leds/flashlight.h>
+#include <linux/ctype.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+
+static const char const *flashlight_type_string[] = {
+ [FLASHLIGHT_TYPE_XENON] = "Xenon",
+ [FLASHLIGHT_TYPE_LED] = "LED",
+ [FLASHLIFHT_TYPE_BULB] = "Bulb",
+};
+
+static const char const *flashlight_mode_string[] = {
+ [FLASHLIGHT_MODE_OFF] = "Off",
+ [FLASHLIGHT_MODE_TORCH] = "Torch",
+ [FLASHLIGHT_MODE_FLASH] = "Flash",
+ [FLASHLIGHT_MODE_MIXED] = "Mixed",
+};
+
+static ssize_t flashlight_show_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct flashlight_device *flashlight_dev = to_flashlight_device(dev);
+ return sprintf(buf, "%s\n",
+ flashlight_dev->props.alias_name ?
+ flashlight_dev->props.alias_name : "anonymous");
+}
+
+static ssize_t flashlight_show_type(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct flashlight_device *flashlight_dev = to_flashlight_device(dev);
+ return sprintf(buf, "%s\n",
+ flashlight_type_string[flashlight_dev->props.type]);
+}
+
+static ssize_t flashlight_show_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct flashlight_device *flashlight_dev = to_flashlight_device(dev);
+ return sprintf(buf, "%s\n",
+ flashlight_mode_string[flashlight_dev->props.mode]);
+}
+
+static ssize_t flashlight_show_torch_max_brightness(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct flashlight_device *flashlight_dev = to_flashlight_device(dev);
+
+ return sprintf(buf, "%d\n",
+ flashlight_dev->props.torch_max_brightness);
+}
+
+static ssize_t flashlight_show_strobe_max_brightness(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct flashlight_device *flashlight_dev = to_flashlight_device(dev);
+
+ return sprintf(buf, "%d\n",
+ flashlight_dev->props.strobe_max_brightness);
+}
+
+static ssize_t flashlight_show_color_temperature(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct flashlight_device *flashlight_dev = to_flashlight_device(dev);
+
+ return sprintf(buf, "%d\n",
+ flashlight_dev->props.color_temperature);
+}
+
+static ssize_t flashlight_show_strobe_delay(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct flashlight_device *flashlight_dev = to_flashlight_device(dev);
+
+ return sprintf(buf, "%d\n",
+ flashlight_dev->props.strobe_delay);
+}
+
+static ssize_t flashlight_store_strobe_timeout(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc;
+ struct flashlight_device *flashlight_dev = to_flashlight_device(dev);
+ long timeout;
+
+ rc = kstrtol(buf, 0, &timeout);
+ if (rc)
+ return rc;
+ rc = flashlight_dev->ops->set_strobe_timeout(flashlight_dev, timeout);
+ if (rc == 0)
+ rc = count;
+ return rc;
+}
+
+static ssize_t flashlight_show_strobe_timeout(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct flashlight_device *flashlight_dev = to_flashlight_device(dev);
+ return sprintf(buf, "%d\n",
+ flashlight_dev->props.strobe_timeout);
+}
+
+
+static ssize_t flashlight_store_torch_brightness(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc;
+ struct flashlight_device *flashlight_dev = to_flashlight_device(dev);
+ long brightness;
+
+ rc = kstrtol(buf, 0, &brightness);
+ if (rc)
+ return rc;
+
+ rc = -ENXIO;
+
+ mutex_lock(&flashlight_dev->ops_lock);
+ if (flashlight_dev->ops &&
+ flashlight_dev->ops->set_torch_brightness) {
+ if (brightness > flashlight_dev->props.torch_max_brightness)
+ rc = -EINVAL;
+ else {
+ pr_debug("flashlight: set torch brightness to %ld\n",
+ brightness);
+ flashlight_dev->props.torch_brightness = brightness;
+ flashlight_dev->ops->set_torch_brightness(
+ flashlight_dev, brightness);
+ rc = count;
+ }
+ }
+ mutex_unlock(&flashlight_dev->ops_lock);
+
+ return rc;
+}
+
+static ssize_t flashlight_show_torch_brightness(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct flashlight_device *flashlight_dev = to_flashlight_device(dev);
+
+ return sprintf(buf, "%d\n",
+ flashlight_dev->props.torch_brightness);
+}
+
+static ssize_t flashlight_store_strobe_brightness(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc;
+ struct flashlight_device *flashlight_dev = to_flashlight_device(dev);
+ long brightness;
+
+ rc = kstrtol(buf, 0, &brightness);
+ if (rc)
+ return rc;
+
+ rc = -ENXIO;
+
+ mutex_lock(&flashlight_dev->ops_lock);
+ if (flashlight_dev->ops &&
+ flashlight_dev->ops->set_strobe_brightness) {
+ if (brightness > flashlight_dev->props.strobe_max_brightness)
+ rc = -EINVAL;
+ else {
+ pr_debug("flashlight: set strobe brightness to %ld\n",
+ brightness);
+ flashlight_dev->props.strobe_brightness = brightness;
+ flashlight_dev->ops->set_strobe_brightness(
+ flashlight_dev, brightness);
+ rc = count;
+ }
+ }
+ mutex_unlock(&flashlight_dev->ops_lock);
+ return rc;
+}
+
+static ssize_t flashlight_show_strobe_brightness(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct flashlight_device *flashlight_dev = to_flashlight_device(dev);
+
+ return sprintf(buf, "%d\n",
+ flashlight_dev->props.strobe_brightness);
+}
+
+
+
+static struct class *flashlight_class;
+
+static int flashlight_suspend(struct device *dev, pm_message_t state)
+{
+ struct flashlight_device *flashlight_dev = to_flashlight_device(dev);
+ if (flashlight_dev->ops) {
+ flashlight_dev->ops->suspend(flashlight_dev, state);
+ }
+ return 0;
+}
+
+static int flashlight_resume(struct device *dev)
+{
+ struct flashlight_device *flashlight_dev = to_flashlight_device(dev);
+ if (flashlight_dev->ops) {
+ flashlight_dev->ops->resume(flashlight_dev);
+ }
+ return 0;
+}
+
+static void flashlight_device_release(struct device *dev)
+{
+ struct flashlight_device *flashlight_dev = to_flashlight_device(dev);
+ kfree(flashlight_dev);
+}
+
+static struct device_attribute flashlight_device_attributes[] = {
+ __ATTR(name, 0444, flashlight_show_name, NULL),
+ __ATTR(type, 0444, flashlight_show_type, NULL),
+ __ATTR(mode, 0444, flashlight_show_mode, NULL),
+ __ATTR(torch_max_brightness, 0444,
+ flashlight_show_torch_max_brightness, NULL),
+ __ATTR(strobe_max_brightness, 0444,
+ flashlight_show_strobe_max_brightness, NULL),
+ __ATTR(color_temperature, 0444,
+ flashlight_show_color_temperature, NULL),
+ __ATTR(strobe_delay, 0444,
+ flashlight_show_strobe_delay, NULL),
+ __ATTR(strobe_timeout, 0644,
+ flashlight_show_strobe_timeout,
+ flashlight_store_strobe_timeout),
+ __ATTR(torch_brightness, 0644,
+ flashlight_show_torch_brightness,
+ flashlight_store_torch_brightness),
+ __ATTR(strobe_brightness, 0644,
+ flashlight_show_strobe_brightness,
+ flashlight_store_strobe_brightness),
+ __ATTR_NULL,
+};
+
+/**
+ * flashlight_device_register - create and register a new object of
+ * flashlight_device class.
+ * @name: the name of the new object(must be the same as the name of the
+ * respective framebuffer device).
+ * @parent: a pointer to the parent device
+ * @devdata: an optional pointer to be stored for private driver use. The
+ * methods may retrieve it by using bl_get_data(flashlight_dev).
+ * @ops: the flashlight operations structure.
+ *
+ * Creates and registers new flashlight device. Returns either an
+ * ERR_PTR() or a pointer to the newly allocated device.
+ */
+struct flashlight_device *flashlight_device_register(const char *name,
+ struct device *parent, void *devdata, const struct flashlight_ops *ops,
+ const struct flashlight_properties *props)
+{
+ struct flashlight_device *flashlight_dev;
+ int rc;
+ pr_debug("flashlight_device_register: name=%s\n", name);
+ flashlight_dev = kzalloc(sizeof(*flashlight_dev), GFP_KERNEL);
+ if (!flashlight_dev)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_init(&flashlight_dev->ops_lock);
+ flashlight_dev->dev.class = flashlight_class;
+ flashlight_dev->dev.parent = parent;
+ flashlight_dev->dev.release = flashlight_device_release;
+ dev_set_name(&flashlight_dev->dev, name);
+ dev_set_drvdata(&flashlight_dev->dev, devdata);
+ /* Copy properties */
+ if (props) {
+ memcpy(&flashlight_dev->props, props,
+ sizeof(struct flashlight_properties));
+ }
+ rc = device_register(&flashlight_dev->dev);
+ if (rc) {
+ kfree(flashlight_dev);
+ return ERR_PTR(rc);
+ }
+ flashlight_dev->ops = ops;
+ return flashlight_dev;
+}
+EXPORT_SYMBOL(flashlight_device_register);
+
+/**
+ * flashlight_device_unregister - unregisters a flashlight device object.
+ * @flashlight_dev: the flashlight device object to be unregistered and freed.
+ *
+ * Unregisters a previously registered via flashlight_device_register object.
+ */
+void flashlight_device_unregister(struct flashlight_device *flashlight_dev)
+{
+ if (!flashlight_dev)
+ return;
+
+ mutex_lock(&flashlight_dev->ops_lock);
+ flashlight_dev->ops = NULL;
+ mutex_unlock(&flashlight_dev->ops_lock);
+ device_unregister(&flashlight_dev->dev);
+}
+EXPORT_SYMBOL(flashlight_device_unregister);
+
+int flashlight_list_color_temperature(
+ struct flashlight_device *flashlight_dev,
+ int selector)
+{
+ if (flashlight_dev->ops &&
+ flashlight_dev->ops->list_color_temperature) {
+ return flashlight_dev->ops->list_color_temperature(
+ flashlight_dev,
+ selector);
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL(flashlight_list_color_temperature);
+
+int flashlight_set_color_temperature(
+ struct flashlight_device *flashlight_dev,
+ int minK, int maxK)
+{
+ int selector = 0;
+ int rc;
+
+ if ((flashlight_dev->ops == NULL) ||
+ (flashlight_dev->ops->set_color_temperature == NULL))
+ return -EINVAL;
+ for (selector = 0; ; selector++) {
+ rc = flashlight_list_color_temperature(flashlight_dev, selector);
+ if (rc < 0)
+ return rc;
+ if (rc >= minK && rc <= maxK) {
+ mutex_lock(&flashlight_dev->ops_lock);
+ rc = flashlight_dev->ops->set_color_temperature(
+ flashlight_dev, rc);
+ mutex_unlock(&flashlight_dev->ops_lock);
+ if (rc == 0)
+ flashlight_dev->props.color_temperature = rc;
+ return rc;
+ }
+
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL(flashlight_set_color_temperature);
+
+int flashlight_set_torch_brightness(
+ struct flashlight_device *flashlight_dev,
+ int brightness_level)
+{
+ int rc;
+ if ((flashlight_dev->ops == NULL) ||
+ (flashlight_dev->ops->set_torch_brightness == NULL))
+ return -EINVAL;
+ if (brightness_level > flashlight_dev->props.torch_max_brightness)
+ return -EINVAL;
+ mutex_lock(&flashlight_dev->ops_lock);
+ rc = flashlight_dev->ops->set_torch_brightness(flashlight_dev,
+ brightness_level);
+ mutex_unlock(&flashlight_dev->ops_lock);
+ if (rc < 0)
+ return rc;
+ flashlight_dev->props.torch_brightness = brightness_level;
+ return rc;
+
+}
+EXPORT_SYMBOL(flashlight_set_torch_brightness);
+
+int flashlight_set_strobe_brightness(
+ struct flashlight_device *flashlight_dev,
+ int brightness_level)
+{
+ int rc;
+ if ((flashlight_dev->ops == NULL) ||
+ (flashlight_dev->ops->set_strobe_brightness == NULL))
+ return -EINVAL;
+ if (brightness_level > flashlight_dev->props.strobe_max_brightness)
+ return -EINVAL;
+ mutex_lock(&flashlight_dev->ops_lock);
+ rc = flashlight_dev->ops->set_strobe_brightness(flashlight_dev,
+ brightness_level);
+ mutex_unlock(&flashlight_dev->ops_lock);
+ if (rc < 0)
+ return rc;
+ flashlight_dev->props.strobe_brightness = brightness_level;
+ return rc;
+}
+EXPORT_SYMBOL(flashlight_set_strobe_brightness);
+
+int flashlight_list_strobe_timeout(
+ struct flashlight_device *flashlight_dev,
+ int selector)
+{
+ if (flashlight_dev->ops &&
+ flashlight_dev->ops->list_strobe_timeout) {
+ return flashlight_dev->ops->list_strobe_timeout(flashlight_dev,
+ selector);
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL(flashlight_list_strobe_timeout);
+
+int flashlight_set_strobe_timeout(
+ struct flashlight_device *flashlight_dev,
+ int min_ms, int max_ms)
+{
+ int selector = 0;
+ int rc = -EINVAL;
+ int timeout;
+
+ if ((flashlight_dev->ops == NULL) ||
+ (flashlight_dev->ops->set_strobe_timeout == NULL))
+ return -EINVAL;
+ for (selector = 0; ; selector++) {
+ timeout = flashlight_list_strobe_timeout(flashlight_dev, selector);
+ if (timeout < 0)
+ return timeout;
+ if (timeout >= min_ms && timeout <= max_ms) {
+ mutex_lock(&flashlight_dev->ops_lock);
+ rc = flashlight_dev->ops->set_strobe_timeout(
+ flashlight_dev, timeout);
+ mutex_unlock(&flashlight_dev->ops_lock);
+ if (rc == 0)
+ flashlight_dev->props.strobe_timeout = timeout;
+ return rc;
+ }
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL(flashlight_set_strobe_timeout);
+
+int flashlight_set_mode(struct flashlight_device *flashlight_dev,
+ int mode)
+{
+ int rc;
+ if (mode >= FLASHLIGHT_MODE_MAX || mode < 0)
+ return -EINVAL;
+ if ((flashlight_dev->ops == NULL) ||
+ (flashlight_dev->ops->set_mode == NULL)) {
+ flashlight_dev->props.mode = mode;
+ return 0;
+ }
+ mutex_lock(&flashlight_dev->ops_lock);
+ rc = flashlight_dev->ops->set_mode(flashlight_dev,
+ mode);
+ mutex_unlock(&flashlight_dev->ops_lock);
+ if (rc < 0)
+ return rc;
+ flashlight_dev->props.mode = mode;
+ return rc;
+}
+EXPORT_SYMBOL(flashlight_set_mode);
+
+int flashlight_strobe(struct flashlight_device *flashlight_dev)
+{
+ if (flashlight_dev->props.mode == FLASHLIGHT_MODE_FLASH
+ || flashlight_dev->props.mode == FLASHLIGHT_MODE_MIXED) {
+ if (flashlight_dev->ops == NULL ||
+ flashlight_dev->ops->strobe == NULL)
+ return -EINVAL;
+ return flashlight_dev->ops->strobe(flashlight_dev);
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL(flashlight_strobe);
+
+static int flashlight_match_device_by_name(struct device *dev, void *data)
+{
+ const char *name = data;
+ return strcmp(dev_name(dev), name) == 0;
+}
+
+struct flashlight_device *find_flashlight_by_name(char *name)
+{
+ struct device *dev;
+ if (!name)
+ return (struct flashlight_device *)NULL;
+ dev = class_find_device(flashlight_class, NULL, name,
+ flashlight_match_device_by_name);
+
+ return dev ? to_flashlight_device(dev) : NULL;
+
+}
+EXPORT_SYMBOL(find_flashlight_by_name);
+
+int flashlight_strobe_charge(struct flashlight_device *flashlight_dev,
+ flashlight_charge_event_cb cb, void *data, int start)
+{
+
+ if (flashlight_dev->ops->strobe_charge)
+ return flashlight_dev->ops->strobe_charge(flashlight_dev,
+ cb, data, start);
+ if (flashlight_dev->props.type == FLASHLIGHT_TYPE_LED) {
+ if (cb)
+ cb(data, 0);
+ return 0;
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL(flashlight_strobe_charge);
+
+static void __exit flashlight_class_exit(void)
+{
+ class_destroy(flashlight_class);
+}
+
+static int __init flashlight_class_init(void)
+{
+ flashlight_class = class_create(THIS_MODULE, "flashlight");
+ if (IS_ERR(flashlight_class)) {
+ printk(KERN_WARNING "Unable to create flashlight class; errno = %ld\n",
+ PTR_ERR(flashlight_class));
+ return PTR_ERR(flashlight_class);
+ }
+ flashlight_class->dev_attrs = flashlight_device_attributes;
+ flashlight_class->suspend = flashlight_suspend;
+ flashlight_class->resume = flashlight_resume;
+ return 0;
+}
+
+subsys_initcall(flashlight_class_init);
+module_exit(flashlight_class_exit);
+
+MODULE_DESCRIPTION("Flashlight Class Device");
+MODULE_AUTHOR("Patrick Chang <patrick_chang@richtek.com>");
+MODULE_VERSION("1.0.0_G");
+MODULE_LICENSE("GPL");