diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-13 19:54:44 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-13 19:54:44 +0100 |
commit | db3ecdee1cf0538f11832f7ef66945c4dd903918 (patch) | |
tree | b3e2b3f7ccc9a1b2ea4aa05ce5bb3f8debbe119e | |
parent | Merge tag 'modules-next-for-linus' of git://git.kernel.org/pub/scm/linux/kern... (diff) | |
parent | leds: leds-gpio: Pass on error codes unmodified (diff) | |
download | linux-db3ecdee1cf0538f11832f7ef66945c4dd903918.tar.xz linux-db3ecdee1cf0538f11832f7ef66945c4dd903918.zip |
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds
Pull LED subsystem update from Bryan Wu:
"The big change of LED subsystem is introducing a new LED class for
Flash type LEDs which will be used for V4L2 subsystem.
Also we got some cleanup and fixes"
* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds:
leds: leds-gpio: Pass on error codes unmodified
DT: leds: Add led-sources property
leds: Add LED Flash class extension to the LED subsystem
leds: leds-mc13783: Use of_get_child_by_name() instead of refcount hack
leds: Use setup_timer
leds: Don't allow brightness values greater than max_brightness
DT: leds: Add flash LED devices related properties
-rw-r--r-- | Documentation/devicetree/bindings/leds/common.txt | 30 | ||||
-rw-r--r-- | drivers/leds/Kconfig | 10 | ||||
-rw-r--r-- | drivers/leds/Makefile | 1 | ||||
-rw-r--r-- | drivers/leds/led-class-flash.c | 486 | ||||
-rw-r--r-- | drivers/leds/led-class.c | 9 | ||||
-rw-r--r-- | drivers/leds/leds-gpio.c | 3 | ||||
-rw-r--r-- | drivers/leds/leds-mc13783.c | 4 | ||||
-rw-r--r-- | drivers/leds/leds.h | 3 | ||||
-rw-r--r-- | include/linux/led-class-flash.h | 207 | ||||
-rw-r--r-- | include/linux/leds.h | 3 |
10 files changed, 748 insertions, 8 deletions
diff --git a/Documentation/devicetree/bindings/leds/common.txt b/Documentation/devicetree/bindings/leds/common.txt index 2d88816dd550..34811c57db69 100644 --- a/Documentation/devicetree/bindings/leds/common.txt +++ b/Documentation/devicetree/bindings/leds/common.txt @@ -1,6 +1,19 @@ Common leds properties. +LED and flash LED devices provide the same basic functionality as current +regulators, but extended with LED and flash LED specific features like +blinking patterns, flash timeout, flash faults and external flash strobe mode. + +Many LED devices expose more than one current output that can be connected +to one or more discrete LED component. Since the arrangement of connections +can influence the way of the LED device initialization, the LED components +have to be tightly coupled with the LED device binding. They are represented +by child nodes of the parent LED device binding. + Optional properties for child nodes: +- led-sources : List of device current outputs the LED is connected to. The + outputs are identified by the numbers that must be defined + in the LED device binding documentation. - label : The label for this LED. If omitted, the label is taken from the node name (excluding the unit address). @@ -14,6 +27,15 @@ Optional properties for child nodes: "ide-disk" - LED indicates disk activity "timer" - LED flashes at a fixed, configurable rate +- max-microamp : maximum intensity in microamperes of the LED + (torch LED for flash devices) +- flash-max-microamp : maximum intensity in microamperes of the + flash LED; it is mandatory if the LED should + support the flash mode +- flash-timeout-us : timeout in microseconds after which the flash + LED is turned off + + Examples: system-status { @@ -21,3 +43,11 @@ system-status { linux,default-trigger = "heartbeat"; ... }; + +camera-flash { + label = "Flash"; + led-sources = <0>, <1>; + max-microamp = <50000>; + flash-max-microamp = <320000>; + flash-timeout-us = <500000>; +}; diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index a6c3d2f153f3..25b320d64e26 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -22,6 +22,16 @@ config LEDS_CLASS This option enables the led sysfs class in /sys/class/leds. You'll need this to do anything useful with LEDs. If unsure, say N. +config LEDS_CLASS_FLASH + tristate "LED Flash Class Support" + depends on LEDS_CLASS + help + This option enables the flash led sysfs class in /sys/class/leds. + It wrapps LED Class and adds flash LEDs specific sysfs attributes + and kernel internal API to it. You'll need this to provide support + for the flash related features of a LED device. It can be built + as a module. + comment "LED drivers" config LEDS_88PM860X diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 1c65a191d907..cbba921b6f1c 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -2,6 +2,7 @@ # LED Core obj-$(CONFIG_NEW_LEDS) += led-core.o obj-$(CONFIG_LEDS_CLASS) += led-class.o +obj-$(CONFIG_LEDS_CLASS_FLASH) += led-class-flash.o obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o # LED Platform Drivers diff --git a/drivers/leds/led-class-flash.c b/drivers/leds/led-class-flash.c new file mode 100644 index 000000000000..4a19fd44f93f --- /dev/null +++ b/drivers/leds/led-class-flash.c @@ -0,0 +1,486 @@ +/* + * LED Flash class interface + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * Author: Jacek Anaszewski <j.anaszewski@samsung.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. + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/led-class-flash.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/slab.h> +#include "leds.h" + +#define has_flash_op(fled_cdev, op) \ + (fled_cdev && fled_cdev->ops->op) + +#define call_flash_op(fled_cdev, op, args...) \ + ((has_flash_op(fled_cdev, op)) ? \ + (fled_cdev->ops->op(fled_cdev, args)) : \ + -EINVAL) + +static const char * const led_flash_fault_names[] = { + "led-over-voltage", + "flash-timeout-exceeded", + "controller-over-temperature", + "controller-short-circuit", + "led-power-supply-over-current", + "indicator-led-fault", + "led-under-voltage", + "controller-under-voltage", + "led-over-temperature", +}; + +static ssize_t flash_brightness_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); + unsigned long state; + ssize_t ret; + + mutex_lock(&led_cdev->led_access); + + if (led_sysfs_is_disabled(led_cdev)) { + ret = -EBUSY; + goto unlock; + } + + ret = kstrtoul(buf, 10, &state); + if (ret) + goto unlock; + + ret = led_set_flash_brightness(fled_cdev, state); + if (ret < 0) + goto unlock; + + ret = size; +unlock: + mutex_unlock(&led_cdev->led_access); + return ret; +} + +static ssize_t flash_brightness_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); + + /* no lock needed for this */ + led_update_flash_brightness(fled_cdev); + + return sprintf(buf, "%u\n", fled_cdev->brightness.val); +} +static DEVICE_ATTR_RW(flash_brightness); + +static ssize_t max_flash_brightness_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); + + return sprintf(buf, "%u\n", fled_cdev->brightness.max); +} +static DEVICE_ATTR_RO(max_flash_brightness); + +static ssize_t flash_strobe_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); + unsigned long state; + ssize_t ret = -EINVAL; + + mutex_lock(&led_cdev->led_access); + + if (led_sysfs_is_disabled(led_cdev)) { + ret = -EBUSY; + goto unlock; + } + + ret = kstrtoul(buf, 10, &state); + if (ret) + goto unlock; + + if (state < 0 || state > 1) { + ret = -EINVAL; + goto unlock; + } + + ret = led_set_flash_strobe(fled_cdev, state); + if (ret < 0) + goto unlock; + ret = size; +unlock: + mutex_unlock(&led_cdev->led_access); + return ret; +} + +static ssize_t flash_strobe_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); + bool state; + int ret; + + /* no lock needed for this */ + ret = led_get_flash_strobe(fled_cdev, &state); + if (ret < 0) + return ret; + + return sprintf(buf, "%u\n", state); +} +static DEVICE_ATTR_RW(flash_strobe); + +static ssize_t flash_timeout_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); + unsigned long flash_timeout; + ssize_t ret; + + mutex_lock(&led_cdev->led_access); + + if (led_sysfs_is_disabled(led_cdev)) { + ret = -EBUSY; + goto unlock; + } + + ret = kstrtoul(buf, 10, &flash_timeout); + if (ret) + goto unlock; + + ret = led_set_flash_timeout(fled_cdev, flash_timeout); + if (ret < 0) + goto unlock; + + ret = size; +unlock: + mutex_unlock(&led_cdev->led_access); + return ret; +} + +static ssize_t flash_timeout_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); + + return sprintf(buf, "%u\n", fled_cdev->timeout.val); +} +static DEVICE_ATTR_RW(flash_timeout); + +static ssize_t max_flash_timeout_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); + + return sprintf(buf, "%u\n", fled_cdev->timeout.max); +} +static DEVICE_ATTR_RO(max_flash_timeout); + +static ssize_t flash_fault_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); + u32 fault, mask = 0x1; + char *pbuf = buf; + int i, ret, buf_len; + + ret = led_get_flash_fault(fled_cdev, &fault); + if (ret < 0) + return -EINVAL; + + *buf = '\0'; + + for (i = 0; i < LED_NUM_FLASH_FAULTS; ++i) { + if (fault & mask) { + buf_len = sprintf(pbuf, "%s ", + led_flash_fault_names[i]); + pbuf += buf_len; + } + mask <<= 1; + } + + return sprintf(buf, "%s\n", buf); +} +static DEVICE_ATTR_RO(flash_fault); + +static ssize_t available_sync_leds_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); + char *pbuf = buf; + int i, buf_len; + + buf_len = sprintf(pbuf, "[0: none] "); + pbuf += buf_len; + + for (i = 0; i < fled_cdev->num_sync_leds; ++i) { + buf_len = sprintf(pbuf, "[%d: %s] ", i + 1, + fled_cdev->sync_leds[i]->led_cdev.name); + pbuf += buf_len; + } + + return sprintf(buf, "%s\n", buf); +} +static DEVICE_ATTR_RO(available_sync_leds); + +static ssize_t flash_sync_strobe_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); + unsigned long led_id; + ssize_t ret; + + mutex_lock(&led_cdev->led_access); + + if (led_sysfs_is_disabled(led_cdev)) { + ret = -EBUSY; + goto unlock; + } + + ret = kstrtoul(buf, 10, &led_id); + if (ret) + goto unlock; + + if (led_id > fled_cdev->num_sync_leds) { + ret = -ERANGE; + goto unlock; + } + + fled_cdev->sync_led_id = led_id; + + ret = size; +unlock: + mutex_unlock(&led_cdev->led_access); + return ret; +} + +static ssize_t flash_sync_strobe_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); + int sled_id = fled_cdev->sync_led_id; + char *sync_led_name = "none"; + + if (fled_cdev->sync_led_id > 0) + sync_led_name = (char *) + fled_cdev->sync_leds[sled_id - 1]->led_cdev.name; + + return sprintf(buf, "[%d: %s]\n", sled_id, sync_led_name); +} +static DEVICE_ATTR_RW(flash_sync_strobe); + +static struct attribute *led_flash_strobe_attrs[] = { + &dev_attr_flash_strobe.attr, + NULL, +}; + +static struct attribute *led_flash_timeout_attrs[] = { + &dev_attr_flash_timeout.attr, + &dev_attr_max_flash_timeout.attr, + NULL, +}; + +static struct attribute *led_flash_brightness_attrs[] = { + &dev_attr_flash_brightness.attr, + &dev_attr_max_flash_brightness.attr, + NULL, +}; + +static struct attribute *led_flash_fault_attrs[] = { + &dev_attr_flash_fault.attr, + NULL, +}; + +static struct attribute *led_flash_sync_strobe_attrs[] = { + &dev_attr_available_sync_leds.attr, + &dev_attr_flash_sync_strobe.attr, + NULL, +}; + +static const struct attribute_group led_flash_strobe_group = { + .attrs = led_flash_strobe_attrs, +}; + +static const struct attribute_group led_flash_timeout_group = { + .attrs = led_flash_timeout_attrs, +}; + +static const struct attribute_group led_flash_brightness_group = { + .attrs = led_flash_brightness_attrs, +}; + +static const struct attribute_group led_flash_fault_group = { + .attrs = led_flash_fault_attrs, +}; + +static const struct attribute_group led_flash_sync_strobe_group = { + .attrs = led_flash_sync_strobe_attrs, +}; + +static void led_flash_resume(struct led_classdev *led_cdev) +{ + struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); + + call_flash_op(fled_cdev, flash_brightness_set, + fled_cdev->brightness.val); + call_flash_op(fled_cdev, timeout_set, fled_cdev->timeout.val); +} + +static void led_flash_init_sysfs_groups(struct led_classdev_flash *fled_cdev) +{ + struct led_classdev *led_cdev = &fled_cdev->led_cdev; + const struct led_flash_ops *ops = fled_cdev->ops; + const struct attribute_group **flash_groups = fled_cdev->sysfs_groups; + + int num_sysfs_groups = 0; + + flash_groups[num_sysfs_groups++] = &led_flash_strobe_group; + + if (ops->flash_brightness_set) + flash_groups[num_sysfs_groups++] = &led_flash_brightness_group; + + if (ops->timeout_set) + flash_groups[num_sysfs_groups++] = &led_flash_timeout_group; + + if (ops->fault_get) + flash_groups[num_sysfs_groups++] = &led_flash_fault_group; + + if (led_cdev->flags & LED_DEV_CAP_SYNC_STROBE) + flash_groups[num_sysfs_groups++] = &led_flash_sync_strobe_group; + + led_cdev->groups = flash_groups; +} + +int led_classdev_flash_register(struct device *parent, + struct led_classdev_flash *fled_cdev) +{ + struct led_classdev *led_cdev; + const struct led_flash_ops *ops; + int ret; + + if (!fled_cdev) + return -EINVAL; + + led_cdev = &fled_cdev->led_cdev; + + if (led_cdev->flags & LED_DEV_CAP_FLASH) { + if (!led_cdev->brightness_set_sync) + return -EINVAL; + + ops = fled_cdev->ops; + if (!ops || !ops->strobe_set) + return -EINVAL; + + led_cdev->flash_resume = led_flash_resume; + + /* Select the sysfs attributes to be created for the device */ + led_flash_init_sysfs_groups(fled_cdev); + } + + /* Register led class device */ + ret = led_classdev_register(parent, led_cdev); + if (ret < 0) + return ret; + + /* Setting a torch brightness needs to have immediate effect */ + led_cdev->flags &= ~SET_BRIGHTNESS_ASYNC; + led_cdev->flags |= SET_BRIGHTNESS_SYNC; + + return 0; +} +EXPORT_SYMBOL_GPL(led_classdev_flash_register); + +void led_classdev_flash_unregister(struct led_classdev_flash *fled_cdev) +{ + if (!fled_cdev) + return; + + led_classdev_unregister(&fled_cdev->led_cdev); +} +EXPORT_SYMBOL_GPL(led_classdev_flash_unregister); + +static void led_clamp_align(struct led_flash_setting *s) +{ + u32 v, offset; + + v = s->val + s->step / 2; + v = clamp(v, s->min, s->max); + offset = v - s->min; + offset = s->step * (offset / s->step); + s->val = s->min + offset; +} + +int led_set_flash_timeout(struct led_classdev_flash *fled_cdev, u32 timeout) +{ + struct led_classdev *led_cdev = &fled_cdev->led_cdev; + struct led_flash_setting *s = &fled_cdev->timeout; + + s->val = timeout; + led_clamp_align(s); + + if (!(led_cdev->flags & LED_SUSPENDED)) + return call_flash_op(fled_cdev, timeout_set, s->val); + + return 0; +} +EXPORT_SYMBOL_GPL(led_set_flash_timeout); + +int led_get_flash_fault(struct led_classdev_flash *fled_cdev, u32 *fault) +{ + return call_flash_op(fled_cdev, fault_get, fault); +} +EXPORT_SYMBOL_GPL(led_get_flash_fault); + +int led_set_flash_brightness(struct led_classdev_flash *fled_cdev, + u32 brightness) +{ + struct led_classdev *led_cdev = &fled_cdev->led_cdev; + struct led_flash_setting *s = &fled_cdev->brightness; + + s->val = brightness; + led_clamp_align(s); + + if (!(led_cdev->flags & LED_SUSPENDED)) + return call_flash_op(fled_cdev, flash_brightness_set, s->val); + + return 0; +} +EXPORT_SYMBOL_GPL(led_set_flash_brightness); + +int led_update_flash_brightness(struct led_classdev_flash *fled_cdev) +{ + struct led_flash_setting *s = &fled_cdev->brightness; + u32 brightness; + + if (has_flash_op(fled_cdev, flash_brightness_get)) { + int ret = call_flash_op(fled_cdev, flash_brightness_get, + &brightness); + if (ret < 0) + return ret; + + s->val = brightness; + } + + return 0; +} +EXPORT_SYMBOL_GPL(led_update_flash_brightness); + +MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>"); +MODULE_DESCRIPTION("LED Flash class interface"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index dbeebac38d31..795ec994c663 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -179,6 +179,10 @@ EXPORT_SYMBOL_GPL(led_classdev_suspend); void led_classdev_resume(struct led_classdev *led_cdev) { led_cdev->brightness_set(led_cdev, led_cdev->brightness); + + if (led_cdev->flash_resume) + led_cdev->flash_resume(led_cdev); + led_cdev->flags &= ~LED_SUSPENDED; } EXPORT_SYMBOL_GPL(led_classdev_resume); @@ -239,9 +243,8 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed); - init_timer(&led_cdev->blink_timer); - led_cdev->blink_timer.function = led_timer_function; - led_cdev->blink_timer.data = (unsigned long)led_cdev; + setup_timer(&led_cdev->blink_timer, led_timer_function, + (unsigned long)led_cdev); #ifdef CONFIG_LEDS_TRIGGERS led_trigger_set_default(led_cdev); diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 7ea1ea42c2d2..d26af0a79a90 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -187,6 +187,7 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) led.gpiod = devm_get_gpiod_from_child(dev, child); if (IS_ERR(led.gpiod)) { fwnode_handle_put(child); + ret = PTR_ERR(led.gpiod); goto err; } @@ -229,7 +230,7 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) err: for (count = priv->num_leds - 2; count >= 0; count--) delete_gpio_led(&priv->leds[count]); - return ERR_PTR(-ENODEV); + return ERR_PTR(ret); } static const struct of_device_id of_gpio_leds_match[] = { diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c index 85c3714e1b5a..e2b847fe22a1 100644 --- a/drivers/leds/leds-mc13783.c +++ b/drivers/leds/leds-mc13783.c @@ -134,9 +134,7 @@ static struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt( if (!pdata) return ERR_PTR(-ENOMEM); - of_node_get(dev->parent->of_node); - - parent = of_find_node_by_name(dev->parent->of_node, "leds"); + parent = of_get_child_by_name(dev->parent->of_node, "leds"); if (!parent) goto out_node_put; diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h index 2348dbda5269..79efe57c7405 100644 --- a/drivers/leds/leds.h +++ b/drivers/leds/leds.h @@ -20,7 +20,8 @@ static inline void led_set_brightness_async(struct led_classdev *led_cdev, enum led_brightness value) { - led_cdev->brightness = min(value, led_cdev->max_brightness); + value = min(value, led_cdev->max_brightness); + led_cdev->brightness = value; if (!(led_cdev->flags & LED_SUSPENDED)) led_cdev->brightness_set(led_cdev, value); diff --git a/include/linux/led-class-flash.h b/include/linux/led-class-flash.h new file mode 100644 index 000000000000..5ba2facd7a51 --- /dev/null +++ b/include/linux/led-class-flash.h @@ -0,0 +1,207 @@ +/* + * LED Flash class interface + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * Author: Jacek Anaszewski <j.anaszewski@samsung.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. + * + */ +#ifndef __LINUX_FLASH_LEDS_H_INCLUDED +#define __LINUX_FLASH_LEDS_H_INCLUDED + +#include <linux/leds.h> +#include <uapi/linux/v4l2-controls.h> + +struct device_node; +struct led_classdev_flash; + +/* + * Supported led fault bits - must be kept in synch + * with V4L2_FLASH_FAULT bits. + */ +#define LED_FAULT_OVER_VOLTAGE (1 << 0) +#define LED_FAULT_TIMEOUT (1 << 1) +#define LED_FAULT_OVER_TEMPERATURE (1 << 2) +#define LED_FAULT_SHORT_CIRCUIT (1 << 3) +#define LED_FAULT_OVER_CURRENT (1 << 4) +#define LED_FAULT_INDICATOR (1 << 5) +#define LED_FAULT_UNDER_VOLTAGE (1 << 6) +#define LED_FAULT_INPUT_VOLTAGE (1 << 7) +#define LED_FAULT_LED_OVER_TEMPERATURE (1 << 8) +#define LED_NUM_FLASH_FAULTS 9 + +#define LED_FLASH_MAX_SYSFS_GROUPS 7 + +struct led_flash_ops { + /* set flash brightness */ + int (*flash_brightness_set)(struct led_classdev_flash *fled_cdev, + u32 brightness); + /* get flash brightness */ + int (*flash_brightness_get)(struct led_classdev_flash *fled_cdev, + u32 *brightness); + /* set flash strobe state */ + int (*strobe_set)(struct led_classdev_flash *fled_cdev, bool state); + /* get flash strobe state */ + int (*strobe_get)(struct led_classdev_flash *fled_cdev, bool *state); + /* set flash timeout */ + int (*timeout_set)(struct led_classdev_flash *fled_cdev, u32 timeout); + /* get the flash LED fault */ + int (*fault_get)(struct led_classdev_flash *fled_cdev, u32 *fault); +}; + +/* + * Current value of a flash setting along + * with its constraints. + */ +struct led_flash_setting { + /* maximum allowed value */ + u32 min; + /* maximum allowed value */ + u32 max; + /* step value */ + u32 step; + /* current value */ + u32 val; +}; + +struct led_classdev_flash { + /* led class device */ + struct led_classdev led_cdev; + + /* flash led specific ops */ + const struct led_flash_ops *ops; + + /* flash brightness value in microamperes along with its constraints */ + struct led_flash_setting brightness; + + /* flash timeout value in microseconds along with its constraints */ + struct led_flash_setting timeout; + + /* LED Flash class sysfs groups */ + const struct attribute_group *sysfs_groups[LED_FLASH_MAX_SYSFS_GROUPS]; + + /* LEDs available for flash strobe synchronization */ + struct led_classdev_flash **sync_leds; + + /* Number of LEDs available for flash strobe synchronization */ + int num_sync_leds; + + /* + * The identifier of the sub-led to synchronize the flash strobe with. + * Identifiers start from 1, which reflects the first element from the + * sync_leds array. 0 means that the flash strobe should not be + * synchronized. + */ + u32 sync_led_id; +}; + +static inline struct led_classdev_flash *lcdev_to_flcdev( + struct led_classdev *lcdev) +{ + return container_of(lcdev, struct led_classdev_flash, led_cdev); +} + +/** + * led_classdev_flash_register - register a new object of led_classdev class + * with support for flash LEDs + * @parent: the flash LED to register + * @fled_cdev: the led_classdev_flash structure for this device + * + * Returns: 0 on success or negative error value on failure + */ +extern int led_classdev_flash_register(struct device *parent, + struct led_classdev_flash *fled_cdev); + +/** + * led_classdev_flash_unregister - unregisters an object of led_classdev class + * with support for flash LEDs + * @fled_cdev: the flash LED to unregister + * + * Unregister a previously registered via led_classdev_flash_register object + */ +extern void led_classdev_flash_unregister(struct led_classdev_flash *fled_cdev); + +/** + * led_set_flash_strobe - setup flash strobe + * @fled_cdev: the flash LED to set strobe on + * @state: 1 - strobe flash, 0 - stop flash strobe + * + * Strobe the flash LED. + * + * Returns: 0 on success or negative error value on failure + */ +static inline int led_set_flash_strobe(struct led_classdev_flash *fled_cdev, + bool state) +{ + return fled_cdev->ops->strobe_set(fled_cdev, state); +} + +/** + * led_get_flash_strobe - get flash strobe status + * @fled_cdev: the flash LED to query + * @state: 1 - flash is strobing, 0 - flash is off + * + * Check whether the flash is strobing at the moment. + * + * Returns: 0 on success or negative error value on failure + */ +static inline int led_get_flash_strobe(struct led_classdev_flash *fled_cdev, + bool *state) +{ + if (fled_cdev->ops->strobe_get) + return fled_cdev->ops->strobe_get(fled_cdev, state); + + return -EINVAL; +} + +/** + * led_set_flash_brightness - set flash LED brightness + * @fled_cdev: the flash LED to set + * @brightness: the brightness to set it to + * + * Set a flash LED's brightness. + * + * Returns: 0 on success or negative error value on failure + */ +extern int led_set_flash_brightness(struct led_classdev_flash *fled_cdev, + u32 brightness); + +/** + * led_update_flash_brightness - update flash LED brightness + * @fled_cdev: the flash LED to query + * + * Get a flash LED's current brightness and update led_flash->brightness + * member with the obtained value. + * + * Returns: 0 on success or negative error value on failure + */ +extern int led_update_flash_brightness(struct led_classdev_flash *fled_cdev); + +/** + * led_set_flash_timeout - set flash LED timeout + * @fled_cdev: the flash LED to set + * @timeout: the flash timeout to set it to + * + * Set the flash strobe duration. + * + * Returns: 0 on success or negative error value on failure + */ +extern int led_set_flash_timeout(struct led_classdev_flash *fled_cdev, + u32 timeout); + +/** + * led_get_flash_fault - get the flash LED fault + * @fled_cdev: the flash LED to query + * @fault: bitmask containing flash faults + * + * Get the flash LED fault. + * + * Returns: 0 on success or negative error value on failure + */ +extern int led_get_flash_fault(struct led_classdev_flash *fled_cdev, + u32 *fault); + +#endif /* __LINUX_FLASH_LEDS_H_INCLUDED */ diff --git a/include/linux/leds.h b/include/linux/leds.h index cfceef32c9b3..f70f84f35674 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -46,6 +46,8 @@ struct led_classdev { #define LED_SYSFS_DISABLE (1 << 20) #define SET_BRIGHTNESS_ASYNC (1 << 21) #define SET_BRIGHTNESS_SYNC (1 << 22) +#define LED_DEV_CAP_FLASH (1 << 23) +#define LED_DEV_CAP_SYNC_STROBE (1 << 24) /* Set LED brightness level */ /* Must not sleep, use a workqueue if needed */ @@ -81,6 +83,7 @@ struct led_classdev { unsigned long blink_delay_on, blink_delay_off; struct timer_list blink_timer; int blink_brightness; + void (*flash_resume)(struct led_classdev *led_cdev); struct work_struct set_brightness_work; int delayed_set_value; |