linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH/RFC v2 0/8] LED / flash API integration
@ 2014-03-28 15:28 Jacek Anaszewski
  2014-03-28 15:28 ` [PATCH/RFC v2 1/8] leds: Add sysfs and kernel internal API for flash LEDs Jacek Anaszewski
                   ` (7 more replies)
  0 siblings, 8 replies; 11+ messages in thread
From: Jacek Anaszewski @ 2014-03-28 15:28 UTC (permalink / raw)
  To: linux-media, linux-leds, devicetree, linux-kernel
  Cc: s.nawrocki, a.hajda, kyungmin.park, Jacek Anaszewski

This is is a second version of the patch series being a follow up
of the discussion on Media summit 2013-10-23, related to the
LED / flash API integration (the notes from the discussion were
enclosed in the message [1], paragraph 5).
The series is based on linux-next next-20140328 and contains
significant number of modifications in comparison to its first
version (Lee, Richard and Sakari - thanks for the review).

In order to show the exemplary usage of the proposed mechanism
the patch series includes implementation of a Flash LED device
driver along with the suitable modifications in a media controller
driver.

Description of the proposed modifications according to
the kernel components they are relevant to:
    - LED subsystem modifications
        * added led_flash module which, when enabled in the config,
	  registers flash specific sysfs attributes:
            - flash_brightness
	    - max_flash_brightness
            - flash_timeout
            - max_flash_timeout
	    - flash_strobe
            - flash_fault
            - hw_triggered
	and exposes kernel internal API
            - led_set_flash_strobe
            - led_get_flash_strobe
            - led_set_flash_brightness
            - led_update_flash_brightness
            - led_set_flash_timeout
            - led_get_flash_fault
            - led_set_hw_triggered
            - led_sysfs_lock
            - led_sysfs_unlock
    - Addition of a V4L2 Flash sub-device registration helpers
        * added v4l2-flash.c and v4l2-flash.h files with helper
          functions that facilitate registration/unregistration
          of a subdevice, which wrapps a LED subsystem device and
          exposes V4L2 Flash control interface
    - exynos4-is media controller modifications
    - Addition of a driver for the flash cell of the MAX77693 mfd
        * the driver exploits the newly introduced mechanism
    - Update of the samsung-fimc.txt DT bindings documentation
    - Update of the max77693.txt DT bindings documentation
    - Update of the LED subsystem documentation

Thanks,
Jacek Anaszewski

[1] http://www.spinics.net/lists/linux-media/msg69253.html

Jacek Anaszewski (8):
  leds: Add sysfs and kernel internal API for flash LEDs
  leds: Improve and export led_update_brightness function
  Documentation: leds: Add description of flash mode
  leds: Add support for max77693 mfd flash cell
  DT: Add documentation for the mfd Maxim max77693 flash cell
  media: Add registration helpers for V4L2 flash sub-devices
  media: exynos4-is: Add support for v4l2-flash subdevs
  DT: Add documentation for exynos4-is camera-flash property

 .../devicetree/bindings/media/samsung-fimc.txt     |    3 +
 Documentation/devicetree/bindings/mfd/max77693.txt |   47 ++
 Documentation/leds/leds-class.txt                  |   52 ++
 drivers/leds/Kconfig                               |   18 +
 drivers/leds/Makefile                              |    2 +
 drivers/leds/led-class.c                           |   62 +-
 drivers/leds/led-core.c                            |   17 +
 drivers/leds/led-flash.c                           |  375 +++++++++
 drivers/leds/led-triggers.c                        |   16 +-
 drivers/leds/leds-max77693.c                       |  864 ++++++++++++++++++++
 drivers/leds/leds.h                                |    3 +
 drivers/media/platform/exynos4-is/media-dev.c      |   34 +-
 drivers/media/platform/exynos4-is/media-dev.h      |   14 +-
 drivers/media/v4l2-core/Kconfig                    |   10 +
 drivers/media/v4l2-core/Makefile                   |    2 +
 drivers/media/v4l2-core/v4l2-flash.c               |  302 +++++++
 drivers/mfd/max77693.c                             |    3 +-
 include/linux/leds.h                               |   34 +-
 include/linux/leds_flash.h                         |  189 +++++
 include/linux/mfd/max77693.h                       |   32 +
 include/media/v4l2-flash.h                         |  104 +++
 21 files changed, 2157 insertions(+), 26 deletions(-)
 create mode 100644 drivers/leds/led-flash.c
 create mode 100644 drivers/leds/leds-max77693.c
 create mode 100644 drivers/media/v4l2-core/v4l2-flash.c
 create mode 100644 include/linux/leds_flash.h
 create mode 100644 include/media/v4l2-flash.h

-- 
1.7.9.5


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH/RFC v2 1/8] leds: Add sysfs and kernel internal API for flash LEDs
  2014-03-28 15:28 [PATCH/RFC v2 0/8] LED / flash API integration Jacek Anaszewski
@ 2014-03-28 15:28 ` Jacek Anaszewski
  2014-03-31 10:16   ` Richard Purdie
  2014-03-28 15:28 ` [PATCH/RFC v2 2/8] leds: Improve and export led_update_brightness function Jacek Anaszewski
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 11+ messages in thread
From: Jacek Anaszewski @ 2014-03-28 15:28 UTC (permalink / raw)
  To: linux-media, linux-leds, devicetree, linux-kernel
  Cc: s.nawrocki, a.hajda, kyungmin.park, Jacek Anaszewski, Bryan Wu,
	Richard Purdie

Some LED devices support two operation modes - torch and
flash. This patch provides support for flash LED devices
in the LED subsystem by introducing new sysfs attributes
and kernel internal interface. The attributes being
introduced are: flash_brightness, flash_strobe, flash_timeout,
max_flash_timeout, max_flash_brightness, flash_fault and
hw_triggered. All the flash related features are placed
in a separate module.
The modifications aim to be compatible with V4L2 framework
requirements related to the flash devices management. The
design assumes that V4L2 sub-device can take of the LED class
device control and communicate with it through the kernel
internal interface. The LED sysfs interface is made
unavailable then.

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Bryan Wu <cooloney@gmail.com>
Cc: Richard Purdie <rpurdie@rpsys.net>
---
 drivers/leds/Kconfig        |    8 +
 drivers/leds/Makefile       |    1 +
 drivers/leds/led-class.c    |   56 +++++--
 drivers/leds/led-flash.c    |  375 +++++++++++++++++++++++++++++++++++++++++++
 drivers/leds/led-triggers.c |   16 +-
 drivers/leds/leds.h         |    3 +
 include/linux/leds.h        |   24 ++-
 include/linux/leds_flash.h  |  189 ++++++++++++++++++++++
 8 files changed, 658 insertions(+), 14 deletions(-)
 create mode 100644 drivers/leds/led-flash.c
 create mode 100644 include/linux/leds_flash.h

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 2062682..1e1c81f 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -19,6 +19,14 @@ 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 "Flash LEDs Support"
+	depends on LEDS_CLASS
+	help
+	  This option enables support for flash LED devices. Say Y if you
+	  want to use flash specific features of a LED device, if they
+	  are supported.
+
 comment "LED drivers"
 
 config LEDS_88PM860X
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 3cd76db..8861b86 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-flash.o
 obj-$(CONFIG_LEDS_TRIGGERS)		+= led-triggers.o
 
 # LED Platform Drivers
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index f37d63c..5bac140 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -9,16 +9,18 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/module.h>
-#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/device.h>
+#include <linux/err.h>
 #include <linux/init.h>
+#include <linux/kernel.h>
 #include <linux/list.h>
+#include <linux/module.h>
+#include <linux/slab.h>
 #include <linux/spinlock.h>
-#include <linux/device.h>
 #include <linux/timer.h>
-#include <linux/err.h>
-#include <linux/ctype.h>
 #include <linux/leds.h>
+#include <linux/leds_flash.h>
 #include "leds.h"
 
 static struct class *leds_class;
@@ -45,28 +47,38 @@ static ssize_t brightness_store(struct device *dev,
 {
 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
 	unsigned long state;
-	ssize_t ret = -EINVAL;
+	ssize_t ret;
+
+	mutex_lock(&led_cdev->led_lock);
+
+	if (led_sysfs_is_locked(led_cdev)) {
+		ret = -EBUSY;
+		goto unlock;
+	}
 
 	ret = kstrtoul(buf, 10, &state);
 	if (ret)
-		return ret;
+		goto unlock;
 
 	if (state == LED_OFF)
 		led_trigger_remove(led_cdev);
 	__led_set_brightness(led_cdev, state);
+	ret = size;
 
-	return size;
+unlock:
+	mutex_unlock(&led_cdev->led_lock);
+	return ret;
 }
 static DEVICE_ATTR_RW(brightness);
 
-static ssize_t led_max_brightness_show(struct device *dev,
+static ssize_t max_brightness_show(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
 
 	return sprintf(buf, "%u\n", led_cdev->max_brightness);
 }
-static DEVICE_ATTR(max_brightness, 0444, led_max_brightness_show, NULL);
+static DEVICE_ATTR_RO(max_brightness);
 
 #ifdef CONFIG_LEDS_TRIGGERS
 static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
@@ -173,7 +185,15 @@ EXPORT_SYMBOL_GPL(led_classdev_suspend);
  */
 void led_classdev_resume(struct led_classdev *led_cdev)
 {
+	struct led_flash *flash = led_cdev->flash;
+
 	led_cdev->brightness_set(led_cdev, led_cdev->brightness);
+	if (flash) {
+		call_flash_op(brightness_set, led_cdev,
+				flash->brightness);
+		call_flash_op(timeout_set, led_cdev,
+				&flash->timeout);
+	}
 	led_cdev->flags &= ~LED_SUSPENDED;
 }
 EXPORT_SYMBOL_GPL(led_classdev_resume);
@@ -210,14 +230,24 @@ static const struct dev_pm_ops leds_class_dev_pm_ops = {
  */
 int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
 {
+	int ret;
+
 	led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
 				      "%s", led_cdev->name);
 	if (IS_ERR(led_cdev->dev))
 		return PTR_ERR(led_cdev->dev);
 
+	ret = led_classdev_init_flash(led_cdev);
+	if (ret < 0) {
+		dev_dbg(parent,
+			"Flash LED initialization failed for the %s\n device", led_cdev->name);
+		goto error_flash_init;
+	}
+
 #ifdef CONFIG_LEDS_TRIGGERS
 	init_rwsem(&led_cdev->trigger_lock);
 #endif
+	mutex_init(&led_cdev->led_lock);
 	/* add to the list of leds */
 	down_write(&leds_list_lock);
 	list_add_tail(&led_cdev->node, &leds_list);
@@ -242,6 +272,10 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
 			led_cdev->name);
 
 	return 0;
+
+error_flash_init:
+	device_destroy(leds_class, 0);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(led_classdev_register);
 
@@ -271,6 +305,8 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
 	down_write(&leds_list_lock);
 	list_del(&led_cdev->node);
 	up_write(&leds_list_lock);
+
+	mutex_destroy(&led_cdev->led_lock);
 }
 EXPORT_SYMBOL_GPL(led_classdev_unregister);
 
diff --git a/drivers/leds/led-flash.c b/drivers/leds/led-flash.c
new file mode 100644
index 0000000..72db06a
--- /dev/null
+++ b/drivers/leds/led-flash.c
@@ -0,0 +1,375 @@
+/*
+ * LED Class Flash interface
+ *
+ * Copyright (C) 2014 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/module.h>
+#include <linux/leds_flash.h>
+#include "leds.h"
+
+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);
+	unsigned long state;
+	ssize_t ret;
+
+	mutex_lock(&led_cdev->led_lock);
+
+	if (led_sysfs_is_locked(led_cdev)) {
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	ret = kstrtoul(buf, 10, &state);
+	if (ret)
+		goto unlock;
+
+	led_set_flash_brightness(led_cdev, state);
+	ret = size;
+unlock:
+	mutex_unlock(&led_cdev->led_lock);
+	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_flash *flash = led_cdev->flash;
+
+	/* no lock needed for this */
+	led_update_flash_brightness(led_cdev);
+
+	return sprintf(buf, "%u\n", flash->brightness);
+}
+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_flash *flash = led_cdev->flash;
+
+	return sprintf(buf, "%u\n", flash->max_brightness);
+}
+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);
+	unsigned long state;
+	ssize_t ret;
+
+	mutex_lock(&led_cdev->led_lock);
+
+	if (led_sysfs_is_locked(led_cdev)) {
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	ret = kstrtoul(buf, 10, &state);
+	if (ret)
+		goto unlock;
+
+	if (state < 0 || state > 1)
+		return -EINVAL;
+
+	ret = led_set_flash_strobe(led_cdev, state);
+	if (ret < 0)
+		goto unlock;
+	ret = size;
+unlock:
+	mutex_unlock(&led_cdev->led_lock);
+	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);
+	int ret;
+
+	/* no lock needed for this */
+	ret = led_get_flash_strobe(led_cdev);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%u\n", ret);
+}
+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);
+	unsigned long flash_timeout;
+	ssize_t ret;
+
+	mutex_lock(&led_cdev->led_lock);
+
+	if (led_sysfs_is_locked(led_cdev)) {
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	ret = kstrtoul(buf, 10, &flash_timeout);
+	if (ret)
+		goto unlock;
+
+	ret = led_set_flash_timeout(led_cdev, &flash_timeout);
+	if (ret < 0)
+		goto unlock;
+
+	ret = size;
+unlock:
+	mutex_unlock(&led_cdev->led_lock);
+	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_flash *flash = led_cdev->flash;
+
+	return sprintf(buf, "%lu\n", flash->timeout);
+}
+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_flash *flash = led_cdev->flash;
+
+	return sprintf(buf, "%lu\n", flash->max_timeout);
+}
+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);
+	unsigned int fault;
+	int ret;
+
+	ret = led_get_flash_fault(led_cdev, &fault);
+	if (ret < 0)
+		return -EINVAL;
+
+	return sprintf(buf, "0x%8.8x\n", fault);
+}
+static DEVICE_ATTR_RO(flash_fault);
+
+static ssize_t hw_triggered_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	unsigned long hw_triggered;
+	ssize_t ret;
+
+	mutex_lock(&led_cdev->led_lock);
+
+	if (led_sysfs_is_locked(led_cdev)) {
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	ret = kstrtoul(buf, 10, &hw_triggered);
+	if (ret)
+		goto unlock;
+
+	if (hw_triggered > 1) {
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	ret = led_set_hw_triggered(led_cdev, hw_triggered);
+	if (ret < 0)
+		goto unlock;
+	ret = size;
+unlock:
+	mutex_unlock(&led_cdev->led_lock);
+	return ret;
+}
+
+static ssize_t hw_triggered_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", led_cdev->flash->hw_triggered);
+}
+static DEVICE_ATTR_RW(hw_triggered);
+
+static struct attribute *flash_led_attrs[] = {
+	&dev_attr_flash_brightness.attr,
+	&dev_attr_flash_strobe.attr,
+	&dev_attr_flash_timeout.attr,
+	&dev_attr_max_flash_timeout.attr,
+	&dev_attr_max_flash_brightness.attr,
+	&dev_attr_flash_fault.attr,
+	&dev_attr_hw_triggered.attr,
+	NULL,
+};
+
+static const struct attribute_group flash_led_group = {
+	.attrs = flash_led_attrs,
+};
+
+int led_classdev_init_flash(struct led_classdev *led_cdev)
+{
+	struct led_flash *flash = led_cdev->flash;
+	const struct led_flash_ops *ops;
+	int ret;
+
+	if (!flash)
+		return 0;
+
+	ops = &flash->ops;
+	if (!ops || !ops->brightness_set || !ops->brightness_get ||
+	    !ops->strobe_set || !ops->strobe_get || !ops->timeout_set ||
+	    !ops->hw_trig_set || !ops->fault_get)
+		return -EINVAL;
+
+	/* Create flash specific sysfs attributes */
+	ret = sysfs_create_group(&led_cdev->dev->kobj, &flash_led_group);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(led_classdev_init_flash);
+
+/* Caller must ensure led_cdev->led_lock held */
+void led_sysfs_lock(struct led_classdev *led_cdev)
+{
+	led_cdev->flags |= LED_SYSFS_LOCK;
+}
+EXPORT_SYMBOL(led_sysfs_lock);
+
+/* Caller must ensure led_cdev->led_lock held */
+void led_sysfs_unlock(struct led_classdev *led_cdev)
+{
+	led_cdev->flags &= ~LED_SYSFS_LOCK;
+}
+EXPORT_SYMBOL(led_sysfs_unlock);
+
+int led_set_flash_strobe(struct led_classdev *led_cdev, bool state)
+{
+	struct led_flash *flash = led_cdev->flash;
+
+	if (!flash)
+		return -EINVAL;
+
+	return call_flash_op(strobe_set, led_cdev, state);
+}
+EXPORT_SYMBOL(led_set_flash_strobe);
+
+int led_get_flash_strobe(struct led_classdev *led_cdev)
+{
+	struct led_flash *flash = led_cdev->flash;
+
+	if (!flash)
+		return -EINVAL;
+
+	return call_flash_op(strobe_get, led_cdev);
+}
+EXPORT_SYMBOL(led_get_flash_strobe);
+
+int led_set_flash_timeout(struct led_classdev *led_cdev, unsigned long *timeout)
+{
+	struct led_flash *flash = led_cdev->flash;
+	int ret = 0;
+
+	if (!flash)
+		return -EINVAL;
+
+	flash->timeout = min(*timeout, flash->max_timeout);
+
+	if (!(led_cdev->flags & LED_SUSPENDED))
+		ret = call_flash_op(timeout_set, led_cdev, &flash->timeout);
+
+	return ret;
+}
+EXPORT_SYMBOL(led_set_flash_timeout);
+
+int led_get_flash_fault(struct led_classdev *led_cdev, unsigned int *fault)
+{
+	if (!led_cdev->flash)
+		return -EINVAL;
+
+	return call_flash_op(fault_get, led_cdev, fault);
+}
+EXPORT_SYMBOL(led_get_flash_fault);
+
+int led_set_hw_triggered(struct led_classdev *led_cdev, bool enable)
+{
+	struct led_flash *flash = led_cdev->flash;
+	int ret;
+
+	if (!flash)
+		return -EINVAL;
+
+	if (flash->has_hw_trig) {
+		ret = call_flash_op(hw_trig_set, led_cdev, enable);
+		if (ret < 0)
+			return -EINVAL;
+		flash->hw_triggered = enable;
+	} else if (enable)
+		return -EINVAL;
+
+	return 0;
+}
+EXPORT_SYMBOL(led_set_hw_triggered);
+
+int led_set_flash_brightness(struct led_classdev *led_cdev, int brightness)
+{
+	struct led_flash *flash = led_cdev->flash;
+	int ret = 0;
+
+	flash->brightness = min(brightness, flash->max_brightness);
+	if (!(led_cdev->flags & LED_SUSPENDED))
+		ret = call_flash_op(brightness_set, led_cdev,
+						flash->brightness);
+	return ret;
+}
+
+int led_update_flash_brightness(struct led_classdev *led_cdev)
+{
+	struct led_flash *flash = led_cdev->flash;
+	int ret;
+
+	ret = call_flash_op(brightness_get, led_cdev);
+	if (ret >= 0) {
+		flash->brightness = ret;
+		return 0;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(led_update_flash_brightness);
+
+static int __init flash_leds_init(void)
+{
+	return 0;
+}
+
+static void __exit flash_leds_exit(void)
+{
+}
+
+subsys_initcall(flash_leds_init);
+module_exit(flash_leds_exit);
+
+MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LED Class Flash Interface");
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index df1a7c1..40e21c0 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -37,6 +37,14 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
 	char trigger_name[TRIG_NAME_MAX];
 	struct led_trigger *trig;
 	size_t len;
+	int ret = count;
+
+	mutex_lock(&led_cdev->led_lock);
+
+	if (led_sysfs_is_locked(led_cdev)) {
+		ret = -EBUSY;
+		goto exit_unlock;
+	}
 
 	trigger_name[sizeof(trigger_name) - 1] = '\0';
 	strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
@@ -47,7 +55,7 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
 
 	if (!strcmp(trigger_name, "none")) {
 		led_trigger_remove(led_cdev);
-		return count;
+		goto exit_unlock;
 	}
 
 	down_read(&triggers_list_lock);
@@ -58,12 +66,14 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
 			up_write(&led_cdev->trigger_lock);
 
 			up_read(&triggers_list_lock);
-			return count;
+			goto exit_unlock;
 		}
 	}
 	up_read(&triggers_list_lock);
 
-	return -EINVAL;
+exit_unlock:
+	mutex_unlock(&led_cdev->led_lock);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(led_trigger_store);
 
diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h
index 4c50365..f815ce7 100644
--- a/drivers/leds/leds.h
+++ b/drivers/leds/leds.h
@@ -17,6 +17,9 @@
 #include <linux/rwsem.h>
 #include <linux/leds.h>
 
+#define call_flash_op(op, args...)		\
+	((led_cdev)->flash->ops.op(args))
+
 static inline void __led_set_brightness(struct led_classdev *led_cdev,
 					enum led_brightness value)
 {
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 0287ab2..596555a 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -13,12 +13,14 @@
 #define __LINUX_LEDS_H_INCLUDED
 
 #include <linux/list.h>
-#include <linux/spinlock.h>
+#include <linux/mutex.h>
 #include <linux/rwsem.h>
+#include <linux/spinlock.h>
 #include <linux/timer.h>
 #include <linux/workqueue.h>
 
 struct device;
+struct led_flash;
 /*
  * LED Core
  */
@@ -42,6 +44,7 @@ struct led_classdev {
 #define LED_BLINK_ONESHOT	(1 << 17)
 #define LED_BLINK_ONESHOT_STOP	(1 << 18)
 #define LED_BLINK_INVERT	(1 << 19)
+#define LED_SYSFS_LOCK		(1 << 21)
 
 	/* Set LED brightness level */
 	/* Must not sleep, use a workqueue if needed */
@@ -69,6 +72,12 @@ struct led_classdev {
 	unsigned long		 blink_delay_on, blink_delay_off;
 	struct timer_list	 blink_timer;
 	int			 blink_brightness;
+	struct led_flash	*flash;
+	/*
+	 * Ensures consistent LED sysfs access and protects
+	 * LED sysfs locking mechanism
+	 */
+	struct mutex		led_lock;
 
 	struct work_struct	set_brightness_work;
 	int			delayed_set_value;
@@ -90,6 +99,7 @@ extern int led_classdev_register(struct device *parent,
 extern void led_classdev_unregister(struct led_classdev *led_cdev);
 extern void led_classdev_suspend(struct led_classdev *led_cdev);
 extern void led_classdev_resume(struct led_classdev *led_cdev);
+extern int led_classdev_init_flash(struct led_classdev *led_cdev);
 
 /**
  * led_blink_set - set blinking with software fallback
@@ -139,6 +149,18 @@ extern void led_blink_set_oneshot(struct led_classdev *led_cdev,
 extern void led_set_brightness(struct led_classdev *led_cdev,
 			       enum led_brightness brightness);
 
+/**
+ * led_sysfs_is_locked
+ * @led_cdev: the LED to query
+ *
+ * Returns: true if the sysfs interface of the led is disabled,
+ *	    false otherwise
+ */
+static inline bool led_sysfs_is_locked(struct led_classdev *led_cdev)
+{
+	return led_cdev->flags & LED_SYSFS_LOCK;
+}
+
 /*
  * LED Triggers
  */
diff --git a/include/linux/leds_flash.h b/include/linux/leds_flash.h
new file mode 100644
index 0000000..7363e0e
--- /dev/null
+++ b/include/linux/leds_flash.h
@@ -0,0 +1,189 @@
+/*
+ * Flash leds API
+ *
+ * Copyright (C) 2014 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>
+
+#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_UNDER_VOLTAGE		(1 << 6)
+#define LED_FAULT_INPUT_VOLTAGE		(1 << 7)
+#define LED_FAULT_LED_OVER_TEMPERATURE	(1 << 8)
+
+struct led_flash_ops {
+	/* set flash_brightness */
+	int	(*brightness_set)(struct led_classdev *led_cdev,
+					int brightness);
+	/* get flash_brightness */
+	int	(*brightness_get)(struct led_classdev *led_cdev);
+	/* setup flash strobe */
+	int	(*strobe_set)(struct led_classdev *led_cdev,
+					bool state);
+	/* get flash strobe state */
+	int	(*strobe_get)(struct led_classdev *led_cdev);
+	/* setup flash timeout */
+	int	(*timeout_set)(struct led_classdev *led_cdev,
+					unsigned long *timeout);
+	/* setup strobing the flash by hardware pin */
+	int	(*hw_trig_set)(struct led_classdev *led_cdev,
+					bool enable);
+	/* get the flash LED fault */
+	int	(*fault_get)(struct led_classdev *led_cdev,
+					unsigned int *fault);
+};
+
+struct led_flash {
+	/* flash led specific ops */
+	const struct led_flash_ops	ops;
+	/* flash led sysfs attributes */
+	const struct attribute_group	*sysfs_attrs;
+	/* current flash brightness */
+	int		brightness;
+	/*
+	 * maximum allowed flash brightness - it is read only and
+	 * must be initialized by the driver
+	 */
+	int		max_brightness;
+	/* current flash timeout */
+	unsigned long	timeout;
+	/*
+	 * maximum allowed flash timeout - it is read only and
+	 * must be initialized by the driver
+	 */
+	unsigned long	max_timeout;
+	/*
+	 * determines whether a device supports triggering a flash led
+	 * with use of a dedicated hardware pin
+	 */
+	bool		has_hw_trig;
+	/* if true then hardware pin triggers flash strobe */
+	bool		hw_triggered;
+};
+
+#ifdef CONFIG_LEDS_CLASS_FLASH
+/**
+ * led_classdev_init_flash - add support for flash led
+ * @led_cdev: the device to add flash led support to
+ *
+ * Returns: 0 on success, error code on failure.
+ */
+extern int led_classdev_init_flash(struct led_classdev *led_cdev);
+#else
+extern int led_classdev_init_flash(struct led_classdev *led_cdev)
+{
+	return 0;
+}
+#endif
+
+/**
+ * led_set_flash_strobe - setup flash strobe
+ * @led_cdev: the flash LED to set strobe on
+ * @state: 1 - strobe flash, 0 - stop flash strobe
+ *
+ * Setup flash strobe - trigger flash strobe
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+extern int led_set_flash_strobe(struct led_classdev *led_cdev, bool state);
+
+/**
+ * led_get_flash_strobe - get flash strobe status
+ * @led_cdev: the LED to query
+ *
+ * Check whether the flash is strobing at the moment or not.
+ *
+ * Returns: flash strobe status (0 or 1) on success or negative
+ *	    error value on failure.
+ */
+extern int led_get_flash_strobe(struct led_classdev *led_cdev);
+
+/**
+ * led_set_flash_brightness - set flash LED brightness
+ * @led_cdev: the LED to set
+ * @brightness: the brightness to set it to
+ *
+ * Returns: 0 on success, -EINVAL on failure
+ *
+ * Set a flash LED's brightness.
+ */
+extern int led_set_flash_brightness(struct led_classdev *led_cdev,
+					int brightness);
+
+/**
+ * led_update_flash_brightness - update flash LED brightness
+ * @led_cdev: the 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 *led_cdev);
+
+/**
+ * led_set_flash_timeout - set flash LED timeout
+ * @led_cdev: the LED to set
+ * @timeout: the flash timeout to set it to
+ *
+ * Returns: 0 on success, -EINVAL on failure
+ *
+ * Set the flash strobe duration. The duration set by the driver
+ * is returned in the timeout argument and may differ from the
+ * one that was originally passed.
+ */
+extern int led_set_flash_timeout(struct led_classdev *led_cdev,
+					unsigned long *timeout);
+
+/**
+ * led_get_flash_fault - get the flash LED fault
+ * @led_cdev: the LED to query
+ * @fault: bitmask containing flash faults
+ *
+ * Returns: 0 on success, -EINVAL on failure
+ *
+ * Get the flash LED fault.
+ */
+extern int led_get_flash_fault(struct led_classdev *led_cdev,
+					unsigned int *fault);
+
+/**
+ * led_set_hw_triggered - set the flash LED hw_triggered mode
+ * @led_cdev: the LED to set
+ * @enable: the state to set it to
+ *
+ * Returns: 0 on success, -EINVAL on failure
+ *
+ * Enable/disable triggering the flash LED via hardware pin
+ */
+extern int led_set_hw_triggered(struct led_classdev *led_cdev, bool enable);
+
+/**
+ * led_sysfs_lock - lock LED sysfs interface
+ * @led_cdev: the LED to set
+ *
+ * Lock the LED's sysfs interface
+ */
+extern void led_sysfs_lock(struct led_classdev *led_cdev);
+
+/**
+ * led_sysfs_unlock - unlock LED sysfs interface
+ * @led_cdev: the LED to set
+ *
+ * Unlock the LED's sysfs interface
+ */
+extern void led_sysfs_unlock(struct led_classdev *led_cdev);
+
+#endif	/* __LINUX_FLASH_LEDS_H_INCLUDED */
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH/RFC v2 2/8] leds: Improve and export led_update_brightness function
  2014-03-28 15:28 [PATCH/RFC v2 0/8] LED / flash API integration Jacek Anaszewski
  2014-03-28 15:28 ` [PATCH/RFC v2 1/8] leds: Add sysfs and kernel internal API for flash LEDs Jacek Anaszewski
@ 2014-03-28 15:28 ` Jacek Anaszewski
  2014-03-28 15:29 ` [PATCH/RFC v2 3/8] Documentation: leds: Add description of flash mode Jacek Anaszewski
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Jacek Anaszewski @ 2014-03-28 15:28 UTC (permalink / raw)
  To: linux-media, linux-leds, devicetree, linux-kernel
  Cc: s.nawrocki, a.hajda, kyungmin.park, Jacek Anaszewski, Bryan Wu,
	Richard Purdie

led_update_brightness helper function used to be exploited
only locally in the led-class.c module, where its result was
being passed to the brightness_show sysfs callback. With the
introduction of v4l2-flash subdevice the same functionality
became required for reading current brightness from a LED
device. This patch adds checking brightness_get callback
error code and adds the function to the LED subsystem
public API.

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Bryan Wu <cooloney@gmail.com>
Cc: Richard Purdie <rpurdie@rpsys.net>
---
 drivers/leds/led-class.c |    6 ------
 drivers/leds/led-core.c  |   17 +++++++++++++++++
 include/linux/leds.h     |   10 ++++++++++
 3 files changed, 27 insertions(+), 6 deletions(-)

diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 5bac140..efe6812 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -25,12 +25,6 @@
 
 static struct class *leds_class;
 
-static void led_update_brightness(struct led_classdev *led_cdev)
-{
-	if (led_cdev->brightness_get)
-		led_cdev->brightness = led_cdev->brightness_get(led_cdev);
-}
-
 static ssize_t brightness_show(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index 71b40d3..41f2a6a 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -126,3 +126,20 @@ void led_set_brightness(struct led_classdev *led_cdev,
 	__led_set_brightness(led_cdev, brightness);
 }
 EXPORT_SYMBOL(led_set_brightness);
+
+int led_update_brightness(struct led_classdev *led_cdev)
+{
+	int ret;
+
+	if (led_cdev->brightness_get == NULL)
+		return -EINVAL;
+
+	ret = led_cdev->brightness_get(led_cdev);
+	if (ret >= 0) {
+		led_cdev->brightness = ret;
+		return 0;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(led_update_brightness);
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 596555a..c02dd7b 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -148,6 +148,16 @@ extern void led_blink_set_oneshot(struct led_classdev *led_cdev,
  */
 extern void led_set_brightness(struct led_classdev *led_cdev,
 			       enum led_brightness brightness);
+/**
+ * led_update_brightness - update LED brightness
+ * @led_cdev: the LED to query
+ *
+ * Get an LED's current brightness and update led_cdev->brightness
+ * member with the obtained value.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+extern int led_update_brightness(struct led_classdev *led_cdev);
 
 /**
  * led_sysfs_is_locked
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH/RFC v2 3/8] Documentation: leds: Add description of flash mode
  2014-03-28 15:28 [PATCH/RFC v2 0/8] LED / flash API integration Jacek Anaszewski
  2014-03-28 15:28 ` [PATCH/RFC v2 1/8] leds: Add sysfs and kernel internal API for flash LEDs Jacek Anaszewski
  2014-03-28 15:28 ` [PATCH/RFC v2 2/8] leds: Improve and export led_update_brightness function Jacek Anaszewski
@ 2014-03-28 15:29 ` Jacek Anaszewski
  2014-03-28 15:29 ` [PATCH/RFC v2 4/8] leds: Add support for max77693 mfd flash cell Jacek Anaszewski
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Jacek Anaszewski @ 2014-03-28 15:29 UTC (permalink / raw)
  To: linux-media, linux-leds, devicetree, linux-kernel
  Cc: s.nawrocki, a.hajda, kyungmin.park, Jacek Anaszewski, Bryan Wu,
	Richard Purdie

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Bryan Wu <cooloney@gmail.com>
Cc: Richard Purdie <rpurdie@rpsys.net>
---
 Documentation/leds/leds-class.txt |   52 +++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/Documentation/leds/leds-class.txt b/Documentation/leds/leds-class.txt
index 62261c0..ea50d27 100644
--- a/Documentation/leds/leds-class.txt
+++ b/Documentation/leds/leds-class.txt
@@ -8,6 +8,58 @@ LED is defined in max_brightness file. The brightness file will set the brightne
 of the LED (taking a value 0-max_brightness). Most LEDs don't have hardware
 brightness support so will just be turned on for non-zero brightness settings.
 
+Some LED devices support two modes - torch and flash. In order to enable
+support for flash LEDs the CONFIG_LEDS_CLASS_FLASH symbol must be defined
+in the kernel config. A flash LED driver must initialize the "flash" field
+of the led_classdev structure (see <linux/leds_flash.h>) to enable flash
+related features of the LED subsystem for the driver.
+
+There are seven sysfs attributes dedicated specifically to the flash LED devices:
+
+	- flash_brightness - flash LED brightness in milliampers (RW)
+	- max_flash_brightness - maximum available flash LED brightness (RO)
+	- flash_timeout - flash strobe duration in milliseconds (RW)
+	- max_flash_timeout - maximum available flash strobe duration (RO)
+	- flash_strobe - flash strobe state (RW)
+	- flash_fault - bitmask of flash faults that may have occured, possible
+			flags are:
+		* 0x01 - flash controller voltage to the flash LED has exceeded
+			 the limit specific to the flash controller
+		* 0x02 - the flash strobe was still on when the timeout set by
+			 the user has expired; not all flash controllers may set
+			 this in all such conditions
+		* 0x04 - the flash controller has overheated
+		* 0x08 - the short circuit protection of the flash controller
+			 has been triggered
+		* 0x10 - current in the LED power supply has exceeded the limit
+			 specific to the flash controller
+		* 0x40 - flash controller voltage to the flash LED has been below
+			 the minimum limit specific to the flash
+		* 0x80 - the input voltage of the flash controller is below
+			 the limit under which strobing the flash at full current
+			 will not be possible. The condition persists until this
+			 flag is no longer set
+		* 0x100 - the temperature of the LED has exceeded its allowed
+			  upper limit
+	- hw_triggered - some devices expose dedicated hardware pins for
+			 triggering a flash LED - the attribute allows to set
+			 this mode (RW)
+
+The LED subsystem driver can be controlled also from the level of
+the VideoForLinux2 subsystem. In order to enable this the CONFIG_V4L2_FLASH
+symbol has to be defined in the kernel config. The driver must
+initialize v4l2_flash_ctrl_config structure and pass it to the v4l2_flash_init
+function. On remove v4l2_flash_release has to be called (see <media/v4l2-flash.h>).
+After proper initialization V4L2 Flash sub-device is created. The sub-device
+must be registered by a V4L2 video device to become available for the user
+space. This is accomplished with use of asynchronous sub-device registration
+mechanism (see <media/v4l2-async.h>).
+A V4l2 Flash sub-device exposes a number of V4L2 controls.
+When the V4L2_CID_FLASH_LED_MODE control is set to V4L2_FLASH_LED_MODE_TORCH
+or V4L2_FLASH_LED_MODE_FLASH the LED subsystem sysfs interface becomes
+unavailable. The interface can be unlocked by setting the mode back
+to V4L2_FLASH_LED_MODE_NONE.
+
 The class also introduces the optional concept of an LED trigger. A trigger
 is a kernel based source of led events. Triggers can either be simple or
 complex. A simple trigger isn't configurable and is designed to slot into
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH/RFC v2 4/8] leds: Add support for max77693 mfd flash cell
  2014-03-28 15:28 [PATCH/RFC v2 0/8] LED / flash API integration Jacek Anaszewski
                   ` (2 preceding siblings ...)
  2014-03-28 15:29 ` [PATCH/RFC v2 3/8] Documentation: leds: Add description of flash mode Jacek Anaszewski
@ 2014-03-28 15:29 ` Jacek Anaszewski
  2014-03-31  7:48   ` Lee Jones
  2014-03-28 15:29 ` [PATCH/RFC v2 5/8] DT: Add documentation for the mfd Maxim max77693 " Jacek Anaszewski
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 11+ messages in thread
From: Jacek Anaszewski @ 2014-03-28 15:29 UTC (permalink / raw)
  To: linux-media, linux-leds, devicetree, linux-kernel
  Cc: s.nawrocki, a.hajda, kyungmin.park, Jacek Anaszewski, Bryan Wu,
	Richard Purdie, SangYoung Son, Samuel Ortiz, Lee Jones

This patch adds led-flash support to Maxim max77693 chipset.
Device can be exposed to user space through LED subsystem
sysfs interface or through V4L2 subdevice when the support
for Multimedia Framework is enabled. Device supports up to
two leds which can work in flash and torch mode. Leds can
be triggered externally or by software.

Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Bryan Wu <cooloney@gmail.com>
Cc: Richard Purdie <rpurdie@rpsys.net>
Cc: SangYoung Son <hello.son@smasung.com>
Cc: Samuel Ortiz <sameo@linux.intel.com>
Cc: Lee Jones <lee.jones@linaro.org>
---
 drivers/leds/Kconfig         |   10 +
 drivers/leds/Makefile        |    1 +
 drivers/leds/leds-max77693.c |  864 ++++++++++++++++++++++++++++++++++++++++++
 drivers/mfd/max77693.c       |    3 +-
 include/linux/mfd/max77693.h |   32 ++
 5 files changed, 909 insertions(+), 1 deletion(-)
 create mode 100644 drivers/leds/leds-max77693.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 1e1c81f..b2152a6 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -462,6 +462,16 @@ config LEDS_TCA6507
 	  LED driver chips accessed via the I2C bus.
 	  Driver support brightness control and hardware-assisted blinking.
 
+config LEDS_MAX77693
+	tristate "LED support for MAX77693 Flash"
+	depends on LEDS_CLASS_FLASH
+	depends on MFD_MAX77693
+	depends on OF
+	help
+	  This option enables support for the flash part of the MAX77693
+	  multifunction device. It has build in control for two leds in flash
+	  and torch mode.
+
 config LEDS_MAX8997
 	tristate "LED support for MAX8997 PMIC"
 	depends on LEDS_CLASS && MFD_MAX8997
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 8861b86..64f6234 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_LEDS_MC13783)		+= leds-mc13783.o
 obj-$(CONFIG_LEDS_NS2)			+= leds-ns2.o
 obj-$(CONFIG_LEDS_NETXBIG)		+= leds-netxbig.o
 obj-$(CONFIG_LEDS_ASIC3)		+= leds-asic3.o
+obj-$(CONFIG_LEDS_MAX77693)		+= leds-max77693.o
 obj-$(CONFIG_LEDS_MAX8997)		+= leds-max8997.o
 obj-$(CONFIG_LEDS_LM355x)		+= leds-lm355x.o
 obj-$(CONFIG_LEDS_BLINKM)		+= leds-blinkm.o
diff --git a/drivers/leds/leds-max77693.c b/drivers/leds/leds-max77693.c
new file mode 100644
index 0000000..1bfac68
--- /dev/null
+++ b/drivers/leds/leds-max77693.c
@@ -0,0 +1,864 @@
+/*
+ *	Copyright (C) 2014, Samsung Electronics Co., Ltd.
+ *
+ *	Authors: Andrzej Hajda <a.hajda@samsung.com>
+ *		 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/slab.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/leds.h>
+#include <linux/mfd/max77693.h>
+#include <linux/mfd/max77693-private.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <asm/div64.h>
+
+#include <media/v4l2-flash.h>
+
+#define MAX77693_LED_NAME		"max77693-flash"
+
+#define MAX77693_TORCH_IOUT_BITS	4
+
+#define MAX77693_TORCH_NO_TIMER		0x40
+#define MAX77693_FLASH_TIMER_LEVEL	0x80
+
+#define MAX77693_FLASH_EN_OFF		0
+#define MAX77693_FLASH_EN_FLASH		1
+#define MAX77693_FLASH_EN_TORCH		2
+#define MAX77693_FLASH_EN_ON		3
+
+#define MAX77693_FLASH_EN1_SHIFT	6
+#define MAX77693_FLASH_EN2_SHIFT	4
+#define MAX77693_TORCH_EN1_SHIFT	2
+#define MAX77693_TORCH_EN2_SHIFT	0
+
+#define MAX77693_FLASH_LOW_BATTERY_EN	0x80
+
+#define MAX77693_FLASH_BOOST_FIXED	0x04
+#define MAX77693_FLASH_BOOST_LEDNUM_2	0x80
+
+#define MAX77693_FLASH_TIMEOUT_MIN	62500
+#define MAX77693_FLASH_TIMEOUT_MAX	1000000
+#define MAX77693_FLASH_TIMEOUT_STEP	62500
+
+#define MAX77693_TORCH_TIMEOUT_MIN	262000
+#define MAX77693_TORCH_TIMEOUT_MAX	15728000
+
+#define MAX77693_FLASH_IOUT_MIN		15625
+#define MAX77693_FLASH_IOUT_MAX_1LED	1000000
+#define MAX77693_FLASH_IOUT_MAX_2LEDS	625000
+#define MAX77693_FLASH_IOUT_STEP	15625
+
+#define MAX77693_TORCH_IOUT_MIN		15625
+#define MAX77693_TORCH_IOUT_MAX		250000
+#define MAX77693_TORCH_IOUT_STEP	15625
+
+#define MAX77693_FLASH_VSYS_MIN		2400
+#define MAX77693_FLASH_VSYS_MAX		3400
+#define MAX77693_FLASH_VSYS_STEP	33
+
+#define MAX77693_FLASH_VOUT_MIN		3300
+#define MAX77693_FLASH_VOUT_MAX		5500
+#define MAX77693_FLASH_VOUT_STEP	25
+#define MAX77693_FLASH_VOUT_RMIN	0x0c
+
+#define MAX77693_LED_STATUS_FLASH_ON	(1 << 3)
+#define MAX77693_LED_STATUS_TORCH_ON	(1 << 2)
+
+#define MAX77693_LED_FLASH_INT_FLED2_OPEN	(1 << 0)
+#define MAX77693_LED_FLASH_INT_FLED2_SHORT	(1 << 1)
+#define MAX77693_LED_FLASH_INT_FLED1_OPEN	(1 << 2)
+#define MAX77693_LED_FLASH_INT_FLED1_SHORT	(1 << 3)
+#define MAX77693_LED_FLASH_INT_OVER_CURRENT	(1 << 4)
+
+#define MAX77693_MODE_OFF			0
+#define MAX77693_MODE_FLASH			(1 << 0)
+#define MAX77693_MODE_TORCH			(1 << 1)
+#define MAX77693_MODE_FLASH_EXTERNAL		(1 << 2)
+
+enum {
+	FLASH1,
+	FLASH2,
+	TORCH1,
+	TORCH2
+};
+
+enum {
+	FLASH,
+	TORCH
+};
+
+struct max77693_led {
+	struct regmap *regmap;
+	struct platform_device *pdev;
+	struct max77693_led_platform_data *pdata;
+	struct mutex lock;
+
+	struct led_classdev ldev;
+	struct v4l2_flash v4l2_flash;
+
+	unsigned int torch_brightness;
+	unsigned int flash_brightness;
+	struct work_struct work_brightness_set;
+	unsigned int mode_flags;
+};
+
+static u8 max77693_led_iout_to_reg(u32 ua)
+{
+	if (ua < MAX77693_FLASH_IOUT_MIN)
+		ua = MAX77693_FLASH_IOUT_MIN;
+	return (ua - MAX77693_FLASH_IOUT_MIN) / MAX77693_FLASH_IOUT_STEP;
+}
+
+static u8 max77693_flash_timeout_to_reg(u32 us)
+{
+	return (us - MAX77693_FLASH_TIMEOUT_MIN) / MAX77693_FLASH_TIMEOUT_STEP;
+}
+
+static const u32 max77693_torch_timeouts[] = {
+	262000, 524000, 786000, 1048000,
+	1572000, 2096000, 2620000, 3144000,
+	4193000, 5242000, 6291000, 7340000,
+	9437000, 11534000, 13631000, 1572800
+};
+
+static u8 max77693_torch_timeout_to_reg(u32 us)
+{
+	int i, b = 0, e = ARRAY_SIZE(max77693_torch_timeouts);
+
+	while (e - b > 1) {
+		i = b + (e - b) / 2;
+		if (us < max77693_torch_timeouts[i])
+			e = i;
+		else
+			b = i;
+	}
+	return b;
+}
+
+static struct max77693_led *ldev_to_led(struct led_classdev *ldev)
+{
+	return container_of(ldev, struct max77693_led, ldev);
+}
+
+static u32 max77693_torch_timeout_from_reg(u8 reg)
+{
+	return max77693_torch_timeouts[reg];
+}
+
+static u8 max77693_led_vsys_to_reg(u32 mv)
+{
+	return ((mv - MAX77693_FLASH_VSYS_MIN) / MAX77693_FLASH_VSYS_STEP) << 2;
+}
+
+static u8 max77693_led_vout_to_reg(u32 mv)
+{
+	return (mv - MAX77693_FLASH_VOUT_MIN) / MAX77693_FLASH_VOUT_STEP +
+		MAX77693_FLASH_VOUT_RMIN;
+}
+
+/* split composite current @i into two @iout according to @imax weights */
+static void max77693_calc_iout(u32 iout[2], u32 i, u32 imax[2])
+{
+	u64 t = i;
+
+	t *= imax[1];
+	do_div(t, imax[0] + imax[1]);
+
+	iout[1] = (u32)t / MAX77693_FLASH_IOUT_STEP * MAX77693_FLASH_IOUT_STEP;
+	iout[0] = i - iout[1];
+}
+
+static int max77693_set_mode(struct max77693_led *led, unsigned int mode)
+{
+	struct max77693_led_platform_data *p = led->pdata;
+	struct regmap *rmap = led->regmap;
+	int ret, v = 0;
+
+	if (mode & MAX77693_MODE_TORCH) {
+		if (p->trigger[TORCH1] & MAX77693_LED_TRIG_SOFT)
+			v |= MAX77693_FLASH_EN_ON << MAX77693_TORCH_EN1_SHIFT;
+		if (p->trigger[TORCH2] & MAX77693_LED_TRIG_SOFT)
+			v |= MAX77693_FLASH_EN_ON << MAX77693_TORCH_EN2_SHIFT;
+	}
+
+	if (mode & MAX77693_MODE_FLASH) {
+		if (p->trigger[FLASH1] & MAX77693_LED_TRIG_SOFT)
+			v |= MAX77693_FLASH_EN_ON << MAX77693_FLASH_EN1_SHIFT;
+		if (p->trigger[FLASH2] & MAX77693_LED_TRIG_SOFT)
+			v |= MAX77693_FLASH_EN_ON << MAX77693_FLASH_EN2_SHIFT;
+	} else if (mode & MAX77693_MODE_FLASH_EXTERNAL) {
+		if (p->trigger[FLASH1] & MAX77693_LED_TRIG_EXT)
+			v |= MAX77693_FLASH_EN_FLASH << MAX77693_FLASH_EN1_SHIFT;
+		if (p->trigger[FLASH2] & MAX77693_LED_TRIG_EXT)
+			v |= MAX77693_FLASH_EN_FLASH << MAX77693_FLASH_EN2_SHIFT;
+		/*
+		 * Enable hw triggering also for torch mode, as some camera
+		 * sensors use torch led to fathom ambient light conditions
+		 * before strobing the flash.
+		 */
+		if (p->trigger[TORCH1] & MAX77693_LED_TRIG_EXT)
+			v |= MAX77693_FLASH_EN_TORCH << MAX77693_TORCH_EN1_SHIFT;
+		if (p->trigger[TORCH2] & MAX77693_LED_TRIG_EXT)
+			v |= MAX77693_FLASH_EN_TORCH << MAX77693_TORCH_EN2_SHIFT;
+	}
+
+	/* Reset the register only prior setting flash modes */
+	if (mode != MAX77693_MODE_TORCH) {
+		ret = max77693_write_reg(rmap, MAX77693_LED_REG_FLASH_EN, 0);
+		if (ret < 0)
+			return ret;
+	}
+
+	return max77693_write_reg(rmap, MAX77693_LED_REG_FLASH_EN, v);
+}
+
+static inline int max77693_add_mode(struct max77693_led *led, unsigned int mode)
+{
+	int ret;
+
+	/* Once enabled torch mode is active until turned off */
+	if ((mode == MAX77693_MODE_TORCH) &&
+	    (led->mode_flags & MAX77693_MODE_TORCH))
+		return 0;
+
+	/*
+	 * FLASH_EXTERNAL mode activates HW triggered flash and torch
+	 * modes in the device. The related register settings interfere
+	 * with SW triggerred modes, thus clear them to ensure proper
+	 * device configuration.
+	 */
+	if (mode == MAX77693_MODE_FLASH_EXTERNAL)
+		led->mode_flags &= (~MAX77693_MODE_TORCH &
+				    ~MAX77693_MODE_FLASH);
+
+	led->mode_flags |= mode;
+
+	ret = max77693_set_mode(led, led->mode_flags);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Clear flash mode flag after setting the mode to avoid
+	 * spurous flash strobing on each successive torch mode
+	 * setting.
+	 */
+	if ((mode == MAX77693_MODE_FLASH) ||
+	    (mode == MAX77693_MODE_FLASH_EXTERNAL))
+		led->mode_flags &= ~mode;
+
+	return 0;
+}
+
+static inline int max77693_clear_mode(struct max77693_led *led,
+							unsigned int mode)
+{
+	led->mode_flags &= ~mode;
+
+	return max77693_set_mode(led, led->mode_flags);
+}
+
+static inline int max77693_calc_ma_current(u8 iout1_reg, u8 iout2_reg,
+					   u32 iout[2])
+{
+	/*
+	 * Calculate the mA current value set by the driver and ensure
+	 * that 0 value is not treated as 15.625mA if the platform
+	 * data iout was set to 0.
+	 */
+	return (iout1_reg + !!iout[0] +
+		iout2_reg + !!iout[1]) *
+		MAX77693_FLASH_IOUT_STEP / 1000;
+}
+
+static int max77693_set_torch_current(struct max77693_led *led,
+				unsigned int milli_amp)
+{
+	struct max77693_led_platform_data *p = led->pdata;
+	struct regmap *rmap = led->regmap;
+	u32 iout[2];
+	u8 v, iout1_reg, iout2_reg;
+	int ret;
+
+	max77693_calc_iout(iout, 1000 * milli_amp, &p->iout[TORCH1]);
+	iout1_reg = max77693_led_iout_to_reg(iout[0]);
+	iout2_reg = max77693_led_iout_to_reg(iout[1]);
+
+	v = iout1_reg | (iout2_reg << MAX77693_TORCH_IOUT_BITS);
+	ret = max77693_write_reg(rmap, MAX77693_LED_REG_ITORCH, v);
+	if (ret < 0)
+		return ret;
+
+	led->torch_brightness = max77693_calc_ma_current(iout1_reg, iout2_reg,
+							 &p->iout[TORCH1]);
+
+	return 0;
+}
+
+static int max77693_set_flash_current(struct max77693_led *led,
+				unsigned int milli_amp)
+{
+	struct max77693_led_platform_data *p = led->pdata;
+	struct regmap *rmap = led->regmap;
+	u32 iout[2];
+	u8 iout1_reg, iout2_reg;
+	int ret;
+
+	max77693_calc_iout(iout, 1000 * milli_amp, &p->iout[FLASH1]);
+	iout1_reg = max77693_led_iout_to_reg(iout[0]);
+	iout2_reg = max77693_led_iout_to_reg(iout[1]);
+
+	ret = max77693_write_reg(rmap, MAX77693_LED_REG_IFLASH1, iout1_reg);
+	if (ret < 0)
+		goto error_ret;
+	ret = max77693_write_reg(rmap, MAX77693_LED_REG_IFLASH2, iout2_reg);
+	if (ret < 0)
+		goto error_ret;
+
+	led->flash_brightness = max77693_calc_ma_current(iout1_reg, iout2_reg,
+							 &p->iout[FLASH1]);
+error_ret:
+	return ret;
+}
+
+static int max77693_set_timeout(struct max77693_led *led,
+				unsigned long *timeout)
+{
+	struct max77693_led_platform_data *p = led->pdata;
+	struct regmap *rmap = led->regmap;
+	u8 v;
+
+	v = max77693_flash_timeout_to_reg(*timeout);
+	*timeout = (v + 1) * MAX77693_FLASH_TIMEOUT_STEP;
+
+	if (p->trigger_type[FLASH] == MAX77693_LED_TRIG_TYPE_LEVEL)
+		v |= MAX77693_FLASH_TIMER_LEVEL;
+
+	return max77693_write_reg(rmap, MAX77693_LED_REG_FLASH_TIMER, v);
+}
+
+static int max77693_strobe_status_get(struct max77693_led *led)
+{
+	struct regmap *rmap = led->regmap;
+	u8 v;
+	int ret;
+
+	ret = max77693_read_reg(rmap, MAX77693_LED_REG_FLASH_INT_STATUS, &v);
+	if (ret < 0)
+		return ret;
+
+	return !!(v & MAX77693_LED_STATUS_FLASH_ON);
+}
+
+static int max77693_int_flag_get(struct max77693_led *led, u8 *v)
+{
+	struct regmap *rmap = led->regmap;
+
+	return max77693_read_reg(rmap, MAX77693_LED_REG_FLASH_INT, v);
+}
+
+static int max77693_setup(struct max77693_led *led)
+{
+	struct max77693_led_platform_data *p = led->pdata;
+	struct regmap *rmap = led->regmap;
+	int ret;
+	u8 v;
+
+	v = max77693_led_iout_to_reg(p->iout[FLASH1]);
+	ret = max77693_write_reg(rmap, MAX77693_LED_REG_IFLASH1, v);
+	if (ret < 0)
+		return ret;
+
+	v = max77693_led_iout_to_reg(p->iout[FLASH2]);
+	ret = max77693_write_reg(rmap, MAX77693_LED_REG_IFLASH2, v);
+	if (ret < 0)
+		return ret;
+
+	v = max77693_led_iout_to_reg(p->iout[TORCH1]);
+	v |= max77693_led_iout_to_reg(p->iout[TORCH2]) <<
+						MAX77693_TORCH_IOUT_BITS;
+	ret = max77693_write_reg(rmap, MAX77693_LED_REG_ITORCH, v);
+	if (ret < 0)
+		return ret;
+
+	if (p->timeout[TORCH] > 0)
+		v = max77693_torch_timeout_to_reg(p->timeout[TORCH]);
+	else
+		v = MAX77693_TORCH_NO_TIMER;
+	if (p->trigger_type[TORCH] == MAX77693_LED_TRIG_TYPE_LEVEL)
+		v |= MAX77693_FLASH_TIMER_LEVEL;
+	ret = max77693_write_reg(rmap, MAX77693_LED_REG_ITORCHTIMER, v);
+	if (ret < 0)
+		return ret;
+
+	v = max77693_flash_timeout_to_reg(p->timeout[FLASH]);
+	if (p->trigger_type[FLASH] == MAX77693_LED_TRIG_TYPE_LEVEL)
+		v |= MAX77693_FLASH_TIMER_LEVEL;
+	ret = max77693_write_reg(rmap, MAX77693_LED_REG_FLASH_TIMER, v);
+	if (ret < 0)
+		return ret;
+
+	if (p->low_vsys > 0)
+		v = max77693_led_vsys_to_reg(p->low_vsys) |
+						MAX77693_FLASH_LOW_BATTERY_EN;
+	else
+		v = 0;
+
+	ret = max77693_write_reg(rmap, MAX77693_LED_REG_MAX_FLASH1, v);
+	if (ret < 0)
+		return ret;
+	ret = max77693_write_reg(rmap, MAX77693_LED_REG_MAX_FLASH2, 0);
+	if (ret < 0)
+		return ret;
+
+	if (p->boost_mode[FLASH1] == MAX77693_LED_BOOST_FIXED ||
+	    p->boost_mode[FLASH2] == MAX77693_LED_BOOST_FIXED)
+		v = MAX77693_FLASH_BOOST_FIXED;
+	else
+		v = p->boost_mode[FLASH1] | (p->boost_mode[FLASH2] << 1);
+	if (p->boost_mode[FLASH1] && p->boost_mode[FLASH2])
+		v |= MAX77693_FLASH_BOOST_LEDNUM_2;
+	ret = max77693_write_reg(rmap, MAX77693_LED_REG_VOUT_CNTL, v);
+	if (ret < 0)
+		return ret;
+
+	v = max77693_led_vout_to_reg(p->boost_vout);
+	ret = max77693_write_reg(rmap, MAX77693_LED_REG_VOUT_FLASH1, v);
+	if (ret < 0)
+		return ret;
+
+	return max77693_set_mode(led, MAX77693_MODE_OFF);
+}
+
+/* LED subsystem callbacks */
+
+static void max77693_brightness_set_work(struct work_struct *work)
+{
+	struct max77693_led *led =
+		container_of(work, struct max77693_led, work_brightness_set);
+	int ret;
+
+	mutex_lock(&led->lock);
+
+	if (led->torch_brightness == 0) {
+		ret = max77693_clear_mode(led, MAX77693_MODE_TORCH);
+		if (ret < 0)
+			dev_dbg(&led->pdev->dev,
+				"Failed to clear torch mode (%d)\n",
+				ret);
+		goto unlock;
+	}
+
+	ret = max77693_set_torch_current(led, led->torch_brightness);
+	if (ret < 0) {
+		dev_dbg(&led->pdev->dev, "Failed to set torch current (%d)\n",
+			ret);
+		goto unlock;
+	}
+
+	ret = max77693_add_mode(led, MAX77693_MODE_TORCH);
+	if (ret < 0)
+		dev_dbg(&led->pdev->dev, "Failed to set torch mode (%d)\n",
+			ret);
+unlock:
+	mutex_unlock(&led->lock);
+}
+
+static void max77693_led_brightness_set(struct led_classdev *led_cdev,
+				enum led_brightness value)
+{
+	struct max77693_led *led = ldev_to_led(led_cdev);
+
+	led->torch_brightness = value;
+	schedule_work(&led->work_brightness_set);
+}
+
+static enum led_brightness max77693_led_brightness_get(
+						struct led_classdev *led_cdev)
+{
+	struct max77693_led *led = ldev_to_led(led_cdev);
+
+	return led->torch_brightness;
+}
+
+static int max77693_led_flash_brightness_get(struct led_classdev *led_cdev)
+{
+	struct max77693_led *led = ldev_to_led(led_cdev);
+
+	return led->flash_brightness;
+}
+
+static int max77693_led_flash_strobe_get(struct led_classdev *led_cdev)
+{
+	struct max77693_led *led = ldev_to_led(led_cdev);
+	int ret;
+
+	mutex_lock(&led->lock);
+	ret = max77693_strobe_status_get(led);
+	mutex_unlock(&led->lock);
+
+	return ret;
+}
+
+static int max77693_led_flash_fault_get(struct led_classdev *led_cdev,
+					unsigned int *fault)
+{
+	struct max77693_led *led = ldev_to_led(led_cdev);
+	u8 v;
+	int ret;
+
+	mutex_lock(&led->lock);
+
+	ret = max77693_int_flag_get(led, &v);
+	if (ret < 0)
+		goto unlock;
+
+	*fault = 0;
+
+	if (v & MAX77693_LED_FLASH_INT_FLED2_OPEN ||
+	    v & MAX77693_LED_FLASH_INT_FLED1_OPEN)
+		*fault |= LED_FAULT_OVER_VOLTAGE;
+	if (v & MAX77693_LED_FLASH_INT_FLED2_SHORT ||
+	    v & MAX77693_LED_FLASH_INT_FLED1_SHORT)
+		*fault |= LED_FAULT_SHORT_CIRCUIT;
+	if (v & MAX77693_LED_FLASH_INT_OVER_CURRENT)
+		*fault |= LED_FAULT_OVER_CURRENT;
+unlock:
+	mutex_unlock(&led->lock);
+	return ret;
+}
+
+static int max77693_led_flash_strobe_set(struct led_classdev *led_cdev,
+						bool state)
+{
+	struct max77693_led *led = ldev_to_led(led_cdev);
+	struct led_flash *flash = led->ldev.flash;
+	int ret;
+
+	mutex_lock(&led->lock);
+
+	if (flash->hw_triggered) {
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	if (!state) {
+		ret = max77693_clear_mode(led, MAX77693_MODE_FLASH);
+		goto unlock;
+	}
+
+	ret = max77693_add_mode(led, MAX77693_MODE_FLASH);
+	if (ret < 0)
+		goto unlock;
+unlock:
+	mutex_unlock(&led->lock);
+	return ret;
+}
+
+static int max77693_led_hw_trig_set(struct led_classdev *led_cdev,
+						bool enable)
+{
+	struct max77693_led *led = ldev_to_led(led_cdev);
+	int ret;
+
+	mutex_lock(&led->lock);
+
+	if (enable)
+		ret = max77693_add_mode(led, MAX77693_MODE_FLASH_EXTERNAL);
+	else
+		ret = max77693_clear_mode(led, MAX77693_MODE_FLASH_EXTERNAL);
+
+	mutex_unlock(&led->lock);
+
+	return ret;
+}
+
+static int max77693_led_flash_brightness_set(struct led_classdev *led_cdev,
+						int brightness)
+{
+	struct max77693_led *led = ldev_to_led(led_cdev);
+	int ret;
+
+	mutex_lock(&led->lock);
+
+	ret = max77693_set_flash_current(led, brightness);
+	if (ret < 0)
+		goto unlock;
+unlock:
+	mutex_unlock(&led->lock);
+	return ret;
+}
+
+static int max77693_led_flash_timeout_set(struct led_classdev *led_cdev,
+					unsigned long *timeout)
+{
+	struct max77693_led *led = ldev_to_led(led_cdev);
+	unsigned long delay_us;
+	int ret;
+
+	mutex_lock(&led->lock);
+
+	delay_us = *timeout * 1000;
+	if (delay_us < MAX77693_FLASH_TIMEOUT_MIN)
+		delay_us = MAX77693_FLASH_TIMEOUT_MIN;
+
+	ret = max77693_set_timeout(led, &delay_us);
+	if (ret < 0)
+		goto unlock;
+
+	*timeout = delay_us / 1000;
+unlock:
+	mutex_unlock(&led->lock);
+	return ret;
+}
+
+static void max77693_led_parse_dt(struct max77693_led_platform_data *p,
+			    struct device_node *node)
+{
+	of_property_read_u32_array(node, "maxim,iout", p->iout, 4);
+	of_property_read_u32_array(node, "maxim,trigger", p->trigger, 4);
+	of_property_read_u32_array(node, "maxim,trigger-type", p->trigger_type,
+									2);
+	of_property_read_u32_array(node, "maxim,timeout", p->timeout, 2);
+	of_property_read_u32_array(node, "maxim,boost-mode", p->boost_mode, 2);
+	of_property_read_u32(node, "maxim,boost-vout", &p->boost_vout);
+	of_property_read_u32(node, "maxim,vsys-min", &p->low_vsys);
+}
+
+static void clamp_align(u32 *v, u32 min, u32 max, u32 step)
+{
+	*v = clamp_val(*v, min, max);
+	if (step > 1)
+		*v = (*v - min) / step * step + min;
+}
+
+static void max77693_led_validate_platform_data(
+					struct max77693_led_platform_data *p)
+{
+	u32 max;
+	int i;
+
+	for (i = 0; i < 2; ++i)
+		clamp_align(&p->boost_mode[i], MAX77693_LED_BOOST_NONE,
+			    MAX77693_LED_BOOST_FIXED, 1);
+	/* boost, if enabled, should be the same on both leds */
+	if (p->boost_mode[0] != MAX77693_LED_BOOST_NONE &&
+	    p->boost_mode[1] != MAX77693_LED_BOOST_NONE)
+		p->boost_mode[1] = p->boost_mode[0];
+
+	max = (p->boost_mode[FLASH1] && p->boost_mode[FLASH2]) ?
+		  MAX77693_FLASH_IOUT_MAX_2LEDS : MAX77693_FLASH_IOUT_MAX_1LED;
+
+	clamp_align(&p->iout[FLASH1], MAX77693_FLASH_IOUT_MIN,
+		    max, MAX77693_FLASH_IOUT_STEP);
+	clamp_align(&p->iout[FLASH2], MAX77693_FLASH_IOUT_MIN,
+		    max, MAX77693_FLASH_IOUT_STEP);
+	clamp_align(&p->iout[TORCH1], MAX77693_TORCH_IOUT_MIN,
+		    MAX77693_TORCH_IOUT_MAX, MAX77693_TORCH_IOUT_STEP);
+	clamp_align(&p->iout[TORCH2], MAX77693_TORCH_IOUT_MIN,
+		    MAX77693_TORCH_IOUT_MAX, MAX77693_TORCH_IOUT_STEP);
+
+	for (i = 0; i < 4; ++i)
+		clamp_align(&p->trigger[i], 0, 7, 1);
+	for (i = 0; i < 2; ++i)
+		clamp_align(&p->trigger_type[i], MAX77693_LED_TRIG_TYPE_EDGE,
+			    MAX77693_LED_TRIG_TYPE_LEVEL, 1);
+
+	clamp_align(&p->timeout[FLASH], MAX77693_FLASH_TIMEOUT_MIN,
+		    MAX77693_FLASH_TIMEOUT_MAX, MAX77693_FLASH_TIMEOUT_STEP);
+
+	if (p->timeout[TORCH]) {
+		clamp_align(&p->timeout[TORCH], MAX77693_TORCH_TIMEOUT_MIN,
+			    MAX77693_TORCH_TIMEOUT_MAX, 1);
+		p->timeout[TORCH] = max77693_torch_timeout_from_reg(
+			      max77693_torch_timeout_to_reg(p->timeout[TORCH]));
+	}
+
+	clamp_align(&p->boost_vout, MAX77693_FLASH_VOUT_MIN,
+		    MAX77693_FLASH_VOUT_MAX, MAX77693_FLASH_VOUT_STEP);
+
+	if (p->low_vsys) {
+		clamp_align(&p->low_vsys, MAX77693_FLASH_VSYS_MIN,
+			    MAX77693_FLASH_VSYS_MAX, MAX77693_FLASH_VSYS_STEP);
+	}
+}
+
+static int max77693_led_get_platform_data(struct max77693_led *led)
+{
+	struct max77693_led_platform_data *p;
+	struct device *dev = &led->pdev->dev;
+
+	if (dev->of_node) {
+		p = devm_kzalloc(dev, sizeof(*led->pdata), GFP_KERNEL);
+		if (!p)
+			return -ENOMEM;
+		max77693_led_parse_dt(p, dev->of_node);
+	} else {
+		p = dev_get_platdata(dev);
+		if (!p)
+			return -ENODEV;
+	}
+	led->pdata = p;
+
+	max77693_led_validate_platform_data(p);
+
+	return 0;
+}
+
+#ifdef CONFIG_V4L2_FLASH
+static void max77693_init_ctrl_config(struct v4l2_flash_ctrl_config *config,
+						void *pdata)
+{
+	struct max77693_led_platform_data *p = pdata;
+	int max;
+
+	config->flags = V4L2_FLASH_CFG_LED_FLASH |
+			V4L2_FLASH_CFG_LED_TORCH |
+			V4L2_FLASH_CFG_STROBE_SOURCE_EXTERNAL |
+			V4L2_FLASH_CFG_FAULT_OVER_VOLTAGE |
+			V4L2_FLASH_CFG_FAULT_SHORT_CIRCUIT |
+			V4L2_FLASH_CFG_FAULT_OVER_CURRENT;
+
+	config->flash_timeout.min = MAX77693_FLASH_TIMEOUT_MIN / 1000;
+	config->flash_timeout.max = MAX77693_FLASH_TIMEOUT_MAX / 1000;
+	config->flash_timeout.step = 1;
+	config->flash_timeout.def = p->timeout[FLASH] / 1000;
+
+	max = (p->iout[FLASH1] + p->iout[FLASH2]) / 1000;
+	config->flash_intensity.min = MAX77693_FLASH_IOUT_MIN / 1000;
+	config->flash_intensity.max = max;
+	config->flash_intensity.step = 1;
+	config->flash_intensity.def = max;
+
+	max = (p->iout[TORCH1] + p->iout[TORCH2]) / 1000;
+	config->torch_intensity.min = 0;
+	config->torch_intensity.max = max;
+	config->torch_intensity.step = 1;
+	config->torch_intensity.def = max;
+}
+#else
+#define max77693_init_ctrl_config(config, pdata)
+#endif
+
+static struct led_flash led_flash = {
+	.ops = {
+		.brightness_set	= max77693_led_flash_brightness_set,
+		.brightness_get = max77693_led_flash_brightness_get,
+		.strobe_set	= max77693_led_flash_strobe_set,
+		.strobe_get	= max77693_led_flash_strobe_get,
+		.timeout_set	= max77693_led_flash_timeout_set,
+		.hw_trig_set	= max77693_led_hw_trig_set,
+		.fault_get	= max77693_led_flash_fault_get,
+	},
+	.max_timeout = MAX77693_FLASH_TIMEOUT_MAX / 1000,
+};
+
+static int max77693_led_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct max77693_dev *iodev = dev_get_drvdata(dev->parent);
+	struct max77693_led *led;
+	struct max77693_led_platform_data *p;
+	struct led_classdev *led_cdev;
+	struct v4l2_flash_ctrl_config config;
+	int ret;
+
+	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	led->pdev = pdev;
+	led_cdev = &led->ldev;
+	led->regmap = iodev->regmap;
+	platform_set_drvdata(pdev, led);
+	ret = max77693_led_get_platform_data(led);
+	if (ret < 0)
+		return -EINVAL;
+	p = led->pdata;
+
+	mutex_init(&led->lock);
+
+	INIT_WORK(&led->work_brightness_set, max77693_brightness_set_work);
+
+	/* register in the LED subsystem */
+	if (p->boost_mode[0] != MAX77693_LED_BOOST_NONE &&
+	    p->boost_mode[1] != MAX77693_LED_BOOST_NONE) {
+		led_cdev->max_brightness = MAX77693_TORCH_IOUT_MAX * 2 / 1000;
+		led_flash.max_brightness =
+				MAX77693_FLASH_IOUT_MAX_2LEDS * 2 / 1000;
+	} else {
+		led_cdev->max_brightness = MAX77693_TORCH_IOUT_MAX / 1000;
+		led_flash.max_brightness = MAX77693_FLASH_IOUT_MAX_1LED / 1000;
+	}
+
+	led_flash.timeout = p->timeout[FLASH] / 1000;
+	led_flash.brightness = (p->iout[FLASH1] + p->iout[FLASH2]) / 1000;
+
+	if ((p->trigger[FLASH1] & MAX77693_LED_TRIG_FLASH) ||
+	    (p->trigger[FLASH2] & MAX77693_LED_TRIG_FLASH))
+		led_flash.has_hw_trig = true;
+
+	led_cdev->name = MAX77693_LED_NAME;
+	led_cdev->brightness_set = max77693_led_brightness_set;
+	led_cdev->brightness_get = max77693_led_brightness_get;
+	led_cdev->max_brightness = led_cdev->max_brightness;
+	led_cdev->flash = &led_flash;
+
+	ret = led_classdev_register(&pdev->dev, led_cdev);
+	if (ret < 0)
+		return -EINVAL;
+
+	/* Initialize V4L2 Flash control configuration data. */
+	max77693_init_ctrl_config(&config, p);
+
+	/* Create V4L2 Flash subdev. */
+	ret = v4l2_flash_init(&led->ldev, &led->v4l2_flash, &config);
+	if (ret < 0)
+		goto error_flash_init;
+
+	ret = max77693_setup(led);
+
+	return ret;
+
+error_flash_init:
+	led_classdev_unregister(&led->ldev);
+	return ret;
+}
+
+static int max77693_led_remove(struct platform_device *pdev)
+{
+	struct max77693_led *led = platform_get_drvdata(pdev);
+
+	v4l2_flash_release(&led->v4l2_flash);
+	led_classdev_unregister(&led->ldev);
+
+	return 0;
+}
+
+static struct of_device_id max77693_led_dt_match[] = {
+	{.compatible = "maxim,max77693-flash"},
+	{},
+};
+
+static struct platform_driver max77693_led_driver = {
+	.probe		= max77693_led_probe,
+	.remove		= max77693_led_remove,
+	.driver		= {
+		.name	= "max77693-flash",
+		.owner	= THIS_MODULE,
+		.of_match_table = max77693_led_dt_match,
+	},
+};
+
+module_platform_driver(max77693_led_driver);
+
+MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
+MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
+MODULE_DESCRIPTION("Maxim MAX77693 led flash driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c
index c5535f0..d53c497 100644
--- a/drivers/mfd/max77693.c
+++ b/drivers/mfd/max77693.c
@@ -44,7 +44,8 @@
 static const struct mfd_cell max77693_devs[] = {
 	{ .name = "max77693-pmic", },
 	{ .name = "max77693-charger", },
-	{ .name = "max77693-flash", },
+	{ .name = "max77693-flash",
+	  .of_compatible = "maxim,max77693-flash", },
 	{ .name = "max77693-muic", },
 	{ .name = "max77693-haptic", },
 };
diff --git a/include/linux/mfd/max77693.h b/include/linux/mfd/max77693.h
index 3f3dc45..5859698 100644
--- a/include/linux/mfd/max77693.h
+++ b/include/linux/mfd/max77693.h
@@ -63,6 +63,37 @@ struct max77693_muic_platform_data {
 	int path_uart;
 };
 
+/* MAX77693 led flash */
+
+/* triggers */
+#define MAX77693_LED_TRIG_OFF	0
+#define MAX77693_LED_TRIG_FLASH	1
+#define MAX77693_LED_TRIG_TORCH	2
+#define MAX77693_LED_TRIG_EXT	(MAX77693_LED_TRIG_FLASH |\
+				MAX77693_LED_TRIG_TORCH)
+#define MAX77693_LED_TRIG_SOFT	4
+
+/* trigger types */
+#define MAX77693_LED_TRIG_TYPE_EDGE	0
+#define MAX77693_LED_TRIG_TYPE_LEVEL	1
+
+/* boost modes */
+#define MAX77693_LED_BOOST_NONE		0
+#define MAX77693_LED_BOOST_ADAPTIVE	1
+#define MAX77693_LED_BOOST_FIXED	2
+
+struct max77693_led_platform_data {
+	u32 iout[4];
+	u32 trigger[4];
+	u32 trigger_type[2];
+	u32 timeout[2];
+	u32 boost_mode[2];
+	u32 boost_vout;
+	u32 low_vsys;
+};
+
+/* MAX77693 */
+
 struct max77693_platform_data {
 	/* regulator data */
 	struct max77693_regulator_data *regulators;
@@ -70,5 +101,6 @@ struct max77693_platform_data {
 
 	/* muic data */
 	struct max77693_muic_platform_data *muic_data;
+	struct max77693_led_platform_data *led_data;
 };
 #endif	/* __LINUX_MFD_MAX77693_H */
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH/RFC v2 5/8] DT: Add documentation for the mfd Maxim max77693 flash cell
  2014-03-28 15:28 [PATCH/RFC v2 0/8] LED / flash API integration Jacek Anaszewski
                   ` (3 preceding siblings ...)
  2014-03-28 15:29 ` [PATCH/RFC v2 4/8] leds: Add support for max77693 mfd flash cell Jacek Anaszewski
@ 2014-03-28 15:29 ` Jacek Anaszewski
  2014-03-28 15:29 ` [PATCH/RFC v2 6/8] media: Add registration helpers for V4L2 flash sub-devices Jacek Anaszewski
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Jacek Anaszewski @ 2014-03-28 15:29 UTC (permalink / raw)
  To: linux-media, linux-leds, devicetree, linux-kernel
  Cc: s.nawrocki, a.hajda, kyungmin.park, Jacek Anaszewski,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

This patch adds device tree binding documentation for
the flash cell of the Maxim max77693 multifunctional device.

Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Pawel Moll <pawel.moll@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
Cc: Kumar Gala <galak@codeaurora.org>
---
 Documentation/devicetree/bindings/mfd/max77693.txt |   47 ++++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/Documentation/devicetree/bindings/mfd/max77693.txt b/Documentation/devicetree/bindings/mfd/max77693.txt
index 11921cc..7a9a4d5 100644
--- a/Documentation/devicetree/bindings/mfd/max77693.txt
+++ b/Documentation/devicetree/bindings/mfd/max77693.txt
@@ -27,6 +27,43 @@ Optional properties:
 
 	[*] refer Documentation/devicetree/bindings/regulator/regulator.txt
 
+Optional node:
+- led-flash : the LED submodule device node
+
+Required properties of "led-flash" node:
+- compatible : must be "maxim,max77693-flash"
+
+Optional properties of "led-flash" node:
+- maxim,iout : Array of four maximum intensities in microampheres of the current
+	in order: flash1, flash2, torch1, torch2.
+	Range:
+		flash - 15625 - 1000000,
+		torch - 15625 - 250000.
+- maxim,trigger : Array of flags indicating which trigger can activate given led
+	in order: flash1, flash2, torch1, torch2.
+	Possible flag values (can be combined):
+		1 - FLASH pin of the chip,
+		2 - TORCH pin of the chip,
+		4 - software via I2C command.
+- maxim,trigger-type : Array of trigger types in order: flash, torch.
+	Possible trigger types:
+		0 - Rising edge of the signal triggers the flash/torch,
+		1 - Signal level controls duration of the flash/torch.
+- maxim,timeout : Array of timeouts in microseconds after which leds are
+	turned off in order: flash, torch.
+	Range:
+		flash: 62500 - 1000000,
+		torch: 0 (no timeout) - 15728000.
+- maxim,boost-mode : Array of the flash boost modes in order: flash1, flash2.
+	Possible values:
+		0 - no boost,
+		1 - adaptive mode,
+		2 - fixed mode.
+- maxim,boost-vout : Output voltage of the boost module in millivolts.
+- maxim,vsys-min : Low input voltage level in millivolts. Flash is not fired
+	if chip estimates that system voltage could drop below this level due
+	to flash power consumption.
+
 Example:
 	max77693@66 {
 		compatible = "maxim,max77693";
@@ -52,4 +89,14 @@ Example:
 					regulator-boot-on;
 			};
 		};
+		led_flash: led-flash {
+			compatible = "maxim,max77693-flash";
+			maxim,iout = <625000 625000 250000 250000>;
+			maxim,trigger = <5 5 6 6>;
+			maxim,trigger-type = <0 1>;
+			maxim,timeout = <500000 0>;
+			maxim,boost-mode = <1 1>;
+			maxim,boost-vout = <5000>;
+			maxim,vsys-min = <2400>;
+		};
 	};
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH/RFC v2 6/8] media: Add registration helpers for V4L2 flash sub-devices
  2014-03-28 15:28 [PATCH/RFC v2 0/8] LED / flash API integration Jacek Anaszewski
                   ` (4 preceding siblings ...)
  2014-03-28 15:29 ` [PATCH/RFC v2 5/8] DT: Add documentation for the mfd Maxim max77693 " Jacek Anaszewski
@ 2014-03-28 15:29 ` Jacek Anaszewski
  2014-03-28 15:29 ` [PATCH/RFC v2 7/8] media: exynos4-is: Add support for v4l2-flash subdevs Jacek Anaszewski
  2014-03-28 15:29 ` [PATCH/RFC v2 8/8] DT: Add documentation for exynos4-is camera-flash property Jacek Anaszewski
  7 siblings, 0 replies; 11+ messages in thread
From: Jacek Anaszewski @ 2014-03-28 15:29 UTC (permalink / raw)
  To: linux-media, linux-leds, devicetree, linux-kernel
  Cc: s.nawrocki, a.hajda, kyungmin.park, Jacek Anaszewski

This patch adds helper functions for registering/unregistering
LED class flash devices as V4L2 subdevs. The functions should
be called from the LED subsystem device driver. In case the
Multimedia Framework support is disabled in the kernel config
the functions' empty versions will be used.

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/media/v4l2-core/Kconfig      |   10 ++
 drivers/media/v4l2-core/Makefile     |    2 +
 drivers/media/v4l2-core/v4l2-flash.c |  302 ++++++++++++++++++++++++++++++++++
 include/media/v4l2-flash.h           |  104 ++++++++++++
 4 files changed, 418 insertions(+)
 create mode 100644 drivers/media/v4l2-core/v4l2-flash.c
 create mode 100644 include/media/v4l2-flash.h

diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
index 2189bfb..07b53e5 100644
--- a/drivers/media/v4l2-core/Kconfig
+++ b/drivers/media/v4l2-core/Kconfig
@@ -35,6 +35,16 @@ config V4L2_MEM2MEM_DEV
         tristate
         depends on VIDEOBUF2_CORE
 
+# Used by LED subsystem flash drivers
+config V4L2_FLASH
+	tristate "Enable support for V4L2 Flash sub-devices"
+	depends on LEDS_CLASS_FLASH
+	---help---
+	  Say Y here to enable support for V4L2 Flash sub-devices, which allow
+	  to control LED class devices with V4L2 API.
+
+	  When in doubt, say N.
+
 # Used by drivers that need Videobuf modules
 config VIDEOBUF_GEN
 	tristate
diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
index c6ae7ba..8e37ab4 100644
--- a/drivers/media/v4l2-core/Makefile
+++ b/drivers/media/v4l2-core/Makefile
@@ -22,6 +22,8 @@ obj-$(CONFIG_VIDEO_TUNER) += tuner.o
 
 obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
 
+obj-$(CONFIG_V4L2_FLASH) += v4l2-flash.o
+
 obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
 obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
 obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o
diff --git a/drivers/media/v4l2-core/v4l2-flash.c b/drivers/media/v4l2-core/v4l2-flash.c
new file mode 100644
index 0000000..81370f9
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-flash.c
@@ -0,0 +1,302 @@
+/*
+ * V4L2 flash LED subdevice registration helpers.
+ *
+ *	Copyright (C) 2014 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 <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-flash.h>
+#include <media/v4l2-ioctl.h>
+
+static int v4l2_flash_g_volatile_ctrl(struct v4l2_ctrl *c)
+
+{
+	struct v4l2_flash *flash = v4l2_ctrl_to_v4l2_flash(c);
+	struct led_classdev *led_cdev = flash->led_cdev;
+	unsigned int fault;
+	int ret;
+
+	switch (c->id) {
+	case V4L2_CID_FLASH_TORCH_INTENSITY:
+		ret = led_update_brightness(led_cdev);
+		if (ret < 0)
+			return ret;
+		c->val = led_cdev->brightness;
+		return 0;
+	case V4L2_CID_FLASH_INTENSITY:
+		ret = led_update_flash_brightness(led_cdev);
+		if (ret < 0)
+			return ret;
+		c->val = led_cdev->flash->brightness;
+		return 0;
+	case V4L2_CID_FLASH_STROBE_STATUS:
+		ret = led_get_flash_strobe(led_cdev);
+		if (ret < 0)
+			return ret;
+		c->val = !!ret;
+		return 0;
+	case V4L2_CID_FLASH_FAULT:
+		/* led faults map directly to V4L2 flash faults */
+		ret = led_get_flash_fault(led_cdev, &fault);
+		if (!ret)
+			c->val = fault;
+		return ret;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int v4l2_flash_s_ctrl(struct v4l2_ctrl *c)
+{
+	struct v4l2_flash *flash = v4l2_ctrl_to_v4l2_flash(c);
+	struct led_classdev *led_cdev = flash->led_cdev;
+	bool hw_trig;
+	int ret;
+
+	switch (c->id) {
+	case V4L2_CID_FLASH_LED_MODE:
+		switch (c->val) {
+		case V4L2_FLASH_LED_MODE_NONE:
+			led_set_brightness(led_cdev, 0);
+			ret = led_set_flash_strobe(led_cdev, false);
+
+			mutex_lock(&led_cdev->led_lock);
+			led_sysfs_unlock(led_cdev);
+			mutex_unlock(&led_cdev->led_lock);
+			return ret;
+		case V4L2_FLASH_LED_MODE_FLASH:
+			mutex_lock(&led_cdev->led_lock);
+			led_sysfs_lock(led_cdev);
+			mutex_unlock(&led_cdev->led_lock);
+
+			/* Turn off torch LED */
+			led_set_brightness(led_cdev, 0);
+			hw_trig = (flash->ctrl.source->val ==
+					V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
+			return led_set_hw_triggered(led_cdev, hw_trig);
+		case V4L2_FLASH_LED_MODE_TORCH:
+			mutex_lock(&led_cdev->led_lock);
+			led_sysfs_lock(led_cdev);
+			mutex_unlock(&led_cdev->led_lock);
+
+			/* Stop flash strobing */
+			ret = led_set_flash_strobe(led_cdev, false);
+			if (ret)
+				return ret;
+			/* torch is always triggered by software */
+			ret = led_set_hw_triggered(led_cdev, false);
+			if (ret)
+				return ret;
+
+			led_set_brightness(led_cdev, flash->torch_intensity);
+			return ret;
+		}
+		break;
+	case V4L2_CID_FLASH_STROBE_SOURCE:
+		return led_set_hw_triggered(led_cdev,
+				c->val == V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
+	case V4L2_CID_FLASH_STROBE:
+		if (flash->ctrl.led_mode->val != V4L2_FLASH_LED_MODE_FLASH ||
+		   flash->ctrl.source->val != V4L2_FLASH_STROBE_SOURCE_SOFTWARE)
+			return -EINVAL;
+		return led_set_flash_strobe(led_cdev, true);
+	case V4L2_CID_FLASH_STROBE_STOP:
+		return led_set_flash_strobe(led_cdev, false);
+	case V4L2_CID_FLASH_TIMEOUT:
+		return led_set_flash_timeout(led_cdev,
+						(unsigned long *) &c->val);
+	case V4L2_CID_FLASH_INTENSITY:
+		return led_set_flash_brightness(led_cdev, c->val);
+	case V4L2_CID_FLASH_TORCH_INTENSITY:
+		flash->torch_intensity = c->val;
+		if (flash->ctrl.led_mode->val == V4L2_FLASH_LED_MODE_TORCH)
+			led_set_brightness(led_cdev, flash->torch_intensity);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops v4l2_flash_ctrl_ops = {
+	.g_volatile_ctrl = v4l2_flash_g_volatile_ctrl,
+	.s_ctrl = v4l2_flash_s_ctrl,
+};
+
+static int v4l2_flash_init_controls(struct v4l2_flash *flash,
+				struct v4l2_flash_ctrl_config *config)
+
+{
+	unsigned int mask;
+	struct v4l2_ctrl *ctrl;
+	struct v4l2_ctrl_config *ctrl_cfg;
+	bool has_flash = config->flags & V4L2_FLASH_CFG_LED_FLASH;
+	bool has_torch = config->flags & V4L2_FLASH_CFG_LED_TORCH;
+	int ret, max, num_ctrls;
+
+	if (!has_flash && !has_torch)
+		return -EINVAL;
+
+	num_ctrls = has_flash ? 8 : 2;
+	if (config->flags & V4L2_FLASH_CFG_FAULTS_MASK)
+		++num_ctrls;
+
+	v4l2_ctrl_handler_init(&flash->hdl, num_ctrls);
+
+	mask = 1 << V4L2_FLASH_LED_MODE_NONE;
+	if (has_flash)
+		mask |= 1 << V4L2_FLASH_LED_MODE_FLASH;
+	if (has_torch)
+		mask |= 1 << V4L2_FLASH_LED_MODE_TORCH;
+
+	/* Configure TORCH_INTENSITY ctrl */
+	ctrl_cfg = &config->torch_intensity;
+	ctrl = v4l2_ctrl_new_std(&flash->hdl, &v4l2_flash_ctrl_ops,
+				 V4L2_CID_FLASH_TORCH_INTENSITY,
+				 ctrl_cfg->min, ctrl_cfg->max,
+				 ctrl_cfg->step, ctrl_cfg->def);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+	if (has_flash) {
+		/* Configure FLASH_LED_MODE ctrl */
+		flash->ctrl.led_mode = v4l2_ctrl_new_std_menu(&flash->hdl,
+				&v4l2_flash_ctrl_ops, V4L2_CID_FLASH_LED_MODE,
+				V4L2_FLASH_LED_MODE_TORCH, ~mask,
+				V4L2_FLASH_LED_MODE_NONE);
+
+		/* Configure FLASH_STROBE_SOURCE ctrl */
+		mask = 1 << V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
+
+		if (config->flags & V4L2_FLASH_CFG_STROBE_SOURCE_EXTERNAL) {
+			mask |= 1 << V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
+			max = V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
+		} else {
+			max = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
+		}
+
+		flash->ctrl.source = v4l2_ctrl_new_std_menu(&flash->hdl,
+					&v4l2_flash_ctrl_ops,
+					V4L2_CID_FLASH_STROBE_SOURCE,
+					max,
+					~mask,
+					V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
+
+		/* Configure FLASH_STROBE ctrl */
+		ctrl = v4l2_ctrl_new_std(&flash->hdl, &v4l2_flash_ctrl_ops,
+					  V4L2_CID_FLASH_STROBE, 0, 1, 1, 0);
+
+		/* Configure FLASH_STROBE_STOP ctrl */
+		ctrl = v4l2_ctrl_new_std(&flash->hdl, &v4l2_flash_ctrl_ops,
+					  V4L2_CID_FLASH_STROBE_STOP,
+					  0, 1, 1, 0);
+
+		/* Configure FLASH_STROBE_STATUS ctrl */
+		ctrl = v4l2_ctrl_new_std(&flash->hdl, &v4l2_flash_ctrl_ops,
+					 V4L2_CID_FLASH_STROBE_STATUS,
+					 0, 1, 1, 1);
+		if (ctrl)
+			ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
+				       V4L2_CTRL_FLAG_READ_ONLY;
+
+		/* Configure FLASH_TIMEOUT ctrl */
+		ctrl_cfg = &config->flash_timeout;
+		ctrl = v4l2_ctrl_new_std(&flash->hdl, &v4l2_flash_ctrl_ops,
+					 V4L2_CID_FLASH_TIMEOUT, ctrl_cfg->min,
+					 ctrl_cfg->max, ctrl_cfg->step,
+					 ctrl_cfg->def);
+
+		/* Configure FLASH_INTENSITY ctrl */
+		ctrl_cfg = &config->flash_intensity;
+		ctrl = v4l2_ctrl_new_std(&flash->hdl, &v4l2_flash_ctrl_ops,
+					 V4L2_CID_FLASH_INTENSITY,
+					 ctrl_cfg->min, ctrl_cfg->max,
+					 ctrl_cfg->step, ctrl_cfg->def);
+		if (ctrl)
+			ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+		if (config->flags & V4L2_FLASH_CFG_FAULTS_MASK) {
+			/* Configure FLASH_FAULT ctrl */
+			ctrl = v4l2_ctrl_new_std(&flash->hdl,
+						 &v4l2_flash_ctrl_ops,
+						 V4L2_CID_FLASH_FAULT, 0,
+						 config->flags &
+						 V4L2_FLASH_CFG_FAULTS_MASK,
+						 0, 0);
+			if (ctrl)
+				ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
+					       V4L2_CTRL_FLAG_READ_ONLY;
+		}
+	}
+
+	if (flash->hdl.error) {
+		ret = flash->hdl.error;
+		goto error_free;
+	}
+
+	ret = v4l2_ctrl_handler_setup(&flash->hdl);
+	if (ret < 0)
+		goto error_free;
+
+	flash->subdev.ctrl_handler = &flash->hdl;
+
+	return 0;
+
+error_free:
+	v4l2_ctrl_handler_free(&flash->hdl);
+	return ret;
+}
+
+static struct v4l2_subdev_ops v4l2_flash_subdev_ops = {
+};
+
+int v4l2_flash_init(struct led_classdev *led_cdev, struct v4l2_flash *flash,
+				struct v4l2_flash_ctrl_config *config)
+{
+	struct v4l2_subdev *sd = &flash->subdev;
+	int ret;
+
+	flash->led_cdev = led_cdev;
+	sd->dev = led_cdev->dev->parent;
+	v4l2_subdev_init(sd, &v4l2_flash_subdev_ops);
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	snprintf(sd->name, sizeof(sd->name), led_cdev->name);
+
+	ret = v4l2_flash_init_controls(flash, config);
+	if (ret < 0)
+		goto err_init_controls;
+
+	ret = media_entity_init(&sd->entity, 0, NULL, 0);
+	if (ret < 0)
+		goto err_init_entity;
+
+	sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
+
+	ret = v4l2_async_register_subdev(sd);
+	if (ret < 0)
+		goto err_init_entity;
+
+	return 0;
+
+err_init_entity:
+	media_entity_cleanup(&sd->entity);
+err_init_controls:
+	v4l2_ctrl_handler_free(sd->ctrl_handler);
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(v4l2_flash_init);
+
+void v4l2_flash_release(struct v4l2_flash *flash)
+{
+	v4l2_ctrl_handler_free(flash->subdev.ctrl_handler);
+	v4l2_async_unregister_subdev(&flash->subdev);
+	media_entity_cleanup(&flash->subdev.entity);
+}
+EXPORT_SYMBOL_GPL(v4l2_flash_release);
diff --git a/include/media/v4l2-flash.h b/include/media/v4l2-flash.h
new file mode 100644
index 0000000..457ed15
--- /dev/null
+++ b/include/media/v4l2-flash.h
@@ -0,0 +1,104 @@
+/*
+ * V4L2 flash LED subdevice registration helpers.
+ *
+ *	Copyright (C) 2014 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 _V4L2_FLASH_H
+#define _V4L2_FLASH_H
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-event.h>
+#include <linux/leds_flash.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/leds.h>
+
+/*
+ * Supported led fault and mode bits -
+ * must be kept in synch with V4L2_FLASH_FAULT bits
+ */
+#define V4L2_FLASH_CFG_FAULT_OVER_VOLTAGE	(1 << 0)
+#define V4L2_FLASH_CFG_FAULT_TIMEOUT		(1 << 1)
+#define V4L2_FLASH_CFG_FAULT_OVER_TEMPERATURE	(1 << 2)
+#define V4L2_FLASH_CFG_FAULT_SHORT_CIRCUIT	(1 << 3)
+#define V4L2_FLASH_CFG_FAULT_OVER_CURRENT	(1 << 4)
+#define V4L2_FLASH_CFG_FAULT_UNDER_VOLTAGE	(1 << 6)
+#define V4L2_FLASH_CFG_FAULT_INPUT_VOLTAGE	(1 << 7)
+#define V4L2_FLASH_CFG_FAULT_LED_OVER_TEMPERATURE (1 << 8)
+#define V4L2_FLASH_CFG_FAULTS_MASK		0x1ff
+#define V4L2_FLASH_CFG_LED_FLASH		(1 << 9)
+#define V4L2_FLASH_CFG_LED_TORCH		(1 << 10)
+#define V4L2_FLASH_CFG_STROBE_SOURCE_EXTERNAL	(1 << 11)
+
+/* Flash control config data initializer */
+
+struct v4l2_flash {
+	struct led_classdev *led_cdev;
+	struct v4l2_subdev subdev;
+	struct v4l2_ctrl_handler hdl;
+
+	struct {
+		struct v4l2_ctrl *source;
+		struct v4l2_ctrl *led_mode;
+	} ctrl;
+
+	unsigned int torch_intensity;
+};
+
+struct v4l2_flash_ctrl_config {
+	struct v4l2_ctrl_config flash_timeout;
+	struct v4l2_ctrl_config flash_intensity;
+	struct v4l2_ctrl_config torch_intensity;
+	unsigned int flags;
+};
+
+static inline struct v4l2_flash *v4l2_subdev_to_flash(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct v4l2_flash, subdev);
+}
+
+static inline struct v4l2_flash *v4l2_ctrl_to_v4l2_flash(struct v4l2_ctrl *c)
+{
+	return container_of(c->handler, struct v4l2_flash, hdl);
+}
+
+#ifdef CONFIG_V4L2_FLASH
+/**
+ * v4l2_flash_init - initialize V4L2 flash led sub-device
+ * @led_cdev: the LED to create subdev upon
+ * @flash: a structure representing V4L2 flash led device
+ * @config: initial data for the flash led subdev controls
+ *
+ * Create V4L2 subdev wrapping given LED subsystem device.
+ */
+int v4l2_flash_init(struct led_classdev *led_cdev, struct v4l2_flash *flash,
+				struct v4l2_flash_ctrl_config *config);
+
+/**
+ * v4l2_flash_release - release V4L2 flash led sub-device
+ * @flash: a structure representing V4L2 flash led device
+ *
+ * Release V4L2 flash led subdev.
+ */
+void v4l2_flash_release(struct v4l2_flash *flash);
+#else
+static inline int v4l2_flash_init(struct led_classdev *led_cdev,
+				  struct v4l2_flash *flash,
+				  struct v4l2_flash_ctrl_config *config)
+{
+	return 0;
+}
+
+static inline void v4l2_flash_release(struct v4l2_flash *flash)
+{
+}
+#endif /* CONFIG_V4L2_FLASH */
+
+#endif /* _V4L2_FLASH_H */
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH/RFC v2 7/8] media: exynos4-is: Add support for v4l2-flash subdevs
  2014-03-28 15:28 [PATCH/RFC v2 0/8] LED / flash API integration Jacek Anaszewski
                   ` (5 preceding siblings ...)
  2014-03-28 15:29 ` [PATCH/RFC v2 6/8] media: Add registration helpers for V4L2 flash sub-devices Jacek Anaszewski
@ 2014-03-28 15:29 ` Jacek Anaszewski
  2014-03-28 15:29 ` [PATCH/RFC v2 8/8] DT: Add documentation for exynos4-is camera-flash property Jacek Anaszewski
  7 siblings, 0 replies; 11+ messages in thread
From: Jacek Anaszewski @ 2014-03-28 15:29 UTC (permalink / raw)
  To: linux-media, linux-leds, devicetree, linux-kernel
  Cc: s.nawrocki, a.hajda, kyungmin.park, Jacek Anaszewski

This patch adds suppport for external v4l2-flash devices.
The support includes parsing camera-flash DT property
and asynchronous subdevice registration.

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/media/platform/exynos4-is/media-dev.c |   34 ++++++++++++++++++++++---
 drivers/media/platform/exynos4-is/media-dev.h |   14 +++++++++-
 2 files changed, 43 insertions(+), 5 deletions(-)

diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index e62211a..3d750891 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -28,6 +28,7 @@
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <media/v4l2-async.h>
+#include <media/v4l2-flash.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-of.h>
 #include <media/media-device.h>
@@ -400,7 +401,7 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
 				   struct device_node *port,
 				   unsigned int index)
 {
-	struct device_node *rem, *ep, *np;
+	struct device_node *rem, *ep, *np, *fn;
 	struct fimc_source_info *pd;
 	struct v4l2_of_endpoint endpoint;
 	u32 val;
@@ -440,6 +441,14 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
 		return -EINVAL;
 	}
 
+	fn = of_parse_phandle(rem, "camera-flash", 0);
+	if (fn) {
+		fmd->flash[fmd->num_flashes].asd.match_type =
+							V4L2_ASYNC_MATCH_OF;
+		fmd->flash[fmd->num_flashes].asd.match.of.node = fn;
+		fmd->num_flashes++;
+	}
+
 	if (fimc_input_is_parallel(endpoint.base.port)) {
 		if (endpoint.bus_type == V4L2_MBUS_PARALLEL)
 			pd->sensor_bus_type = FIMC_BUS_TYPE_ITU_601;
@@ -1531,6 +1540,15 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
 	struct fimc_sensor_info *si = NULL;
 	int i;
 
+	/* Register flash subdev if detected any */
+	for (i = 0; i < ARRAY_SIZE(fmd->flash); i++) {
+		if (fmd->flash[i].asd.match.of.node == subdev->dev->of_node) {
+			fmd->flash[i].v4l2_flash = v4l2_subdev_to_flash(subdev);
+			fmd->num_flashes++;
+			return 0;
+		}
+	}
+
 	/* Find platform data for this sensor subdev */
 	for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++)
 		if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node)
@@ -1578,7 +1596,7 @@ static int fimc_md_probe(struct platform_device *pdev)
 	struct device *dev = &pdev->dev;
 	struct v4l2_device *v4l2_dev;
 	struct fimc_md *fmd;
-	int ret;
+	int i, ret;
 
 	fmd = devm_kzalloc(dev, sizeof(*fmd), GFP_KERNEL);
 	if (!fmd)
@@ -1646,6 +1664,12 @@ static int fimc_md_probe(struct platform_device *pdev)
 			mutex_unlock(&fmd->media_dev.graph_mutex);
 			goto err_m_ent;
 		}
+
+		if (dev->of_node) {
+			for (i = 0; i < fmd->num_flashes; ++i)
+				fmd->async_subdevs[fmd->num_sensors + i] =
+						&fmd->flash[i].asd;
+		}
 	}
 
 	mutex_unlock(&fmd->media_dev.graph_mutex);
@@ -1664,12 +1688,14 @@ static int fimc_md_probe(struct platform_device *pdev)
 		goto err_attr;
 	}
 
-	if (fmd->num_sensors > 0) {
+	if (fmd->num_sensors > 0 || fmd->num_flashes > 0) {
 		fmd->subdev_notifier.subdevs = fmd->async_subdevs;
-		fmd->subdev_notifier.num_subdevs = fmd->num_sensors;
+		fmd->subdev_notifier.num_subdevs = fmd->num_sensors +
+							fmd->num_flashes;
 		fmd->subdev_notifier.bound = subdev_notifier_bound;
 		fmd->subdev_notifier.complete = subdev_notifier_complete;
 		fmd->num_sensors = 0;
+		fmd->num_flashes = 0;
 
 		ret = v4l2_async_notifier_register(&fmd->v4l2_dev,
 						&fmd->subdev_notifier);
diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
index ee1e251..9af9de8 100644
--- a/drivers/media/platform/exynos4-is/media-dev.h
+++ b/drivers/media/platform/exynos4-is/media-dev.h
@@ -18,6 +18,7 @@
 #include <media/media-device.h>
 #include <media/media-entity.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-flash.h>
 #include <media/v4l2-subdev.h>
 #include <media/s5p_fimc.h>
 
@@ -33,6 +34,8 @@
 #define PINCTRL_STATE_IDLE	"idle"
 
 #define FIMC_MAX_SENSORS	4
+#define FIMC_MAX_FLASHES	1
+#define FIMC_MAX_ASYNC_SUBDEVS	(FIMC_MAX_SENSORS + FIMC_MAX_FLASHES)
 #define FIMC_MAX_CAMCLKS	2
 #define DEFAULT_SENSOR_CLK_FREQ	24000000U
 
@@ -93,6 +96,11 @@ struct fimc_sensor_info {
 	struct fimc_dev *host;
 };
 
+struct fimc_flash_info {
+	struct v4l2_flash *v4l2_flash;
+	struct v4l2_async_subdev asd;
+};
+
 struct cam_clk {
 	struct clk_hw hw;
 	struct fimc_md *fmd;
@@ -104,6 +112,8 @@ struct cam_clk {
  * @csis: MIPI CSIS subdevs data
  * @sensor: array of registered sensor subdevs
  * @num_sensors: actual number of registered sensors
+ * @flash: array of registered flash subdevs
+ * @num_flashes: actual number of registered flashes
  * @camclk: external sensor clock information
  * @fimc: array of registered fimc devices
  * @fimc_is: fimc-is data structure
@@ -123,6 +133,8 @@ struct fimc_md {
 	struct fimc_csis_info csis[CSIS_MAX_ENTITIES];
 	struct fimc_sensor_info sensor[FIMC_MAX_SENSORS];
 	int num_sensors;
+	struct fimc_flash_info flash[FIMC_MAX_FLASHES];
+	int num_flashes;
 	struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS];
 	struct clk *wbclk[FIMC_MAX_WBCLKS];
 	struct fimc_lite *fimc_lite[FIMC_LITE_MAX_DEVS];
@@ -149,7 +161,7 @@ struct fimc_md {
 	} clk_provider;
 
 	struct v4l2_async_notifier subdev_notifier;
-	struct v4l2_async_subdev *async_subdevs[FIMC_MAX_SENSORS];
+	struct v4l2_async_subdev *async_subdevs[FIMC_MAX_ASYNC_SUBDEVS];
 
 	bool user_subdev_api;
 	spinlock_t slock;
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH/RFC v2 8/8] DT: Add documentation for exynos4-is camera-flash property
  2014-03-28 15:28 [PATCH/RFC v2 0/8] LED / flash API integration Jacek Anaszewski
                   ` (6 preceding siblings ...)
  2014-03-28 15:29 ` [PATCH/RFC v2 7/8] media: exynos4-is: Add support for v4l2-flash subdevs Jacek Anaszewski
@ 2014-03-28 15:29 ` Jacek Anaszewski
  7 siblings, 0 replies; 11+ messages in thread
From: Jacek Anaszewski @ 2014-03-28 15:29 UTC (permalink / raw)
  To: linux-media, linux-leds, devicetree, linux-kernel
  Cc: s.nawrocki, a.hajda, kyungmin.park, Jacek Anaszewski,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Pawel Moll <pawel.moll@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
Cc: Kumar Gala <galak@codeaurora.org>
---
 .../devicetree/bindings/media/samsung-fimc.txt     |    3 +++
 1 file changed, 3 insertions(+)

diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt
index 922d6f8..88f9287 100644
--- a/Documentation/devicetree/bindings/media/samsung-fimc.txt
+++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt
@@ -108,6 +108,8 @@ Image sensor nodes
 The sensor device nodes should be added to their control bus controller (e.g.
 I2C0) nodes and linked to a port node in the csis or the parallel-ports node,
 using the common video interfaces bindings, defined in video-interfaces.txt.
+If the sensor device has a led flash device associated with it then its phandle
+should be assigned to the camera-flash property.
 
 Example:
 
@@ -125,6 +127,7 @@ Example:
 			clock-frequency = <24000000>;
 			clocks = <&camera 1>;
 			clock-names = "mclk";
+			camera-flash = <&led_flash>;
 
 			port {
 				s5k6aa_ep: endpoint {
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH/RFC v2 4/8] leds: Add support for max77693 mfd flash cell
  2014-03-28 15:29 ` [PATCH/RFC v2 4/8] leds: Add support for max77693 mfd flash cell Jacek Anaszewski
@ 2014-03-31  7:48   ` Lee Jones
  0 siblings, 0 replies; 11+ messages in thread
From: Lee Jones @ 2014-03-31  7:48 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-media, linux-leds, devicetree, linux-kernel, s.nawrocki,
	a.hajda, kyungmin.park, Bryan Wu, Richard Purdie, SangYoung Son,
	Samuel Ortiz

On Fri, 28 Mar 2014, Jacek Anaszewski wrote:

> This patch adds led-flash support to Maxim max77693 chipset.
> Device can be exposed to user space through LED subsystem
> sysfs interface or through V4L2 subdevice when the support
> for Multimedia Framework is enabled. Device supports up to
> two leds which can work in flash and torch mode. Leds can
> be triggered externally or by software.
> 
> Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> Cc: Bryan Wu <cooloney@gmail.com>
> Cc: Richard Purdie <rpurdie@rpsys.net>
> Cc: SangYoung Son <hello.son@smasung.com>
> Cc: Samuel Ortiz <sameo@linux.intel.com>
> Cc: Lee Jones <lee.jones@linaro.org>
> ---
>  drivers/leds/Kconfig         |   10 +
>  drivers/leds/Makefile        |    1 +
>  drivers/leds/leds-max77693.c |  864 ++++++++++++++++++++++++++++++++++++++++++
>  drivers/mfd/max77693.c       |    3 +-
>  include/linux/mfd/max77693.h |   32 ++
>  5 files changed, 909 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/leds/leds-max77693.c

[...]

> diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c
> index c5535f0..d53c497 100644
> --- a/drivers/mfd/max77693.c
> +++ b/drivers/mfd/max77693.c
> @@ -44,7 +44,8 @@
>  static const struct mfd_cell max77693_devs[] = {
>  	{ .name = "max77693-pmic", },
>  	{ .name = "max77693-charger", },
> -	{ .name = "max77693-flash", },
> +	{ .name = "max77693-flash",
> +	  .of_compatible = "maxim,max77693-flash", },

On one line please.

>  	{ .name = "max77693-muic", },
>  	{ .name = "max77693-haptic", },
>  };
> diff --git a/include/linux/mfd/max77693.h b/include/linux/mfd/max77693.h
> index 3f3dc45..5859698 100644
> --- a/include/linux/mfd/max77693.h
> +++ b/include/linux/mfd/max77693.h
> @@ -63,6 +63,37 @@ struct max77693_muic_platform_data {
>  	int path_uart;
>  };
>  
> +/* MAX77693 led flash */
> +
> +/* triggers */
> +#define MAX77693_LED_TRIG_OFF	0
> +#define MAX77693_LED_TRIG_FLASH	1
> +#define MAX77693_LED_TRIG_TORCH	2
> +#define MAX77693_LED_TRIG_EXT	(MAX77693_LED_TRIG_FLASH |\
> +				MAX77693_LED_TRIG_TORCH)
> +#define MAX77693_LED_TRIG_SOFT	4
> +
> +/* trigger types */
> +#define MAX77693_LED_TRIG_TYPE_EDGE	0
> +#define MAX77693_LED_TRIG_TYPE_LEVEL	1
> +
> +/* boost modes */
> +#define MAX77693_LED_BOOST_NONE		0
> +#define MAX77693_LED_BOOST_ADAPTIVE	1
> +#define MAX77693_LED_BOOST_FIXED	2

I think it would be better to enum all of the above.

> +struct max77693_led_platform_data {
> +	u32 iout[4];
> +	u32 trigger[4];
> +	u32 trigger_type[2];
> +	u32 timeout[2];
> +	u32 boost_mode[2];
> +	u32 boost_vout;
> +	u32 low_vsys;
> +};

I'll leave this LED stuff to the expert(s).

[...]

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH/RFC v2 1/8] leds: Add sysfs and kernel internal API for flash LEDs
  2014-03-28 15:28 ` [PATCH/RFC v2 1/8] leds: Add sysfs and kernel internal API for flash LEDs Jacek Anaszewski
@ 2014-03-31 10:16   ` Richard Purdie
  0 siblings, 0 replies; 11+ messages in thread
From: Richard Purdie @ 2014-03-31 10:16 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-media, linux-leds, devicetree, linux-kernel, s.nawrocki,
	a.hajda, kyungmin.park, Bryan Wu

On Fri, 2014-03-28 at 16:28 +0100, Jacek Anaszewski wrote:
> Some LED devices support two operation modes - torch and
> flash. This patch provides support for flash LED devices
> in the LED subsystem by introducing new sysfs attributes
> and kernel internal interface. The attributes being
> introduced are: flash_brightness, flash_strobe, flash_timeout,
> max_flash_timeout, max_flash_brightness, flash_fault and
> hw_triggered. All the flash related features are placed
> in a separate module.
> The modifications aim to be compatible with V4L2 framework
> requirements related to the flash devices management. The
> design assumes that V4L2 sub-device can take of the LED class
> device control and communicate with it through the kernel
> internal interface. The LED sysfs interface is made
> unavailable then.
> 
> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> Cc: Bryan Wu <cooloney@gmail.com>
> Cc: Richard Purdie <rpurdie@rpsys.net>
> ---
>  drivers/leds/Kconfig        |    8 +
>  drivers/leds/Makefile       |    1 +
>  drivers/leds/led-class.c    |   56 +++++--
>  drivers/leds/led-flash.c    |  375 +++++++++++++++++++++++++++++++++++++++++++
>  drivers/leds/led-triggers.c |   16 +-
>  drivers/leds/leds.h         |    3 +
>  include/linux/leds.h        |   24 ++-
>  include/linux/leds_flash.h  |  189 ++++++++++++++++++++++
>  8 files changed, 658 insertions(+), 14 deletions(-)
>  create mode 100644 drivers/leds/led-flash.c
>  create mode 100644 include/linux/leds_flash.h
> 
> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> index 2062682..1e1c81f 100644
> --- a/drivers/leds/Kconfig
> +++ b/drivers/leds/Kconfig
> @@ -19,6 +19,14 @@ 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 "Flash LEDs Support"
> +	depends on LEDS_CLASS
> +	help
> +	  This option enables support for flash LED devices. Say Y if you
> +	  want to use flash specific features of a LED device, if they
> +	  are supported.
> +
>  comment "LED drivers"
>  
>  config LEDS_88PM860X
> diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
> index 3cd76db..8861b86 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-flash.o
>  obj-$(CONFIG_LEDS_TRIGGERS)		+= led-triggers.o
>  
>  # LED Platform Drivers
> diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
> index f37d63c..5bac140 100644
> --- a/drivers/leds/led-class.c
> +++ b/drivers/leds/led-class.c
> @@ -9,16 +9,18 @@
>   * published by the Free Software Foundation.
>   */
>  
> -#include <linux/module.h>
> -#include <linux/kernel.h>
> +#include <linux/ctype.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
>  #include <linux/init.h>
> +#include <linux/kernel.h>
>  #include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
>  #include <linux/spinlock.h>
> -#include <linux/device.h>
>  #include <linux/timer.h>
> -#include <linux/err.h>
> -#include <linux/ctype.h>
>  #include <linux/leds.h>
> +#include <linux/leds_flash.h>
>  #include "leds.h"
>  
>  static struct class *leds_class;
> @@ -45,28 +47,38 @@ static ssize_t brightness_store(struct device *dev,
>  {
>  	struct led_classdev *led_cdev = dev_get_drvdata(dev);
>  	unsigned long state;
> -	ssize_t ret = -EINVAL;
> +	ssize_t ret;
> +
> +	mutex_lock(&led_cdev->led_lock);
> +
> +	if (led_sysfs_is_locked(led_cdev)) {
> +		ret = -EBUSY;
> +		goto unlock;
> +	}
>  
>  	ret = kstrtoul(buf, 10, &state);
>  	if (ret)
> -		return ret;
> +		goto unlock;
>  
>  	if (state == LED_OFF)
>  		led_trigger_remove(led_cdev);
>  	__led_set_brightness(led_cdev, state);
> +	ret = size;
>  
> -	return size;
> +unlock:
> +	mutex_unlock(&led_cdev->led_lock);
> +	return ret;
>  }
>  static DEVICE_ATTR_RW(brightness);
>  
> -static ssize_t led_max_brightness_show(struct device *dev,
> +static ssize_t max_brightness_show(struct device *dev,
>  		struct device_attribute *attr, char *buf)
>  {
>  	struct led_classdev *led_cdev = dev_get_drvdata(dev);
>  
>  	return sprintf(buf, "%u\n", led_cdev->max_brightness);
>  }
> -static DEVICE_ATTR(max_brightness, 0444, led_max_brightness_show, NULL);
> +static DEVICE_ATTR_RO(max_brightness);
>  
>  #ifdef CONFIG_LEDS_TRIGGERS
>  static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
> @@ -173,7 +185,15 @@ EXPORT_SYMBOL_GPL(led_classdev_suspend);
>   */
>  void led_classdev_resume(struct led_classdev *led_cdev)
>  {
> +	struct led_flash *flash = led_cdev->flash;
> +
>  	led_cdev->brightness_set(led_cdev, led_cdev->brightness);
> +	if (flash) {
> +		call_flash_op(brightness_set, led_cdev,
> +				flash->brightness);
> +		call_flash_op(timeout_set, led_cdev,
> +				&flash->timeout);
> +	}
>  	led_cdev->flags &= ~LED_SUSPENDED;
>  }
>  EXPORT_SYMBOL_GPL(led_classdev_resume);
> @@ -210,14 +230,24 @@ static const struct dev_pm_ops leds_class_dev_pm_ops = {
>   */
>  int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
>  {
> +	int ret;
> +
>  	led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
>  				      "%s", led_cdev->name);
>  	if (IS_ERR(led_cdev->dev))
>  		return PTR_ERR(led_cdev->dev);
>  
> +	ret = led_classdev_init_flash(led_cdev);
> +	if (ret < 0) {
> +		dev_dbg(parent,
> +			"Flash LED initialization failed for the %s\n device", led_cdev->name);
> +		goto error_flash_init;
> +	}
> +

Thanks for moving things to the separate file, I think it is cleaner.
The trouble is that because of the above call to
led_classdev_init_flash(), you can't have the led core loaded without
the flash module too. There are a few other calls which also give the
same end result.

I guess what I'm wondering is whether this can work the other way
around, the flash class wraps around the led core and extends it for
those devices with flash capable LEDs? There may be some internals need
accessing from led-core but it would mean you can have one module loaded
without the other. This would mean a new registration function for flash
capable LED devices but in reality that shouldn't be much of an issue?

I appreciate this may seem like a small issue since RAM is cheap and who
cares about the kernel size, but these small pieces all mount up. I wish
more people thought about how to make the kernel modules work more
effectively...

Cheers,

Richard




^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2014-03-31 10:16 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-03-28 15:28 [PATCH/RFC v2 0/8] LED / flash API integration Jacek Anaszewski
2014-03-28 15:28 ` [PATCH/RFC v2 1/8] leds: Add sysfs and kernel internal API for flash LEDs Jacek Anaszewski
2014-03-31 10:16   ` Richard Purdie
2014-03-28 15:28 ` [PATCH/RFC v2 2/8] leds: Improve and export led_update_brightness function Jacek Anaszewski
2014-03-28 15:29 ` [PATCH/RFC v2 3/8] Documentation: leds: Add description of flash mode Jacek Anaszewski
2014-03-28 15:29 ` [PATCH/RFC v2 4/8] leds: Add support for max77693 mfd flash cell Jacek Anaszewski
2014-03-31  7:48   ` Lee Jones
2014-03-28 15:29 ` [PATCH/RFC v2 5/8] DT: Add documentation for the mfd Maxim max77693 " Jacek Anaszewski
2014-03-28 15:29 ` [PATCH/RFC v2 6/8] media: Add registration helpers for V4L2 flash sub-devices Jacek Anaszewski
2014-03-28 15:29 ` [PATCH/RFC v2 7/8] media: exynos4-is: Add support for v4l2-flash subdevs Jacek Anaszewski
2014-03-28 15:29 ` [PATCH/RFC v2 8/8] DT: Add documentation for exynos4-is camera-flash property Jacek Anaszewski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).