linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH/RFC v4 00/21] LED / flash API integration
@ 2014-07-11 14:04 Jacek Anaszewski
  2014-07-11 14:04 ` [PATCH/RFC v4 01/21] leds: make brightness type consistent across whole subsystem Jacek Anaszewski
                   ` (22 more replies)
  0 siblings, 23 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski

This is is the fourth 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-20140707

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, if the driver
	  declares related features:
            - flash_brightness
            - max_flash_brightness
            - indicator_brightness
            - max_indicator_brightness
            - flash_timeout
            - max_flash_timeout
            - flash_strobe
            - external_strobe
            - flash_fault
          and exposes kernel internal API:
            - led_classdev_flash_register
            - led_classdev_flash_unregister
            - led_set_flash_strobe
            - led_get_flash_strobe
            - led_set_flash_brightness
            - led_update_flash_brightness
            - led_set_indicator_brightness
            - led_update_indicator_brightness
            - led_set_flash_timeout
            - led_get_flash_fault
            - led_set_external_strobe
            - led_sysfs_lock
            - led_sysfs_unlock
        * added Flash Manager functionality, available when
          led_flash module is enable in the config;
          if the device tree node of a flash led device contains
          relevant subnodes, it registers following sysfs attributes:
            - strobe_provider
            - strobe_providerN
            - blocking_strobe
          following kernel internal API is exposed by the flash manager:
            - led_flash_manager_register_flash
            - led_flash_manager_unregister_flash 
            - led_flash_manager_setup_strobe
            - led_flash_manager_bind_async_mux
            - led_flash_manager_unbind_async_mux
    - 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
    - Addition of a LED Flash Class driver for the flash cell of
      the MAX77693 mfd
    - Addition of a LED Flash Class driver for the AAT1290 current
      regulator for flash leds along with its DT binding for the
      exynos4412-trats2 board, where standalone multiplexer is
      used for modifying strobe signal routing - either from the SoC
      GPIO or from a camera sensor. This arrangment is handled
      by the newly introduced Flash Manager functionality.
    - Update of the max77693.txt DT bindings documentation
    - Update of the common leds DT bindings documentation

================
Changes since v2
================

    - refactored the code so that it is possible to build
      led-core without led-flash module
    - added v4l2-flash ops which slackens dependency from
      the led-flash module
    - implemented led_clamp_align_val function and led_ctrl
      structure which allows to align led control values
      in the manner compatible with V4L2 Flash controls;
      the flash brightness and timeout units have been defined
      as microamperes and microseconds respectively to properly
      support devices which define current and time levels
      as fractions of 1/1000.
    - added support for the flash privacy leds
    - modified LED sysfs locking mechanism - now it locks/unlocks
      the interface on V4L2 Flash sub-device file open/close
    - changed hw_triggered attribute name to external_strobe,
      which maps on the V4L2_FLASH_STROBE_SOURCE_EXTERNAL name 
      more intuitively
    - made external_strobe and indicator related sysfs attributes
      created optionally only if related features are declared
      by the led device driver
    - removed from the series patches modifying exynos4-is media
      controller - a proposal for "flash manager" which will take
      care of flash devices registration is due to be submitted
    - removed modifications to the LED class devices documentation,
      it will be covered after the whole functionality is accepted

================
Changes since v3
================

    - added Flash Manager feature
      - added generic LED Flash Class gpio mux driver
      - added sample async mux driver
      - added of helpers for parsing Flash Manager related
        device tree data
    - added V4L2_CID_FLASH_STROBE_PROVIDER control
    - introduced struct led_classdev_flash, which wrapps
      struct led_classdev
    - made all flash ops, except strobe_set, optional; if an op
      is absent the related sysfs attribute isn't created
    - added LED_DEV_CAP* flags
    - modified v4l2-flash helpers to create v4l2_device
      for v4l2_flash subdevices to register in it
    - modified max77693-flash driver and its DT bindings
      to allow for registering either one or two LED Flash
      Class devices, depending on the device tree settings.
    - added new API for setting torch_brightness
    - extended leds common DT binding documentation

Issues:

1) Who should register V4L2 Flash sub-device?

LED Flash Class devices, after introduction of the Flash Manager,
are not tightly coupled with any media controller. They are maintained
by the Flash Manager and made available for dynamic assignment to
any media system they are connected to through multiplexing devices.

In the proposed rough solution, when support for V4L2 Flash sub-devices
is enabled, there is a v4l2_device created for them to register in.
This however implies that V4L2 Flash device will not be available
in any media controller, which calls its existence into question.

Therefore I'd like to consult possible ways of solving this issue.
The option I see is implementing a mechanism for moving V4L2 Flash
sub-devices between media controllers. A V4L2 Flash sub-device
would initially be assigned to one media system in the relevant
device tree binding, but it could be dynamically reassigned to
the other one. However I'm not sure if media controller design
is prepared for dynamic modifications of its graph and how many
modifications in the existing drivers this solution would require.

2) Consequences of locking the Flash Manager during flash strobe

In case a LED Flash Class device depends on muxes involved in
routing the other LED Flash Class device's strobe signals,
the Flash Manager must be locked for the time of strobing
to prevent reconfiguration of the strobe signal routing
by the other device.

A blocking_strobe sysfs attribute was added to indicate whether
this is the case for the device.

Nonetheless, this modifies behaviour of led_set_external_strobe
API, which also must block the caller for the time of strobing
to protect the strobe signal routing while the external strobe
signal provider asserts the flash_en pin.

Use case for user space application would be following in this case:
  - spawn a new thread in which external strobe is activated
  - in the parent thread instruct the external strobe provider
    to strobe the flash

As an improvement there could be an op added for notifying
the application that the strobe signal routing has been
set up. It would have to be called from the function
led_flash_manager_setup_strobe after led_flash_manager_set_external_strobe
returns.

The blocking_strobe attribute would have to be also
mapped to a new V4L2 Flash control. Unfortunately it poses
a problem for the existing users of the V4L2 Flash API
which are not aware that setting V4L2_CID_FLASH_STROBE_SOURCE
may be blocking.

As a solution led_set_external_strobe API could be extended
by a parameter telling whether the caller is prepared for
the blocking call. led_flash_manager_setup_strobe should
be extended accordingly then.
In V4L2 "V4L2_FLASH_STROBE_SOURCE_EXTERNAL_BLOCKING" menu item
could be added to handle this.
With existing V4L2_FLASH_STROBE_SOURCE_EXTERNAL the flash manager
wouldn't protect muxes against reconfiguration.

TODO:
  - switch to using V4L2 array controls 
  - add s_power op to the LED Flash Class


I will be off-line for three weeks from now on and will respond
to any questions after getting back.


Thanks,
Jacek Anaszewski

Jacek Anaszewski (21):
  leds: make brightness type consistent across whole subsystem
  leds: implement sysfs interface locking mechanism
  leds: Improve and export led_update_brightness
  leds: Reorder include directives
  leds: avoid using deprecated DEVICE_ATTR macro
  leds: add API for setting torch brightness
  of: add of_node_ncmp wrapper
  leds: Add sysfs and kernel internal API for flash LEDs
  Documentation: leds: Add description of LED Flash Class extension
  Documentation: leds: add exemplary asynchronous mux driver
  DT: leds: Add flash led devices related properties
  DT: Add documentation for LED Class Flash Manger
  v4l2-device: add v4l2_device_register_subdev_node API
  v4l2-ctrls: add control for flash strobe signal providers
  media: Add registration helpers for V4L2 flash
  leds: Add support for max77693 mfd flash cell
  DT: Add documentation for the mfd Maxim max77693
  leds: Add driver for AAT1290 current regulator
  of: Add Skyworks Solutions, Inc. vendor prefix
  DT: Add documentation for the Skyworks AAT1290
  ARM: dts: add aat1290 current regulator device node

 Documentation/DocBook/media/v4l/controls.xml       |   11 +
 Documentation/devicetree/bindings/leds/common.txt  |   16 +
 .../devicetree/bindings/leds/leds-aat1290.txt      |   17 +
 .../bindings/leds/leds-flash-manager.txt           |  165 +++
 Documentation/devicetree/bindings/mfd/max77693.txt |   62 ++
 .../devicetree/bindings/vendor-prefixes.txt        |    1 +
 Documentation/leds/leds-async-mux.c                |   65 ++
 Documentation/leds/leds-class-flash.txt            |  126 +++
 arch/arm/boot/dts/exynos4412-trats2.dts            |   24 +
 drivers/leds/Kconfig                               |   28 +
 drivers/leds/Makefile                              |    7 +
 drivers/leds/led-class-flash.c                     |  715 +++++++++++++
 drivers/leds/led-class.c                           |   58 +-
 drivers/leds/led-core.c                            |   51 +-
 drivers/leds/led-flash-gpio-mux.c                  |  102 ++
 drivers/leds/led-flash-manager.c                   |  698 +++++++++++++
 drivers/leds/led-triggers.c                        |   16 +-
 drivers/leds/leds-aat1290.c                        |  455 +++++++++
 drivers/leds/leds-max77693.c                       | 1070 ++++++++++++++++++++
 drivers/leds/of_led_flash_manager.c                |  155 +++
 drivers/media/v4l2-core/Kconfig                    |   11 +
 drivers/media/v4l2-core/Makefile                   |    2 +
 drivers/media/v4l2-core/v4l2-ctrls.c               |    2 +
 drivers/media/v4l2-core/v4l2-device.c              |   63 +-
 drivers/media/v4l2-core/v4l2-flash.c               |  580 +++++++++++
 drivers/mfd/max77693.c                             |    5 +-
 include/linux/led-class-flash.h                    |  290 ++++++
 include/linux/led-flash-gpio-mux.h                 |   68 ++
 include/linux/led-flash-manager.h                  |  121 +++
 include/linux/leds.h                               |   73 +-
 include/linux/mfd/max77693.h                       |   40 +
 include/linux/of.h                                 |    1 +
 include/linux/of_led_flash_manager.h               |   80 ++
 include/media/v4l2-device.h                        |    7 +
 include/media/v4l2-flash.h                         |  137 +++
 include/uapi/linux/v4l2-controls.h                 |    2 +
 36 files changed, 5272 insertions(+), 52 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-aat1290.txt
 create mode 100644 Documentation/devicetree/bindings/leds/leds-flash-manager.txt
 create mode 100644 Documentation/leds/leds-async-mux.c
 create mode 100644 Documentation/leds/leds-class-flash.txt
 create mode 100644 drivers/leds/led-class-flash.c
 create mode 100644 drivers/leds/led-flash-gpio-mux.c
 create mode 100644 drivers/leds/led-flash-manager.c
 create mode 100644 drivers/leds/leds-aat1290.c
 create mode 100644 drivers/leds/leds-max77693.c
 create mode 100644 drivers/leds/of_led_flash_manager.c
 create mode 100644 drivers/media/v4l2-core/v4l2-flash.c
 create mode 100644 include/linux/led-class-flash.h
 create mode 100644 include/linux/led-flash-gpio-mux.h
 create mode 100644 include/linux/led-flash-manager.h
 create mode 100644 include/linux/of_led_flash_manager.h
 create mode 100644 include/media/v4l2-flash.h

-- 
1.7.9.5


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

* [PATCH/RFC v4 01/21] leds: make brightness type consistent across whole subsystem
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-11 14:04 ` [PATCH/RFC v4 02/21] leds: implement sysfs interface locking mechanism Jacek Anaszewski
                   ` (21 subsequent siblings)
  22 siblings, 0 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Bryan Wu, Richard Purdie

Documentations states that brightness units type is enum led_brightness
and this is the type used by the led API functions. Adjust the type
of brightness variables in the struct led_classdev accordingly.

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>
---
 include/linux/leds.h |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/include/linux/leds.h b/include/linux/leds.h
index e436864..995f933 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -31,8 +31,8 @@ enum led_brightness {
 
 struct led_classdev {
 	const char		*name;
-	int			 brightness;
-	int			 max_brightness;
+	enum led_brightness	 brightness;
+	enum led_brightness	 max_brightness;
 	int			 flags;
 
 	/* Lower 16 bits reflect status */
-- 
1.7.9.5


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

* [PATCH/RFC v4 02/21] leds: implement sysfs interface locking mechanism
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
  2014-07-11 14:04 ` [PATCH/RFC v4 01/21] leds: make brightness type consistent across whole subsystem Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-16 15:35   ` Sakari Ailus
  2014-07-11 14:04 ` [PATCH/RFC v4 03/21] leds: Improve and export led_update_brightness Jacek Anaszewski
                   ` (20 subsequent siblings)
  22 siblings, 1 reply; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Bryan Wu, Richard Purdie

Add mechanism for locking LED subsystem sysfs interface.
This patch prepares ground for addition of LED Flash Class extension.

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    |   13 +++++++++++--
 drivers/leds/led-core.c     |   18 ++++++++++++++++++
 drivers/leds/led-triggers.c |   11 ++++++++---
 include/linux/leds.h        |   31 +++++++++++++++++++++++++++++++
 4 files changed, 68 insertions(+), 5 deletions(-)

diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index aa29198..ea04891 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -45,17 +45,23 @@ 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);
 
 	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);
 
 	return size;
+	ret = size;
+unlock:
+	mutex_unlock(&led_cdev->led_lock);
+	return ret;
 }
 static DEVICE_ATTR_RW(brightness);
 
@@ -219,6 +225,7 @@ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
 #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);
@@ -272,6 +279,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-core.c b/drivers/leds/led-core.c
index 71b40d3..4d7cb31 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -126,3 +126,21 @@ void led_set_brightness(struct led_classdev *led_cdev,
 	__led_set_brightness(led_cdev, brightness);
 }
 EXPORT_SYMBOL(led_set_brightness);
+
+/* Caller must ensure led_cdev->led_lock held */
+void led_sysfs_lock(struct led_classdev *led_cdev)
+{
+	WARN_ON(!mutex_is_locked(&led_cdev->led_lock));
+
+	led_cdev->flags |= LED_SYSFS_LOCK;
+}
+EXPORT_SYMBOL_GPL(led_sysfs_lock);
+
+/* Caller must ensure led_cdev->led_lock held */
+void led_sysfs_unlock(struct led_classdev *led_cdev)
+{
+	WARN_ON(!mutex_is_locked(&led_cdev->led_lock));
+
+	led_cdev->flags &= ~LED_SYSFS_LOCK;
+}
+EXPORT_SYMBOL_GPL(led_sysfs_unlock);
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index c3734f1..0545530 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -37,6 +37,9 @@ 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);
 
 	trigger_name[sizeof(trigger_name) - 1] = '\0';
 	strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
@@ -47,7 +50,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 +61,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/include/linux/leds.h b/include/linux/leds.h
index 995f933..da7c6b5 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -13,6 +13,7 @@
 #define __LINUX_LEDS_H_INCLUDED
 
 #include <linux/list.h>
+#include <linux/mutex.h>
 #include <linux/spinlock.h>
 #include <linux/rwsem.h>
 #include <linux/timer.h>
@@ -42,6 +43,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 << 20)
 
 	/* Set LED brightness level */
 	/* Must not sleep, use a workqueue if needed */
@@ -85,6 +87,9 @@ struct led_classdev {
 	/* true if activated - deactivate routine uses it to do cleanup */
 	bool			activated;
 #endif
+
+	/* Ensures consistent access to the LED Flash Class device */
+	struct mutex		led_lock;
 };
 
 extern int led_classdev_register(struct device *parent,
@@ -140,6 +145,32 @@ 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_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);
+
+/**
+ * led_sysfs_is_locked
+ * @led_cdev: the LED to query
+ *
+ * Returns: true if the sysfs interface of the led is locked
+ */
+static inline bool led_sysfs_is_locked(struct led_classdev *led_cdev)
+{
+	return led_cdev->flags & LED_SYSFS_LOCK;
+}
 
 /*
  * LED Triggers
-- 
1.7.9.5


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

* [PATCH/RFC v4 03/21] leds: Improve and export led_update_brightness
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
  2014-07-11 14:04 ` [PATCH/RFC v4 01/21] leds: make brightness type consistent across whole subsystem Jacek Anaszewski
  2014-07-11 14:04 ` [PATCH/RFC v4 02/21] leds: implement sysfs interface locking mechanism Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-11 14:04 ` [PATCH/RFC v4 04/21] leds: Reorder include directives Jacek Anaszewski
                   ` (19 subsequent siblings)
  22 siblings, 0 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, 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 of return value
of the brightness_get callback and moves the led_update_brightness()
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  |   15 +++++++++++++++
 include/linux/leds.h     |   10 ++++++++++
 3 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index ea04891..da79bbb 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -23,12 +23,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 4d7cb31..0ac06ed 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -127,6 +127,21 @@ void led_set_brightness(struct led_classdev *led_cdev,
 }
 EXPORT_SYMBOL(led_set_brightness);
 
+int led_update_brightness(struct led_classdev *led_cdev)
+{
+	int ret = 0;
+
+	if (led_cdev->brightness_get) {
+		ret = led_cdev->brightness_get(led_cdev);
+		if (ret >= 0) {
+			led_cdev->brightness = ret;
+			return 0;
+		}
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(led_update_brightness);
 /* Caller must ensure led_cdev->led_lock held */
 void led_sysfs_lock(struct led_classdev *led_cdev)
 {
diff --git a/include/linux/leds.h b/include/linux/leds.h
index da7c6b5..e9b025d 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -146,6 +146,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_lock - lock LED sysfs interface
  * @led_cdev: the LED to set
  *
-- 
1.7.9.5


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

* [PATCH/RFC v4 04/21] leds: Reorder include directives
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
                   ` (2 preceding siblings ...)
  2014-07-11 14:04 ` [PATCH/RFC v4 03/21] leds: Improve and export led_update_brightness Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-16 15:42   ` Sakari Ailus
  2014-07-11 14:04 ` [PATCH/RFC v4 05/21] leds: avoid using deprecated DEVICE_ATTR macro Jacek Anaszewski
                   ` (18 subsequent siblings)
  22 siblings, 1 reply; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Bryan Wu, Richard Purdie

Reorder include directives so that they are arranged
in alphabetical order.

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 |   13 +++++++------
 drivers/leds/led-core.c  |    3 ++-
 include/linux/leds.h     |    2 +-
 3 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index da79bbb..0127783 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -9,16 +9,17 @@
  * 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/leds.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 "leds.h"
 
 static struct class *leds_class;
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index 0ac06ed..d156fb6 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -12,10 +12,11 @@
  */
 
 #include <linux/kernel.h>
+#include <linux/leds.h>
 #include <linux/list.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 #include <linux/rwsem.h>
-#include <linux/leds.h>
 #include "leds.h"
 
 DECLARE_RWSEM(leds_list_lock);
diff --git a/include/linux/leds.h b/include/linux/leds.h
index e9b025d..1a130cc 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -14,8 +14,8 @@
 
 #include <linux/list.h>
 #include <linux/mutex.h>
-#include <linux/spinlock.h>
 #include <linux/rwsem.h>
+#include <linux/spinlock.h>
 #include <linux/timer.h>
 #include <linux/workqueue.h>
 
-- 
1.7.9.5


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

* [PATCH/RFC v4 05/21] leds: avoid using deprecated DEVICE_ATTR macro
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
                   ` (3 preceding siblings ...)
  2014-07-11 14:04 ` [PATCH/RFC v4 04/21] leds: Reorder include directives Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-16 15:46   ` Sakari Ailus
  2014-07-11 14:04 ` [PATCH/RFC v4 06/21] leds: add API for setting torch brightness Jacek Anaszewski
                   ` (17 subsequent siblings)
  22 siblings, 1 reply; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Bryan Wu, Richard Purdie

Make the sysfs attributes definition consistent in the whole file.
The modification entails change of the function name:
led_max_brightness_show -> max_brightness_show

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 |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 0127783..a96a1a7 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -60,14 +60,14 @@ unlock:
 }
 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);
-- 
1.7.9.5


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

* [PATCH/RFC v4 06/21] leds: add API for setting torch brightness
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
                   ` (4 preceding siblings ...)
  2014-07-11 14:04 ` [PATCH/RFC v4 05/21] leds: avoid using deprecated DEVICE_ATTR macro Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-16 21:54   ` Sakari Ailus
  2014-07-11 14:04 ` [PATCH/RFC v4 07/21] of: add of_node_ncmp wrapper Jacek Anaszewski
                   ` (16 subsequent siblings)
  22 siblings, 1 reply; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Bryan Wu, Richard Purdie

This patch prepares ground for addition of LED Flash Class extension to
the LED subsystem. Since turning the torch on must have guaranteed
immediate effect the brightness_set op can't be used for it. Drivers must
schedule a work queue task in this op to be compatible with led-triggers,
which call brightess_set from timer irqs. In order to address this
limitiation a torch_brightness_set op and led_set_torch_brightness API
is introduced. Setting brightness sysfs attribute will result in calling
brightness_set op for LED Class decices and torch_brightness_set op for
LED Flash Class devices, whereas triggers will still call brightness
op in both cases.

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 |   18 ++++++++++++++++--
 drivers/leds/led-core.c  |   15 +++++++++++++++
 include/linux/leds.h     |   22 ++++++++++++++++++++++
 3 files changed, 53 insertions(+), 2 deletions(-)

diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index a96a1a7..c17dda0 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -44,15 +44,29 @@ static ssize_t brightness_store(struct device *dev,
 
 	mutex_lock(&led_cdev->led_lock);
 
+	/*
+	 * Having LED_DEV_CAP_TORCH implies this is LED Flash Class
+	 * device and we need to check sysfs accessibility.
+	 */
+	if (led_cdev->flags & LED_DEV_CAP_TORCH) {
+		if (led_sysfs_is_locked(led_cdev)) {
+			ret = -EBUSY;
+			goto unlock;
+		}
+	}
+
 	ret = kstrtoul(buf, 10, &state);
 	if (ret)
 		goto unlock;
 
 	if (state == LED_OFF)
 		led_trigger_remove(led_cdev);
-	__led_set_brightness(led_cdev, state);
 
-	return size;
+	if (led_cdev->flags & LED_DEV_CAP_TORCH)
+		ret = led_set_torch_brightness(led_cdev, state);
+	else
+		__led_set_brightness(led_cdev, state);
+
 	ret = size;
 unlock:
 	mutex_unlock(&led_cdev->led_lock);
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index d156fb6..0ce087a 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -143,6 +143,21 @@ int led_update_brightness(struct led_classdev *led_cdev)
 	return ret;
 }
 EXPORT_SYMBOL(led_update_brightness);
+
+int led_set_torch_brightness(struct led_classdev *led_cdev,
+				enum led_brightness brightness)
+{
+	int ret = 0;
+
+	led_cdev->brightness = min(brightness, led_cdev->max_brightness);
+
+	if (!(led_cdev->flags & LED_SUSPENDED))
+		ret = led_cdev->torch_brightness_set(led_cdev,
+						     led_cdev->brightness);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(led_set_torch_brightness);
+
 /* Caller must ensure led_cdev->led_lock held */
 void led_sysfs_lock(struct led_classdev *led_cdev)
 {
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 1a130cc..9bea9e6 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -44,11 +44,21 @@ struct led_classdev {
 #define LED_BLINK_ONESHOT_STOP	(1 << 18)
 #define LED_BLINK_INVERT	(1 << 19)
 #define LED_SYSFS_LOCK		(1 << 20)
+#define LED_DEV_CAP_TORCH	(1 << 21)
 
 	/* Set LED brightness level */
 	/* Must not sleep, use a workqueue if needed */
 	void		(*brightness_set)(struct led_classdev *led_cdev,
 					  enum led_brightness brightness);
+	/*
+	 * Set LED brightness immediately - it is required for flash led
+	 * devices as they require setting torch brightness to have immediate
+	 * effect. brightness_set op cannot be used for this purpose because
+	 * the led drivers schedule a work queue task in it to allow for
+	 * being called from led-triggers, i.e. from the timer irq context.
+	 */
+	int		(*torch_brightness_set)(struct led_classdev *led_cdev,
+					enum led_brightness brightness);
 	/* Get LED brightness level */
 	enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
 
@@ -156,6 +166,18 @@ extern void led_set_brightness(struct led_classdev *led_cdev,
  */
 extern int led_update_brightness(struct led_classdev *led_cdev);
 
+/**
+ * led_set_torch_brightness - set torch LED brightness
+ * @led_cdev: the LED to set
+ * @brightness: the brightness to set it to
+ *
+ * Returns: 0 on success or negative error value on failure
+ *
+ * Set a torch LED's brightness.
+ */
+extern int led_set_torch_brightness(struct led_classdev *led_cdev,
+					enum led_brightness brightness);
+/**
  * led_sysfs_lock - lock LED sysfs interface
  * @led_cdev: the LED to set
  *
-- 
1.7.9.5


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

* [PATCH/RFC v4 07/21] of: add of_node_ncmp wrapper
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
                   ` (5 preceding siblings ...)
  2014-07-11 14:04 ` [PATCH/RFC v4 06/21] leds: add API for setting torch brightness Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-16 22:00   ` Sakari Ailus
  2014-07-28 13:41   ` Grant Likely
  2014-07-11 14:04 ` [PATCH/RFC v4 08/21] leds: Add sysfs and kernel internal API for flash LEDs Jacek Anaszewski
                   ` (15 subsequent siblings)
  22 siblings, 2 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Grant Likely,
	Benjamin Herrenschmidt, Michal Simek

The wrapper for strnicmp is required for checking whether a node has
expected prefix.

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Michal Simek <monstr@monstr.eu>
---
 include/linux/of.h |    1 +
 1 file changed, 1 insertion(+)

diff --git a/include/linux/of.h b/include/linux/of.h
index 692b56c..9a53eea 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -199,6 +199,7 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size)
 #define of_compat_cmp(s1, s2, l)	strcasecmp((s1), (s2))
 #define of_prop_cmp(s1, s2)		strcmp((s1), (s2))
 #define of_node_cmp(s1, s2)		strcasecmp((s1), (s2))
+#define of_node_ncmp(s1, s2, n)		strnicmp((s1), (s2), (n))
 #endif
 
 /* flag descriptions */
-- 
1.7.9.5


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

* [PATCH/RFC v4 08/21] leds: Add sysfs and kernel internal API for flash LEDs
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
                   ` (6 preceding siblings ...)
  2014-07-11 14:04 ` [PATCH/RFC v4 07/21] of: add of_node_ncmp wrapper Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-11 14:04 ` [PATCH/RFC v4 09/21] Documentation: leds: Add description of LED Flash Class extension Jacek Anaszewski
                   ` (14 subsequent siblings)
  22 siblings, 0 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, 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,
external_strobe, indicator_brightness, max_indicator_brightness,
strobe_provider, strobe_providerN, blocking_strobe. 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. When V4L2 Flash sub-device
file is opened, the LED class device sysfs interface is made
unavailable.

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                 |   13 +
 drivers/leds/Makefile                |    5 +
 drivers/leds/led-class-flash.c       |  715 ++++++++++++++++++++++++++++++++++
 drivers/leds/led-class.c             |    4 +
 drivers/leds/led-flash-gpio-mux.c    |  102 +++++
 drivers/leds/led-flash-manager.c     |  698 +++++++++++++++++++++++++++++++++
 drivers/leds/led-triggers.c          |    5 +
 drivers/leds/of_led_flash_manager.c  |  155 ++++++++
 include/linux/led-class-flash.h      |  290 ++++++++++++++
 include/linux/led-flash-gpio-mux.h   |   68 ++++
 include/linux/led-flash-manager.h    |  121 ++++++
 include/linux/leds.h                 |    4 +
 include/linux/of_led_flash_manager.h |   80 ++++
 13 files changed, 2260 insertions(+)
 create mode 100644 drivers/leds/led-class-flash.c
 create mode 100644 drivers/leds/led-flash-gpio-mux.c
 create mode 100644 drivers/leds/led-flash-manager.c
 create mode 100644 drivers/leds/of_led_flash_manager.c
 create mode 100644 include/linux/led-class-flash.h
 create mode 100644 include/linux/led-flash-gpio-mux.h
 create mode 100644 include/linux/led-flash-manager.h
 create mode 100644 include/linux/of_led_flash_manager.h

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 6784c17..5032c6f 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -19,6 +19,19 @@ config LEDS_CLASS
 	  This option enables the led sysfs class in /sys/class/leds.  You'll
 	  need this to do anything useful with LEDs.  If unsure, say N.
 
+config LEDS_CLASS_FLASH
+	tristate "LED Flash Class Support"
+	depends on LEDS_CLASS
+	depends on OF
+	help
+	  This option enables the flash led sysfs class in /sys/class/leds.
+	  It wrapps LED Class and adds flash LEDs specific sysfs attributes
+	  and kernel internal API to it. It allows also for dynamic routing
+	  of external strobe signals basing on the information provided
+	  in the Device Tree binding. You'll need this to provide support
+	  for the flash related features of a LED device. It can be built
+	  as a module.
+
 comment "LED drivers"
 
 config LEDS_88PM860X
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 79c5155..237c5ba 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -2,6 +2,11 @@
 # LED Core
 obj-$(CONFIG_NEW_LEDS)			+= led-core.o
 obj-$(CONFIG_LEDS_CLASS)		+= led-class.o
+obj-$(CONFIG_LEDS_CLASS_FLASH)		+= led-flash.o
+led-flash-objs				:= led-class-flash.o \
+					   led-flash-manager.o \
+					   led-flash-gpio-mux.o \
+					   of_led_flash_manager.o
 obj-$(CONFIG_LEDS_TRIGGERS)		+= led-triggers.o
 
 # LED Platform Drivers
diff --git a/drivers/leds/led-class-flash.c b/drivers/leds/led-class-flash.c
new file mode 100644
index 0000000..607f2d7
--- /dev/null
+++ b/drivers/leds/led-class-flash.c
@@ -0,0 +1,715 @@
+/*
+ * LED Flash Class 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/leds.h>
+#include <linux/led-class-flash.h>
+#include <linux/led-flash-manager.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <media/v4l2-flash.h>
+#include "leds.h"
+
+#define has_flash_op(flash, op)				\
+	(flash && flash->ops->op)
+
+#define call_flash_op(flash, op, args...)		\
+	((has_flash_op(flash, op)) ?			\
+			(flash->ops->op(flash, args)) :	\
+			-EINVAL)
+
+static ssize_t flash_brightness_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+	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;
+
+	ret = led_set_flash_brightness(flash, state);
+	if (ret < 0)
+		goto unlock;
+
+	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_classdev_flash *flash = lcdev_to_flash(led_cdev);
+
+	/* no lock needed for this */
+	led_update_flash_brightness(flash);
+
+	return sprintf(buf, "%u\n", flash->brightness.val);
+}
+static DEVICE_ATTR_RW(flash_brightness);
+
+static ssize_t max_flash_brightness_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+
+	return sprintf(buf, "%u\n", flash->brightness.max);
+}
+static DEVICE_ATTR_RO(max_flash_brightness);
+
+static ssize_t indicator_brightness_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+	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;
+
+	ret = led_set_indicator_brightness(flash, state);
+	if (ret < 0)
+		goto unlock;
+
+	ret = size;
+unlock:
+	mutex_unlock(&led_cdev->led_lock);
+	return ret;
+}
+
+static ssize_t indicator_brightness_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+
+	/* no lock needed for this */
+	led_update_indicator_brightness(flash);
+
+	return sprintf(buf, "%u\n", flash->indicator_brightness->val);
+}
+static DEVICE_ATTR_RW(indicator_brightness);
+
+static ssize_t max_indicator_brightness_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+
+	return sprintf(buf, "%u\n", flash->indicator_brightness->max);
+}
+static DEVICE_ATTR_RO(max_indicator_brightness);
+
+static ssize_t flash_strobe_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+	unsigned long state;
+	ssize_t ret = -EINVAL;
+
+	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) {
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	ret = led_set_flash_strobe(flash, 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);
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+	bool state;
+	int ret;
+
+	/* no lock needed for this */
+	ret = led_get_flash_strobe(flash, &state);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%u\n", state);
+}
+static DEVICE_ATTR_RW(flash_strobe);
+
+static ssize_t flash_timeout_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+	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(flash, 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_classdev_flash *flash = lcdev_to_flash(led_cdev);
+
+	return sprintf(buf, "%u\n", flash->timeout.val);
+}
+static DEVICE_ATTR_RW(flash_timeout);
+
+static ssize_t max_flash_timeout_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+
+	return sprintf(buf, "%u\n", flash->timeout.max);
+}
+static DEVICE_ATTR_RO(max_flash_timeout);
+
+static ssize_t flash_fault_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+	u32 fault;
+	int ret;
+
+	ret = led_get_flash_fault(flash, &fault);
+	if (ret < 0)
+		return -EINVAL;
+
+	return sprintf(buf, "0x%8.8x\n", fault);
+}
+static DEVICE_ATTR_RO(flash_fault);
+
+static ssize_t external_strobe_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+	unsigned long external_strobe;
+	ssize_t ret;
+
+	mutex_lock(&led_cdev->led_lock);
+
+	if (led_sysfs_is_locked(led_cdev)) {
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	ret = kstrtoul(buf, 10, &external_strobe);
+	if (ret)
+		goto unlock;
+
+	if (external_strobe > 1) {
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	ret = led_set_external_strobe(flash, external_strobe);
+	if (ret < 0)
+		goto unlock;
+	ret = size;
+unlock:
+	mutex_unlock(&led_cdev->led_lock);
+	return ret;
+}
+
+static ssize_t external_strobe_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+
+	return sprintf(buf, "%u\n", flash->external_strobe);
+}
+static DEVICE_ATTR_RW(external_strobe);
+
+static struct attribute *led_flash_strobe_attrs[] = {
+	&dev_attr_flash_strobe.attr,
+	NULL,
+};
+
+static struct attribute *led_flash_indicator_attrs[] = {
+	&dev_attr_indicator_brightness.attr,
+	&dev_attr_max_indicator_brightness.attr,
+	NULL,
+};
+
+static struct attribute *led_flash_timeout_attrs[] = {
+	&dev_attr_flash_timeout.attr,
+	&dev_attr_max_flash_timeout.attr,
+	NULL,
+};
+
+static struct attribute *led_flash_brightness_attrs[] = {
+	&dev_attr_flash_brightness.attr,
+	&dev_attr_max_flash_brightness.attr,
+	NULL,
+};
+
+static struct attribute *led_flash_external_strobe_attrs[] = {
+	&dev_attr_external_strobe.attr,
+	NULL,
+};
+
+static struct attribute *led_flash_fault_attrs[] = {
+	&dev_attr_flash_fault.attr,
+	NULL,
+};
+
+static struct attribute_group led_flash_strobe_group = {
+	.attrs = led_flash_strobe_attrs,
+};
+
+static struct attribute_group led_flash_brightness_group = {
+	.attrs = led_flash_brightness_attrs,
+};
+
+static struct attribute_group led_flash_timeout_group = {
+	.attrs = led_flash_timeout_attrs,
+};
+
+static struct attribute_group led_flash_indicator_group = {
+	.attrs = led_flash_indicator_attrs,
+};
+
+static struct attribute_group led_flash_fault_group = {
+	.attrs = led_flash_fault_attrs,
+};
+
+static struct attribute_group led_flash_external_strobe_group = {
+	.attrs = led_flash_external_strobe_attrs,
+};
+
+static void led_flash_resume(struct led_classdev *led_cdev)
+{
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+
+	call_flash_op(flash, flash_brightness_set, flash->brightness.val);
+	call_flash_op(flash, timeout_set, flash->timeout.val);
+	call_flash_op(flash, indicator_brightness_set,
+				flash->indicator_brightness->val);
+}
+
+#ifdef CONFIG_V4L2_FLASH_LED_CLASS
+const struct v4l2_flash_ops led_flash_v4l2_ops = {
+	.torch_brightness_set = led_set_torch_brightness,
+	.torch_brightness_update = led_update_brightness,
+	.flash_brightness_set = led_set_flash_brightness,
+	.flash_brightness_update = led_update_flash_brightness,
+	.indicator_brightness_set = led_set_indicator_brightness,
+	.indicator_brightness_update = led_update_indicator_brightness,
+	.strobe_set = led_set_flash_strobe,
+	.strobe_get = led_get_flash_strobe,
+	.timeout_set = led_set_flash_timeout,
+	.external_strobe_set = led_set_external_strobe,
+	.fault_get = led_get_flash_fault,
+	.sysfs_lock = led_sysfs_lock,
+	.sysfs_unlock = led_sysfs_unlock,
+};
+
+const struct v4l2_flash_ops *led_get_v4l2_flash_ops(void)
+{
+	return &led_flash_v4l2_ops;
+}
+EXPORT_SYMBOL_GPL(led_get_v4l2_flash_ops);
+#endif
+
+static void led_flash_remove_sysfs_groups(struct led_classdev_flash *flash)
+{
+	struct led_classdev *led_cdev = &flash->led_cdev;
+	int i;
+
+	for (i = 0; i < LED_FLASH_MAX_SYSFS_GROUPS; ++i)
+		if (flash->sysfs_groups[i])
+			sysfs_remove_group(&led_cdev->dev->kobj,
+						flash->sysfs_groups[i]);
+}
+
+static int led_flash_create_sysfs_groups(struct led_classdev_flash *flash)
+{
+	struct led_classdev *led_cdev = &flash->led_cdev;
+	const struct led_flash_ops *ops = flash->ops;
+	int ret, num_sysfs_groups = 0;
+
+	memset(flash->sysfs_groups, 0, sizeof(*flash->sysfs_groups) *
+						LED_FLASH_MAX_SYSFS_GROUPS);
+
+	ret = sysfs_create_group(&led_cdev->dev->kobj, &led_flash_strobe_group);
+	if (ret < 0)
+		goto err_create_group;
+	flash->sysfs_groups[num_sysfs_groups++] = &led_flash_strobe_group;
+
+	if (flash->indicator_brightness) {
+		ret = sysfs_create_group(&led_cdev->dev->kobj,
+					&led_flash_indicator_group);
+		if (ret < 0)
+			goto err_create_group;
+		flash->sysfs_groups[num_sysfs_groups++] =
+					&led_flash_indicator_group;
+	}
+
+	if (ops->flash_brightness_set) {
+		ret = sysfs_create_group(&led_cdev->dev->kobj,
+					&led_flash_brightness_group);
+		if (ret < 0)
+			goto err_create_group;
+		flash->sysfs_groups[num_sysfs_groups++] =
+					&led_flash_brightness_group;
+	}
+
+	if (ops->timeout_set) {
+		ret = sysfs_create_group(&led_cdev->dev->kobj,
+					&led_flash_timeout_group);
+		if (ret < 0)
+			goto err_create_group;
+		flash->sysfs_groups[num_sysfs_groups++] =
+					&led_flash_timeout_group;
+	}
+
+	if (ops->fault_get) {
+		ret = sysfs_create_group(&led_cdev->dev->kobj,
+					&led_flash_fault_group);
+		if (ret < 0)
+			goto err_create_group;
+		flash->sysfs_groups[num_sysfs_groups++] =
+					&led_flash_fault_group;
+	}
+
+	if (flash->has_external_strobe) {
+		ret = sysfs_create_group(&led_cdev->dev->kobj,
+					&led_flash_external_strobe_group);
+		if (ret < 0)
+			goto err_create_group;
+		flash->sysfs_groups[num_sysfs_groups++] =
+					&led_flash_external_strobe_group;
+	}
+
+	return 0;
+
+err_create_group:
+	led_flash_remove_sysfs_groups(flash);
+	return ret;
+}
+
+int led_classdev_flash_register(struct device *parent,
+				struct led_classdev_flash *flash,
+				struct device_node *node)
+{
+	struct led_classdev *led_cdev;
+	const struct led_flash_ops *ops;
+	int ret = -EINVAL;
+
+	if (!flash)
+		return -EINVAL;
+
+	led_cdev = &flash->led_cdev;
+
+	/* Torch capability is default for every LED Flash Class device */
+	led_cdev->flags |= LED_DEV_CAP_TORCH;
+
+	if (led_cdev->flags & LED_DEV_CAP_FLASH) {
+		if (!led_cdev->torch_brightness_set)
+			return -EINVAL;
+
+		ops = flash->ops;
+		if (!ops || !ops->strobe_set)
+			return -EINVAL;
+
+		if ((led_cdev->flags & LED_DEV_CAP_INDICATOR) &&
+		    (!flash->indicator_brightness ||
+		     !ops->indicator_brightness_set))
+			return -EINVAL;
+
+		led_cdev->flash_resume = led_flash_resume;
+	}
+
+	/* Register led class device */
+	ret = led_classdev_register(parent, led_cdev);
+	if (ret < 0)
+		return -EINVAL;
+
+	/* Register in the flash manager if there is related data to parse */
+	if (node) {
+		ret = led_flash_manager_register_flash(flash, node);
+		if (ret < 0)
+			goto err_flash_manager_register;
+	}
+
+	/* Create flash led specific sysfs attributes */
+	ret = led_flash_create_sysfs_groups(flash);
+	if (ret < 0)
+		goto err_create_sysfs_groups;
+
+	return 0;
+
+err_create_sysfs_groups:
+	led_flash_manager_unregister_flash(flash);
+err_flash_manager_register:
+	led_classdev_unregister(led_cdev);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(led_classdev_flash_register);
+
+void led_classdev_flash_unregister(struct led_classdev_flash *flash)
+{
+	led_flash_remove_sysfs_groups(flash);
+	led_flash_manager_unregister_flash(flash);
+	led_classdev_unregister(&flash->led_cdev);
+}
+EXPORT_SYMBOL_GPL(led_classdev_flash_unregister);
+
+int led_set_flash_strobe(struct led_classdev_flash *flash, bool state)
+{
+	struct led_classdev *led_cdev = &flash->led_cdev;
+	int ret = 0;
+
+	if (flash->external_strobe)
+		return -EBUSY;
+
+	/* strobe can be stopped without flash manager involvement */
+	if (!state)
+		return call_flash_op(flash, strobe_set, state);
+
+	/*
+	 * Flash manager needs to be involved in setting flash
+	 * strobe if there were strobe gates defined in the
+	 * device tree binding. This call blocks the caller for
+	 * the current flash timeout period if state == true and
+	 * the flash led device depends on shared muxes. Locking is
+	 * required for assuring that nobody will reconfigure muxes
+	 * in the meantime.
+	 */
+	if ((led_cdev->flags & LED_DEV_CAP_FL_MANAGER))
+		ret = led_flash_manager_setup_strobe(flash, false);
+	else
+		ret = call_flash_op(flash, strobe_set, true);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(led_set_flash_strobe);
+
+int led_get_flash_strobe(struct led_classdev_flash *flash, bool *state)
+{
+	return call_flash_op(flash, strobe_get, state);
+}
+EXPORT_SYMBOL_GPL(led_get_flash_strobe);
+
+void led_clamp_align(struct led_flash_setting *s)
+{
+	u32 v, offset;
+
+	v = s->val + s->step / 2;
+	v = clamp(v, s->min, s->max);
+	offset = v - s->min;
+	offset = s->step * (offset / s->step);
+	s->val = s->min + offset;
+}
+
+int led_set_flash_timeout(struct led_classdev_flash *flash, u32 timeout)
+{
+	struct led_classdev *led_cdev = &flash->led_cdev;
+	struct led_flash_setting *s = &flash->timeout;
+	int ret = 0;
+
+	s->val = timeout;
+	led_clamp_align(s);
+
+	if (!(led_cdev->flags & LED_SUSPENDED))
+		ret = call_flash_op(flash, timeout_set, s->val);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(led_set_flash_timeout);
+
+int led_get_flash_fault(struct led_classdev_flash *flash, u32 *fault)
+{
+	return call_flash_op(flash, fault_get, fault);
+}
+EXPORT_SYMBOL_GPL(led_get_flash_fault);
+
+int led_set_external_strobe(struct led_classdev_flash *flash, bool enable)
+{
+	struct led_classdev *led_cdev = &flash->led_cdev;
+	int ret;
+
+	if (flash->has_external_strobe) {
+		/*
+		 * Some flash led devices need altering their register
+		 * settings to start listen to the external strobe signal.
+		 */
+		if (has_flash_op(flash, external_strobe_set)) {
+			ret = call_flash_op(flash, external_strobe_set, enable);
+			if (ret < 0)
+				return ret;
+		}
+
+		flash->external_strobe = enable;
+
+		/*
+		 * Flash manager needs to be involved in setting external
+		 * strobe mode if there were strobe gates defined in the
+		 * device tree binding. This call blocks the caller for
+		 * the current flash timeout period if enable == true and
+		 * the flash led device depends on shared muxes. Locking is
+		 * required for assuring that nobody will reconfigure muxes
+		 * while the flash device is awaiting external strobe signal.
+		 */
+		if (enable && (led_cdev->flags & LED_DEV_CAP_FL_MANAGER)) {
+			ret = led_flash_manager_setup_strobe(flash, true);
+			if (ret < 0)
+				return ret;
+		}
+	} else if (enable) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(led_set_external_strobe);
+
+int led_set_flash_brightness(struct led_classdev_flash *flash,
+				u32 brightness)
+{
+	struct led_classdev *led_cdev = &flash->led_cdev;
+	struct led_flash_setting *s = &flash->brightness;
+	int ret = 0;
+
+	s->val = brightness;
+	led_clamp_align(s);
+
+	if (!(led_cdev->flags & LED_SUSPENDED))
+		ret = call_flash_op(flash, flash_brightness_set, s->val);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(led_set_flash_brightness);
+
+int led_update_flash_brightness(struct led_classdev_flash *flash)
+{
+	struct led_flash_setting *s = &flash->brightness;
+	u32 brightness;
+	int ret = 0;
+
+	if (has_flash_op(flash, flash_brightness_get)) {
+		ret = call_flash_op(flash, flash_brightness_get,
+						&brightness);
+		if (ret < 0)
+			return ret;
+		s->val = brightness;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(led_update_flash_brightness);
+
+int led_set_indicator_brightness(struct led_classdev_flash *flash,
+					u32 brightness)
+{
+	struct led_classdev *led_cdev = &flash->led_cdev;
+	struct led_flash_setting *s = flash->indicator_brightness;
+	int ret = 0;
+
+	if (!s)
+		return -EINVAL;
+
+	s->val = brightness;
+	led_clamp_align(s);
+
+	if (!(led_cdev->flags & LED_SUSPENDED))
+		ret = call_flash_op(flash, indicator_brightness_set, s->val);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(led_set_indicator_brightness);
+
+int led_update_indicator_brightness(struct led_classdev_flash *flash)
+{
+	struct led_flash_setting *s = flash->indicator_brightness;
+	u32 brightness;
+	int ret = 0;
+
+	if (!s)
+		return -EINVAL;
+
+	if (has_flash_op(flash, indicator_brightness_get)) {
+		ret = call_flash_op(flash, indicator_brightness_get,
+							&brightness);
+		if (ret < 0)
+			return ret;
+		s->val = brightness;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(led_update_indicator_brightness);
+
+MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LED Flash Class Interface");
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index c17dda0..165a1fb 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -189,6 +189,10 @@ EXPORT_SYMBOL_GPL(led_classdev_suspend);
 void led_classdev_resume(struct led_classdev *led_cdev)
 {
 	led_cdev->brightness_set(led_cdev, led_cdev->brightness);
+
+	if (led_cdev->flash_resume)
+		led_cdev->flash_resume(led_cdev);
+
 	led_cdev->flags &= ~LED_SUSPENDED;
 }
 EXPORT_SYMBOL_GPL(led_classdev_resume);
diff --git a/drivers/leds/led-flash-gpio-mux.c b/drivers/leds/led-flash-gpio-mux.c
new file mode 100644
index 0000000..2803fcc
--- /dev/null
+++ b/drivers/leds/led-flash-gpio-mux.c
@@ -0,0 +1,102 @@
+/*
+ * LED Flash Class gpio mux
+ *
+ *      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/gpio.h>
+#include <linux/led-flash-gpio-mux.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+
+int led_flash_gpio_mux_select_line(u32 line_id, void *mux)
+{
+	struct led_flash_gpio_mux *gpio_mux = (struct led_flash_gpio_mux *) mux;
+	struct led_flash_gpio_mux_selector *sel;
+	u32 mask = 1;
+
+	/* Setup selectors */
+	list_for_each_entry(sel, &gpio_mux->selectors, list) {
+		gpio_set_value(sel->gpio, !!(line_id & mask));
+		mask <<= 1;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(led_flash_gpio_mux_select_line);
+
+/* Create standard gpio mux */
+int led_flash_gpio_mux_create(struct led_flash_gpio_mux **new_mux,
+			      struct device_node *mux_node)
+{
+	struct led_flash_gpio_mux *mux;
+	struct led_flash_gpio_mux_selector *sel;
+	int gpio_num, gpio, ret, i;
+	char gpio_name[20];
+	static int cnt_gpio;
+
+	/* Get the number of mux selectors */
+	gpio_num = of_gpio_count(mux_node);
+	if (gpio_num == 0)
+		return -EINVAL;
+
+	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+	if (!mux)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&mux->selectors);
+
+	/* Request gpios for all selectors */
+	for (i = 0; i < gpio_num; ++i) {
+		gpio = of_get_gpio(mux_node, i);
+		if (gpio_is_valid(gpio)) {
+			sprintf(gpio_name, "v4l2_mux selector %d", cnt_gpio++);
+			ret = gpio_request_one(gpio, GPIOF_DIR_OUT, gpio_name);
+			if (ret < 0)
+				goto err_gpio_request;
+
+			/* Add new entry to the gpio selectors list */
+			sel = kzalloc(sizeof(*sel), GFP_KERNEL);
+			if (!sel) {
+				ret = -ENOMEM;
+				goto err_gpio_request;
+			}
+			sel->gpio = gpio;
+
+			list_add_tail(&sel->list, &mux->selectors);
+		} else {
+			ret = -EINVAL;
+			goto err_gpio_request;
+		}
+
+	}
+
+	*new_mux = mux;
+
+	return 0;
+
+err_gpio_request:
+	led_flash_gpio_mux_release(mux);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(led_flash_gpio_mux_create);
+
+void led_flash_gpio_mux_release(void *mux)
+{
+	struct led_flash_gpio_mux *gpio_mux = (struct led_flash_gpio_mux *) mux;
+	struct led_flash_gpio_mux_selector *sel, *n;
+
+	list_for_each_entry_safe(sel, n, &gpio_mux->selectors, list) {
+		if (gpio_is_valid(sel->gpio))
+			gpio_free(sel->gpio);
+		kfree(sel);
+	}
+	kfree(gpio_mux);
+}
+EXPORT_SYMBOL_GPL(led_flash_gpio_mux_release);
diff --git a/drivers/leds/led-flash-manager.c b/drivers/leds/led-flash-manager.c
new file mode 100644
index 0000000..2133277
--- /dev/null
+++ b/drivers/leds/led-flash-manager.c
@@ -0,0 +1,698 @@
+/*
+ *  LED Flash Manager for maintaining LED Flash Class devices
+ *  along with their corresponding muxex, faciliating dynamic
+ *  reconfiguration of the strobe signal source.
+ *
+ *	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/delay.h>
+#include <linux/led-class-flash.h>
+#include <linux/led-flash-gpio-mux.h>
+#include <linux/led-flash-manager.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_gpio.h>
+#include <linux/of_led_flash_manager.h>
+#include <linux/slab.h>
+
+static LIST_HEAD(flash_list);
+static LIST_HEAD(mux_bound_list);
+static LIST_HEAD(mux_waiting_list);
+static DEFINE_MUTEX(fm_lock);
+
+static ssize_t strobe_provider_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+	unsigned long provider_id;
+	ssize_t ret;
+
+	mutex_lock(&led_cdev->led_lock);
+
+	if (led_sysfs_is_locked(led_cdev)) {
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	ret = kstrtoul(buf, 10, &provider_id);
+	if (ret)
+		goto unlock;
+
+	if (provider_id > flash->num_strobe_providers - 1) {
+		ret = -ERANGE;
+		goto unlock;
+	}
+
+	flash->strobe_provider_id = provider_id;
+
+	ret = size;
+unlock:
+	mutex_unlock(&led_cdev->led_lock);
+	return ret;
+}
+
+static ssize_t strobe_provider_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+
+	return sprintf(buf, "%u\n", flash->strobe_provider_id);
+}
+static DEVICE_ATTR_RW(strobe_provider);
+
+static ssize_t available_strobe_providers_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct led_flash_strobe_provider *provider =
+		container_of(attr, struct led_flash_strobe_provider, attr);
+	const char *no_name = "undefined";
+	const char *provider_name;
+
+	provider_name = provider->name ? provider->name :
+					 no_name;
+
+	return sprintf(buf, "%s\n", provider_name);
+}
+
+static ssize_t blocking_strobe_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+
+	return sprintf(buf, "%u\n", !!flash->num_shared_muxes);
+}
+static DEVICE_ATTR_RO(blocking_strobe);
+
+static int led_flash_manager_create_providers_attrs(
+					struct led_classdev_flash *flash)
+{
+	struct led_flash_strobe_provider *provider;
+	struct led_classdev *led_cdev = &flash->led_cdev;
+	int cnt_attr = 0;
+	int ret;
+
+	list_for_each_entry(provider, &flash->strobe_providers, list) {
+		provider->attr.show = available_strobe_providers_show;
+		provider->attr.attr.mode = S_IRUGO;
+
+		sprintf(provider->attr_name, "strobe_provider%d",
+							cnt_attr++);
+		provider->attr.attr.name = provider->attr_name;
+
+		sysfs_attr_init(&provider->attr.attr);
+
+		ret = sysfs_create_file(&led_cdev->dev->kobj,
+						&provider->attr.attr);
+		if (ret < 0)
+			goto error_create_attr;
+
+		provider->attr_registered = true;
+	}
+
+	/*
+	 * strobe_provider attribute is required only if there have been more
+	 * than one strobe source defined for the LED Flash Class device.
+	 */
+	if (cnt_attr > 1) {
+		ret = sysfs_create_file(&led_cdev->dev->kobj,
+					&dev_attr_strobe_provider.attr);
+		if (ret < 0)
+			goto error_create_attr;
+	}
+
+	return 0;
+
+error_create_attr:
+	list_for_each_entry(provider, &flash->strobe_providers, list) {
+		if (!provider->attr_registered)
+			break;
+		sysfs_remove_file(&led_cdev->dev->kobj, &provider->attr.attr);
+	}
+
+	return ret;
+}
+
+static void led_flash_manager_remove_providers_attrs(
+					struct led_classdev_flash *flash)
+{
+	struct led_flash_strobe_provider *provider;
+	struct led_classdev *led_cdev = &flash->led_cdev;
+	int cnt_attr = 0;
+
+	list_for_each_entry(provider, &flash->strobe_providers, list) {
+		if (!provider->attr_registered)
+			break;
+		sysfs_remove_file(&led_cdev->dev->kobj, &provider->attr.attr);
+		provider->attr_registered = false;
+		++cnt_attr;
+	}
+
+	/*
+	 * If there was more than one strobe_providerN attr to remove
+	 * than there is also strobe_provider attr to remove.
+	 */
+	if (cnt_attr > 1)
+		sysfs_remove_file(&led_cdev->dev->kobj,
+				  &dev_attr_strobe_provider.attr);
+}
+
+/* Return mux associated with gate */
+static struct led_flash_mux *led_flash_manager_get_mux_by_gate(
+					struct led_flash_strobe_gate *gate,
+					struct list_head *mux_list)
+{
+	struct led_flash_mux *mux;
+
+	list_for_each_entry(mux, mux_list, list)
+		if (mux->node == gate->mux_node)
+			return mux;
+
+	return NULL;
+}
+
+/* Setup all muxes in the gate list */
+static int led_flash_manager_setup_muxes(struct led_classdev_flash *flash,
+					  struct list_head *gate_list)
+{
+	struct led_classdev *led_cdev = &flash->led_cdev;
+	struct led_flash_strobe_gate *gate;
+	struct led_flash_mux *mux;
+	struct device *dev = led_cdev->dev->parent;
+	int ret = 0;
+
+	list_for_each_entry(gate, gate_list, list) {
+		mux = led_flash_manager_get_mux_by_gate(gate, &mux_bound_list);
+		if (!mux) {
+			dev_err(dev, "Flash mux not bound (%s)\n",
+				gate->mux_node->name);
+			return -ENODEV;
+		}
+
+		ret = mux->ops->select_line(gate->line_id,
+						mux->private_data);
+	}
+
+	return ret;
+}
+
+/*
+ * Setup all muxes required to open the route
+ * to the external strobe signal provider
+ */
+static int led_flash_manager_select_strobe_provider(
+					struct led_classdev_flash *flash,
+					int provider_id)
+{
+	struct led_flash_strobe_provider *provider;
+	int ret, provider_cnt = 0;
+
+	list_for_each_entry(provider, &flash->strobe_providers, list)
+		if (provider_cnt++ == provider_id) {
+			ret = led_flash_manager_setup_muxes(flash,
+						&provider->strobe_gates);
+			return ret;
+		}
+
+	return -EINVAL;
+}
+
+/*
+ * Setup all muxes required to open the route
+ * either to software or external strobe source.
+ */
+static int led_flash_manager_set_external_strobe(
+					struct led_classdev_flash *flash,
+					bool external)
+{
+	int ret;
+
+	if (external)
+		ret = led_flash_manager_select_strobe_provider(flash,
+						flash->strobe_provider_id);
+	else
+		ret = led_flash_manager_setup_muxes(flash,
+						&flash->software_strobe_gates);
+
+	return ret;
+}
+
+/* Notify flash manager that async mux is available. */
+int led_flash_manager_bind_async_mux(struct led_flash_mux *async_mux)
+{
+	struct led_flash_mux *mux;
+	bool mux_found = false;
+	int ret = 0;
+
+	if (!async_mux)
+		return -EINVAL;
+
+	mutex_lock(&fm_lock);
+
+	/*
+	 * Check whether the LED Flash Class device using this
+	 * mux has been already registered in the flash manager.
+	 */
+	list_for_each_entry(mux, &mux_waiting_list, list)
+		if (async_mux->node == mux->node) {
+			/* Move the mux to the bound muxes list */
+
+			list_move(&mux->list, &mux_bound_list);
+			mux_found = true;
+
+			if (!try_module_get(async_mux->owner)) {
+				ret = -ENODEV;
+				goto unlock;
+			}
+
+			break;
+		}
+
+	if (!mux_found) {
+		/* This is a new mux - create its representation */
+		mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+		if (!mux) {
+			ret = -ENOMEM;
+			goto unlock;
+		}
+
+		INIT_LIST_HEAD(&mux->refs);
+
+		/* Add the mux to the bound list. */
+		list_add(&mux->list, &mux_bound_list);
+	}
+
+	mux->ops = async_mux->ops;
+	mux->node = async_mux->node;
+	mux->owner = async_mux->owner;
+
+unlock:
+	mutex_unlock(&fm_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(led_flash_manager_bind_async_mux);
+
+int led_flash_manager_unbind_async_mux(struct device_node *mux_node)
+{
+	struct led_flash_mux *mux;
+	int ret = -ENODEV;
+
+	mutex_lock(&fm_lock);
+
+	/*
+	 * Mux can be unbound only when is not used by any
+	 * flash led device, otherwise this is erroneous call.
+	 */
+	list_for_each_entry(mux, &mux_waiting_list, list)
+		if (mux->node == mux_node) {
+			list_move(&mux->list, &mux_waiting_list);
+			ret = 0;
+			break;
+		}
+
+	mutex_unlock(&fm_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(led_flash_manager_unbind_async_mux);
+
+struct led_flash_mux_ops gpio_mux_ops = {
+	.select_line = led_flash_gpio_mux_select_line,
+	.release_private_data = led_flash_gpio_mux_release,
+};
+
+static int __add_mux_ref(struct led_flash_mux_ref *ref,
+				struct led_flash_mux *mux)
+{
+	struct led_flash_mux_ref *r;
+	int ret;
+
+	/*
+	 * A flash can be associated with a mux through
+	 * more than one gate - increment mux reference
+	 * count in such a case.
+	 */
+	list_for_each_entry(r, &mux->refs, list)
+		if (r->flash == ref->flash)
+			return 0;
+
+	/* protect async mux against rmmod */
+	if (mux->owner) {
+		ret = try_module_get(mux->owner);
+		if (ret < 0)
+			return ret;
+	}
+
+	list_add(&ref->list, &mux->refs);
+	++mux->num_refs;
+
+	if (mux->num_refs == 2) {
+		list_for_each_entry(r, &mux->refs, list) {
+			++r->flash->num_shared_muxes;
+		}
+		return 0;
+	}
+
+	if (mux->num_refs > 2)
+		++ref->flash->num_shared_muxes;
+
+	return 0;
+}
+
+static void __remove_mux_ref(struct led_flash_mux_ref *ref,
+				struct led_flash_mux *mux)
+{
+	struct led_flash_mux_ref *r;
+
+	/* decrement async mux refcount */
+	if (mux->owner)
+		module_put(mux->owner);
+
+	list_del(&ref->list);
+	--mux->num_refs;
+
+	if (mux->num_refs == 1) {
+		r = list_first_entry(&mux->refs, struct led_flash_mux_ref,
+						list);
+		--r->flash->num_shared_muxes;
+		return;
+	}
+
+	if (mux->num_refs > 1)
+		--ref->flash->num_shared_muxes;
+}
+
+/*
+ * Parse mux node and add the mux it refers to either to waiting
+ * or bound list depending on the mux type (gpio or asynchronous).
+ */
+static int led_flash_manager_parse_mux_node(struct led_classdev_flash *flash,
+					    struct led_flash_strobe_gate *gate)
+{
+	struct led_flash_mux *mux;
+	struct device_node *async_mux_node;
+	struct led_flash_gpio_mux *gpio_mux;
+	struct led_flash_mux_ref *ref;
+	int ret = -EINVAL;
+
+	/* Create flash ref to be added to the mux references list */
+	ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+	if (!ref)
+		return -ENOMEM;
+	ref->flash = flash;
+
+	/* if this is async mux update gate's mux_node accordingly */
+	async_mux_node = of_parse_phandle(gate->mux_node, "mux-async", 0);
+	if (async_mux_node)
+		gate->mux_node = async_mux_node;
+
+	/* Check if the mux isn't already on waiting list */
+	list_for_each_entry(mux, &mux_waiting_list, list)
+		if (mux->node == gate->mux_node)
+			return __add_mux_ref(ref, mux);
+
+	/* Check if the mux isn't already on bound list */
+	list_for_each_entry(mux, &mux_bound_list, list)
+		if (mux->node == gate->mux_node)
+			return __add_mux_ref(ref, mux);
+
+	/* This is a new mux - create its representation */
+	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+	if (!mux)
+		return -ENOMEM;
+
+	mux->node = gate->mux_node;
+
+	INIT_LIST_HEAD(&mux->refs);
+
+	/* Increment reference count */
+	ret = __add_mux_ref(ref, mux);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Check if this mux has its own driver
+	 * or this is standard gpio mux.
+	 */
+	if (async_mux_node) {
+		/* Add async mux to the waiting list */
+		list_add(&mux->list, &mux_waiting_list);
+	} else {
+		/* Create default gpio mux */
+		ret = led_flash_gpio_mux_create(&gpio_mux, gate->mux_node);
+		if (ret < 0)
+			goto err_gpio_mux_init;
+
+		/* Register gpio mux device */
+		mux->private_data = gpio_mux;
+		mux->ops = &gpio_mux_ops;
+		list_add(&mux->list, &mux_bound_list);
+	}
+
+	return 0;
+
+err_gpio_mux_init:
+	kfree(mux);
+	kfree(ref);
+
+	return ret;
+}
+
+static void led_flash_manager_release_mux(struct led_flash_mux *mux)
+{
+	if (mux->ops->release_private_data) {
+		if (mux->ops->release_private_data)
+			mux->ops->release_private_data(mux->private_data);
+		list_del(&mux->list);
+		kfree(mux);
+	}
+}
+
+static void led_flash_manager_remove_flash_refs(
+					struct led_classdev_flash *flash)
+{
+	struct led_flash_mux_ref *ref, *rn;
+	struct led_flash_mux *mux, *mn;
+
+	/*
+	 * Remove references to the flash from
+	 * all the muxes on the list.
+	 */
+	list_for_each_entry_safe(mux, mn, &mux_bound_list, list)
+		/* Seek for matching flash ref */
+		list_for_each_entry_safe(ref, rn, &mux->refs, list)
+			if (ref->flash == flash) {
+				/* Decrement reference count */
+				__remove_mux_ref(ref, mux);
+				kfree(ref);
+				/*
+				 * Release mux if there are no more
+				 * references pointing to it.
+				 */
+				if (list_empty(&mux->refs))
+					led_flash_manager_release_mux(mux);
+			}
+}
+
+int led_flash_manager_setup_strobe(struct led_classdev_flash *flash,
+				 bool external)
+{
+	u32 flash_timeout = flash->timeout.val / 1000;
+	int ret;
+
+	mutex_lock(&fm_lock);
+
+	/* Setup muxes */
+	ret = led_flash_manager_set_external_strobe(flash, external);
+	if (ret < 0)
+		goto unlock;
+
+	/*
+	 * Trigger software strobe under flash manager lock
+	 * to make sure that nobody will reconfigure muxes
+	 * in the meantime.
+	 */
+	if (!external) {
+		ret = flash->ops->strobe_set(flash, true);
+		if (ret < 0)
+			goto unlock;
+	}
+
+	/*
+	 * Hold lock for the time of strobing if the
+	 * LED Flash Class device depends on shared muxes.
+	 */
+	if (flash->num_shared_muxes > 0) {
+		msleep(flash_timeout);
+		/*
+		 * external strobe is turned on only for the time of
+		 * this call if there are shared muxes involved
+		 * in strobe signal routing.
+		 */
+		flash->external_strobe = false;
+	}
+
+unlock:
+	mutex_unlock(&fm_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(led_flash_manager_setup_strobe);
+
+int led_flash_manager_create_sysfs_attrs(struct led_classdev_flash *flash)
+{
+	struct led_classdev *led_cdev = &flash->led_cdev;
+	int ret;
+
+	/*
+	 * Create sysfs attribtue for indicating if activation of
+	 * software or external flash strobe will block the caller.
+	 */
+	sysfs_attr_init(&dev_attr_blocking_strobe.attr);
+	ret = sysfs_create_file(&led_cdev->dev->kobj,
+					&dev_attr_blocking_strobe.attr);
+	if (ret < 0)
+		return ret;
+
+	/* Create strobe_providerN attributes */
+	ret = led_flash_manager_create_providers_attrs(flash);
+	if (ret < 0)
+		goto err_create_provider_attrs;
+
+	return 0;
+
+err_create_provider_attrs:
+	sysfs_remove_file(&led_cdev->dev->kobj,
+				&dev_attr_blocking_strobe.attr);
+
+	return ret;
+}
+
+void led_flash_manager_remove_sysfs_attrs(struct led_classdev_flash *flash)
+{
+	struct led_classdev *led_cdev = &flash->led_cdev;
+
+	led_flash_manager_remove_providers_attrs(flash);
+	sysfs_remove_file(&led_cdev->dev->kobj,
+				&dev_attr_blocking_strobe.attr);
+}
+
+int led_flash_manager_register_flash(struct led_classdev_flash *flash,
+				     struct device_node *node)
+{
+	struct led_flash_strobe_provider *provider;
+	struct led_flash_strobe_gate *gate;
+	struct led_classdev_flash *fl;
+	struct led_classdev *led_cdev = &flash->led_cdev;
+	int ret = 0;
+
+	if (!flash || !node)
+		return -EINVAL;
+
+	mutex_lock(&fm_lock);
+
+	/* Don't allow to register the same flash more than once */
+	list_for_each_entry(fl, &flash_list, list)
+		if (fl == flash)
+			goto unlock;
+
+	INIT_LIST_HEAD(&flash->software_strobe_gates);
+	INIT_LIST_HEAD(&flash->strobe_providers);
+
+	ret = of_led_flash_manager_parse_dt(flash, node);
+	if (ret < 0)
+		goto unlock;
+
+	/*
+	 * Register mux devices declared by the flash device
+	 * if they have not been yet known to the flash manager.
+	 */
+	list_for_each_entry(gate, &flash->software_strobe_gates, list) {
+		ret = led_flash_manager_parse_mux_node(flash, gate);
+		if (ret < 0)
+			goto err_parse_mux_node;
+	}
+
+	list_for_each_entry(provider, &flash->strobe_providers, list) {
+		list_for_each_entry(gate, &provider->strobe_gates, list) {
+			ret = led_flash_manager_parse_mux_node(flash, gate);
+			if (ret < 0)
+				goto err_parse_mux_node;
+		}
+		++flash->num_strobe_providers;
+	}
+
+	/*
+	 * It doesn't make sense to register in the flash manager
+	 * if there are no strobe providers defined.
+	 */
+	if (flash->num_strobe_providers == 0)
+		goto unlock;
+
+	led_cdev->flags |= LED_DEV_CAP_FL_MANAGER;
+	flash->has_external_strobe = true;
+	flash->strobe_provider_id = 0;
+
+	ret = led_flash_manager_create_sysfs_attrs(flash);
+	if (ret < 0)
+		goto err_parse_mux_node;
+
+	/* Add flash to the list of flashes maintained by the flash manager. */
+	list_add_tail(&flash->list, &flash_list);
+
+	mutex_unlock(&fm_lock);
+
+	return ret;
+
+err_parse_mux_node:
+	led_flash_manager_remove_flash_refs(flash);
+	of_led_flash_manager_release_dt_data(flash);
+unlock:
+	mutex_unlock(&fm_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(led_flash_manager_register_flash);
+
+void led_flash_manager_unregister_flash(struct led_classdev_flash *flash)
+{
+	struct led_classdev_flash *fl, *n;
+	bool found_flash = false;
+
+	mutex_lock(&fm_lock);
+
+	/* Remove flash from the list, if found */
+	list_for_each_entry_safe(fl, n, &flash_list, list)
+		if (fl == flash) {
+			found_flash = true;
+			break;
+		}
+
+	if (!found_flash)
+		goto unlock;
+
+	list_del(&fl->list);
+
+	led_flash_manager_remove_sysfs_attrs(flash);
+
+	/* Remove all references to the flash */
+	led_flash_manager_remove_flash_refs(flash);
+	of_led_flash_manager_release_dt_data(flash);
+
+unlock:
+	mutex_unlock(&fm_lock);
+}
+EXPORT_SYMBOL_GPL(led_flash_manager_unregister_flash);
+
+MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LED Flash Manager");
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index 0545530..e8380bb 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -41,6 +41,11 @@ ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
 
 	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);
 	len = strlen(trigger_name);
diff --git a/drivers/leds/of_led_flash_manager.c b/drivers/leds/of_led_flash_manager.c
new file mode 100644
index 0000000..d22d853
--- /dev/null
+++ b/drivers/leds/of_led_flash_manager.c
@@ -0,0 +1,155 @@
+/*
+ * LED flash manager of 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 <linux/device.h>
+#include <linux/led-class-flash.h>
+#include <linux/of.h>
+#include <linux/of_led_flash_manager.h>
+#include <linux/slab.h>
+
+static int __parse_strobe_gate_node(struct led_classdev_flash *flash,
+				    struct device_node *node,
+				    struct list_head *gates)
+{
+	struct device_node *mux_node, *subnode;
+	struct led_flash_strobe_gate *gate;
+	struct led_classdev *led_cdev = &flash->led_cdev;
+	u32 line_id;
+	int ret, num_gates = 0;
+
+	/* Get node mux node */
+	mux_node = of_parse_phandle(node, "mux", 0);
+	if (!mux_node)
+		return -EINVAL;
+
+	/* Get the value the mux has to be written to open the gate */
+	ret = of_property_read_u32(node, "mux-line-id", &line_id);
+	if (ret < 0)
+		return ret;
+
+	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+	if (!gate)
+		return -ENOMEM;
+
+	gate->mux_node = mux_node;
+	gate->line_id = line_id;
+	list_add_tail(&gate->list, gates);
+
+	/* Parse nested gate nodes */
+	for_each_available_child_of_node(node, subnode) {
+		if (!of_node_ncmp(subnode->name, "gate", 4)) {
+			ret = __parse_strobe_gate_node(flash, subnode, gates);
+			if (ret < 0) {
+				dev_dbg(led_cdev->dev->parent,
+				"Failed to parse gate node (%d)\n",
+				ret);
+				goto err_parse_gate;
+			}
+
+			if (++num_gates > 1) {
+				dev_dbg(led_cdev->dev->parent,
+				"Only one available child gate is allowed.\n");
+				ret = -EINVAL;
+				goto err_parse_gate;
+			}
+		}
+	}
+
+	return 0;
+
+err_parse_gate:
+	kfree(gate);
+
+	return ret;
+}
+
+static int __parse_strobe_provider_node(struct led_classdev_flash *flash,
+			       struct device_node *node)
+{
+	struct device_node *provider_node;
+	struct led_flash_strobe_provider *provider;
+	int ret;
+
+	/* Create strobe provider representation */
+	provider = kzalloc(sizeof(*provider), GFP_KERNEL);
+	if (!provider)
+		return -ENOMEM;
+
+	/* Get phandle of the device generating strobe source signal */
+	provider_node = of_parse_phandle(node, "strobe-provider", 0);
+
+	/* provider property may be absent */
+	if (provider_node) {
+		/* Use compatible property as a strobe provider name */
+		ret = of_property_read_string(provider_node, "compatible",
+					(const char **) &provider->name);
+		if (ret < 0)
+			goto err_read_name;
+	}
+
+	INIT_LIST_HEAD(&provider->strobe_gates);
+
+	list_add_tail(&provider->list, &flash->strobe_providers);
+
+	ret = __parse_strobe_gate_node(flash, node, &provider->strobe_gates);
+	if (ret < 0)
+		goto err_read_name;
+
+	return 0;
+
+err_read_name:
+	kfree(provider);
+
+	return ret;
+}
+
+int of_led_flash_manager_parse_dt(struct led_classdev_flash *flash,
+				  struct device_node *node)
+{
+	struct device_node *subnode;
+	int ret;
+
+	for_each_available_child_of_node(node, subnode) {
+		if (!of_node_cmp(subnode->name, "gate-software-strobe")) {
+			ret = __parse_strobe_gate_node(flash, subnode,
+						&flash->software_strobe_gates);
+			if (ret < 0)
+				return ret;
+		}
+		if (!of_node_ncmp(subnode->name, "gate-external-strobe", 20)) {
+			ret = __parse_strobe_provider_node(flash, subnode);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_led_flash_manager_parse_dt);
+
+void of_led_flash_manager_release_dt_data(struct led_classdev_flash *flash)
+{
+	struct list_head *gate_list;
+	struct led_flash_strobe_gate *gate, *gn;
+	struct led_flash_strobe_provider *provider, *sn;
+
+	gate_list = &flash->software_strobe_gates;
+	list_for_each_entry_safe(gate, gn, gate_list, list)
+		kfree(gate);
+
+	list_for_each_entry_safe(provider, sn, &flash->strobe_providers, list) {
+		list_for_each_entry_safe(gate, gn, &provider->strobe_gates,
+								list)
+			kfree(gate);
+		kfree(provider);
+	}
+}
+EXPORT_SYMBOL_GPL(of_led_flash_manager_release_dt_data);
diff --git a/include/linux/led-class-flash.h b/include/linux/led-class-flash.h
new file mode 100644
index 0000000..6ed7e8a
--- /dev/null
+++ b/include/linux/led-class-flash.h
@@ -0,0 +1,290 @@
+/*
+ * LED Flash Class 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.
+ *
+ */
+#ifndef __LINUX_FLASH_LEDS_H_INCLUDED
+#define __LINUX_FLASH_LEDS_H_INCLUDED
+
+#include <linux/leds.h>
+
+struct v4l2_flash_ops;
+struct led_classdev_flash;
+struct device_node;
+
+/*
+ * Supported led fault bits - must be kept in synch
+ * with V4L2_FLASH_FAULT bits.
+ */
+#define LED_FAULT_OVER_VOLTAGE		 V4L2_FLASH_FAULT_OVER_VOLTAGE
+#define LED_FAULT_TIMEOUT		 V4L2_FLASH_FAULT_TIMEOUT
+#define LED_FAULT_OVER_TEMPERATURE	 V4L2_FLASH_FAULT_OVER_TEMPERATURE
+#define LED_FAULT_SHORT_CIRCUIT		 V4L2_FLASH_FAULT_SHORT_CIRCUIT
+#define LED_FAULT_OVER_CURRENT		 V4L2_FLASH_FAULT_OVER_CURRENT
+#define LED_FAULT_INDICATOR		 V4L2_FLASH_FAULT_INDICATOR
+#define LED_FAULT_UNDER_VOLTAGE		 V4L2_FLASH_FAULT_UNDER_VOLTAGE
+#define LED_FAULT_INPUT_VOLTAGE		 V4L2_FLASH_FAULT_INPUT_VOLTAGE
+#define LED_FAULT_LED_OVER_TEMPERATURE	 V4L2_FLASH_OVER_TEMPERATURE
+
+#define LED_FLASH_MAX_SYSFS_GROUPS 6
+
+struct led_flash_ops {
+	/* set flash brightness */
+	int (*flash_brightness_set)(struct led_classdev_flash *flash,
+					u32 brightness);
+	/* get flash brightness */
+	int (*flash_brightness_get)(struct led_classdev_flash *flash,
+					u32 *brightness);
+	/* set flash indicator brightness */
+	int (*indicator_brightness_set)(struct led_classdev_flash *flash,
+					u32 brightness);
+	/* get flash indicator brightness */
+	int (*indicator_brightness_get)(struct led_classdev_flash *flash,
+					u32 *brightness);
+	/* set flash strobe state */
+	int (*strobe_set)(struct led_classdev_flash *flash, bool state);
+	/* get flash strobe state */
+	int (*strobe_get)(struct led_classdev_flash *flash, bool *state);
+	/* set flash timeout */
+	int (*timeout_set)(struct led_classdev_flash *flash, u32 timeout);
+	/* setup the device to strobe the flash upon a pin state assertion */
+	int (*external_strobe_set)(struct led_classdev_flash *flash,
+					bool enable);
+	/* get the flash LED fault */
+	int (*fault_get)(struct led_classdev_flash *flash, u32 *fault);
+};
+
+/*
+ * Current value of a flash setting along
+ * with its constraints.
+ */
+struct led_flash_setting {
+	/* maximum allowed value */
+	u32 min;
+	/* maximum allowed value */
+	u32 max;
+	/* step value */
+	u32 step;
+	/* current value */
+	u32 val;
+};
+
+/*
+ * Aggregated flash settings - designed for ease
+ * of passing initialization data to the clients
+ * wrapping a LED Flash class device.
+ */
+struct led_flash_config {
+	struct led_flash_setting torch_brightness;
+	struct led_flash_setting flash_brightness;
+	struct led_flash_setting indicator_brightness;
+	struct led_flash_setting flash_timeout;
+	u32 flash_faults;
+};
+
+struct led_classdev_flash {
+	/* led-flash-manager uses it to link flashes */
+	struct list_head list;
+	/* led class device */
+	struct led_classdev led_cdev;
+	/* flash led specific ops */
+	const struct led_flash_ops *ops;
+
+	/* flash sysfs groups */
+	struct attribute_group *sysfs_groups[LED_FLASH_MAX_SYSFS_GROUPS];
+
+	/* flash brightness value in microamperes along with its constraints */
+	struct led_flash_setting brightness;
+
+	/* timeout value in microseconds along with its constraints */
+	struct led_flash_setting timeout;
+
+	/*
+	 * Indicator brightness value in microamperes along with
+	 * its constraints - this is an optional setting and must
+	 * be allocated by the driver if the device supports privacy
+	 * indicator led.
+	 */
+	struct led_flash_setting *indicator_brightness;
+
+	/*
+	 * determines if a device supports external
+	 * flash strobe sources
+	 */
+	bool has_external_strobe;
+
+	/* If true the flash led is strobed from external source */
+	bool external_strobe;
+
+	/* Flash manager data */
+	/* Strobe signals topology data */
+	struct list_head software_strobe_gates;
+	struct list_head strobe_providers;
+
+	/* identifier of the selected strobe signal provider */
+	int strobe_provider_id;
+
+	/* number of defined strobe providers */
+	int num_strobe_providers;
+
+	/*
+	 * number of muxes that this device shares
+	 * with other LED Flash Class devices.
+	 */
+	int num_shared_muxes;
+};
+
+static inline struct led_classdev_flash *lcdev_to_flash(
+						struct led_classdev *lcdev)
+{
+	return container_of(lcdev, struct led_classdev_flash, led_cdev);
+}
+
+/**
+ * led_classdev_flash_register - register a new object of led_classdev_flash class
+				 with support for flash LEDs
+ * @parent: the device to register
+ * @flash: the led_classdev_flash structure for this device
+ * @node: device tree node of the LED Flash Class device - it must be
+	  initialized if the device is to be registered in the flash manager
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+int led_classdev_flash_register(struct device *parent,
+				struct led_classdev_flash *flash,
+				struct device_node *node);
+
+/**
+ * led_classdev_flash_unregister - unregisters an object of led_classdev_flash class
+				   with support for flash LEDs
+ * @flash: the flash led device to unregister
+ *
+ * Unregisters a previously registered via led_classdev_flash_register object
+ */
+void led_classdev_flash_unregister(struct led_classdev_flash *flash);
+
+/**
+ * led_set_flash_strobe - setup flash strobe
+ * @flash: 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_flash *flash,
+				bool state);
+
+/**
+ * led_get_flash_strobe - get flash strobe status
+ * @flash: the LED to query
+ * @state: 1 - flash is strobing, 0 - flash is off
+ *
+ * Check whether the flash is strobing at the moment or not.
+ *
+u* Returns: 0 on success or negative error value on failure
+ */
+extern int led_get_flash_strobe(struct led_classdev_flash *flash,
+				bool *state);
+/**
+ * led_set_flash_brightness - set flash LED brightness
+ * @flash: the LED to set
+ * @brightness: the brightness to set it to
+ *
+ * Returns: 0 on success or negative error value on failure
+ *
+ * Set a flash LED's brightness.
+ */
+extern int led_set_flash_brightness(struct led_classdev_flash *flash,
+					u32 brightness);
+
+/**
+ * led_update_flash_brightness - update flash LED brightness
+ * @flash: 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_flash *flash);
+
+/**
+ * led_set_flash_timeout - set flash LED timeout
+ * @flash: the LED to set
+ * @timeout: the flash timeout to set it to
+ *
+ * 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.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+extern int led_set_flash_timeout(struct led_classdev_flash *flash,
+					u32 timeout);
+
+/**
+ * led_get_flash_fault - get the flash LED fault
+ * @flash: the LED to query
+ * @fault: bitmask containing flash faults
+ *
+ * Get the flash LED fault.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+extern int led_get_flash_fault(struct led_classdev_flash *flash,
+					u32 *fault);
+
+/**
+ * led_set_external_strobe - set the flash LED external_strobe mode
+ * @flash: the LED to set
+ * @enable: the state to set it to
+ *
+ * Enable/disable strobing the flash LED with use of external source
+ *
+  Returns: 0 on success or negative error value on failure
+ */
+extern int led_set_external_strobe(struct led_classdev_flash *flash,
+					bool enable);
+
+/**
+ * led_set_indicator_brightness - set indicator LED brightness
+ * @flash: the LED to set
+ * @brightness: the brightness to set it to
+ *
+ * Set an indicator LED's brightness.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+extern int led_set_indicator_brightness(struct led_classdev_flash *flash,
+					u32 led_brightness);
+
+/**
+ * led_update_indicator_brightness - update flash indicator LED brightness
+ * @flash: the LED to query
+ *
+ * Get a flash indicator LED's current brightness and update
+ * led_flash->indicator_brightness member with the obtained value.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+extern int led_update_indicator_brightness(struct led_classdev_flash *flash);
+
+#ifdef CONFIG_V4L2_FLASH_LED_CLASS
+/**
+ * led_get_v4l2_flash_ops - get ops for controlling LED Flash Class
+			    device with use of V4L2 Flash controls
+ * Returns: v4l2_flash_ops
+ */
+const struct v4l2_flash_ops *led_get_v4l2_flash_ops(void);
+#else
+#define led_get_v4l2_flash_ops() (0)
+#endif
+
+#endif	/* __LINUX_FLASH_LEDS_H_INCLUDED */
diff --git a/include/linux/led-flash-gpio-mux.h b/include/linux/led-flash-gpio-mux.h
new file mode 100644
index 0000000..c56d38f
--- /dev/null
+++ b/include/linux/led-flash-gpio-mux.h
@@ -0,0 +1,68 @@
+/*
+ * LED Flash Class gpio mux
+ *
+ *	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 _LED_FLASH_GPIO_MUX_H
+#define _LED_FLASH_GPIO_MUX_H
+
+struct device_node;
+
+struct list_head;
+
+/**
+ * struct led_flash_gpio_mux_selector - element of gpio mux selectors list
+ * @list: links mux selectors
+ * @gpio: mux selector gpio
+ */
+struct led_flash_gpio_mux_selector {
+	struct list_head list;
+	int gpio;
+};
+
+/**
+ * struct led_flash_gpio_mux - gpio mux
+ * @selectors:	mux selectors
+ */
+struct led_flash_gpio_mux {
+	struct list_head selectors;
+};
+
+/**
+ * led_flash_gpio_mux_create - create gpio mux
+ * @new_mux: created mux
+ * @mux_node: device tree node with gpio definitions
+ *
+ * Create V4L2 subdev wrapping given LED subsystem device.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+int led_flash_gpio_mux_create(struct led_flash_gpio_mux **new_mux,
+			struct device_node *mux_node);
+
+/**
+ * led_flash_gpio_mux_release - release gpio mux
+ * @gpio_mux: mux to be released
+ *
+ * Create V4L2 subdev wrapping given LED subsystem device.
+ */
+void led_flash_gpio_mux_release(void *gpio_mux);
+
+/**
+ * led_flash_gpio_mux_select_line - select mux line
+ * @line_id: id of the line to be selected
+ * @mux: mux to be set
+ *
+ * Create V4L2 subdev wrapping given LED subsystem device.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+int led_flash_gpio_mux_select_line(u32 line_id, void *mux);
+
+#endif /* _LED_FLASH_GPIO_MUX_H */
diff --git a/include/linux/led-flash-manager.h b/include/linux/led-flash-manager.h
new file mode 100644
index 0000000..174eedb
--- /dev/null
+++ b/include/linux/led-flash-manager.h
@@ -0,0 +1,121 @@
+/*
+ *  LED Flash Manager for maintaining LED Flash Class devices
+ *  along with their corresponding muxes that faciliate dynamic
+ *  reconfiguration of the strobe signal source.
+ *
+ *	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 _LED_FLASH_MANAGER_H
+#define _LED_FLASH_MANAGER_H
+
+#include <linux/types.h>
+
+struct list_head;
+
+struct led_flash_mux_ops {
+	/* select mux line */
+	int (*select_line)(u32 line_id, void *mux);
+	/*
+	 * release private mux data - it is intended for gpio muxes
+	 * only and mustn't be initialized by asynchronous muxes.
+	 */
+	void (*release_private_data)(void *mux);
+};
+
+/**
+ * struct led_flash_mux_ref - flash mux reference
+ * @list:	links flashes pointing to a mux
+ * @flash:	flash holding a mux reference
+ */
+struct led_flash_mux_ref {
+	struct list_head list;
+	struct led_classdev_flash *flash;
+};
+
+/**
+ * struct led_flash_mux - flash mux used for routing strobe signal
+ * @list:		list of muxes declared by registered flashes
+ * @node:		mux Device Tree node
+ * @private_data:	mux internal data
+ * @ops:		ops facilitating mux state manipulation
+ * @refs:		list of flashes using the mux
+ * @num_refs:		number of flashes using the mux
+ * @owner:		module owning an async mux driver
+ */
+struct led_flash_mux {
+	struct list_head list;
+	struct device_node *node;
+	void *private_data;
+	struct led_flash_mux_ops *ops;
+	struct list_head refs;
+	int num_refs;
+	struct module *owner;
+};
+
+/**
+ * led_flash_manager_setup_strobe - setup flash strobe
+ * @flash: the LED Flash Class device to strobe
+ * @external: true - setup external strobe,
+ *	      false - setup software strobe
+ *
+ * Configure muxes to route relevant strobe signals to the
+ * flash led device and strobe the flash if software strobe
+ * is to be activated. If the flash device depends on shared
+ * muxes the caller is blocked for the flash_timeout period.
+ *
+ * Returns: 0 on success or negative error value on failure.
+ */
+int led_flash_manager_setup_strobe(struct led_classdev_flash *flash,
+				   bool external);
+
+/**
+ * led_flash_manager_bind_async_mux - bind asynchronous mulitplexer
+ * @async_mux: mux registration data
+ *
+ * Notify the flash manager that an asychronous mux has been probed.
+ *
+ * Returns: 0 on success or negative error value on failure.
+ */
+int led_flash_manager_bind_async_mux(struct led_flash_mux *async_mux);
+
+/**
+ * led_flash_manager_unbind_async_mux - unbind asynchronous mulitplexer
+ * @mux_node: device node of the mux to be unbound
+ *
+ * Notify the flash manager that an asychronous mux has been removed.
+ *
+ * Returns: 0 on success or negative error value on failure.
+ */
+int led_flash_manager_unbind_async_mux(struct device_node *mux_node);
+
+/**
+ * led_flash_manager_register_flash - register LED Flash Class device
+				      in the flash manager
+ * @flash: the LED Flash Class device to be registered
+ * @node: Device Tree node - it is expected to contain information
+ *	  about strobe signal topology
+ *
+ * Register LED Flash Class device and retrieve information
+ * about related strobe signals topology.
+ *
+ * Returns: 0 on success or negative error value on failure.
+ */
+int led_flash_manager_register_flash(struct led_classdev_flash *flash,
+				     struct device_node *node);
+
+/**
+ * led_flash_manager_unregister_flash - unregister LED Flash Class device
+ *					from the flash manager
+ * @flash: the LED Flash Class device to be unregistered
+ *
+ * Unregister LED Flash Class device from the flash manager.
+ */
+void led_flash_manager_unregister_flash(struct led_classdev_flash *flash);
+
+#endif /* _LED_FLASH_MANAGER_H */
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 9bea9e6..1f12d36 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -45,6 +45,9 @@ struct led_classdev {
 #define LED_BLINK_INVERT	(1 << 19)
 #define LED_SYSFS_LOCK		(1 << 20)
 #define LED_DEV_CAP_TORCH	(1 << 21)
+#define LED_DEV_CAP_FLASH	(1 << 22)
+#define LED_DEV_CAP_INDICATOR	(1 << 23)
+#define LED_DEV_CAP_FL_MANAGER	(1 << 24)
 
 	/* Set LED brightness level */
 	/* Must not sleep, use a workqueue if needed */
@@ -83,6 +86,7 @@ struct led_classdev {
 	unsigned long		 blink_delay_on, blink_delay_off;
 	struct timer_list	 blink_timer;
 	int			 blink_brightness;
+	void			(*flash_resume)(struct led_classdev *led_cdev);
 
 	struct work_struct	set_brightness_work;
 	int			delayed_set_value;
diff --git a/include/linux/of_led_flash_manager.h b/include/linux/of_led_flash_manager.h
new file mode 100644
index 0000000..d27cb46
--- /dev/null
+++ b/include/linux/of_led_flash_manager.h
@@ -0,0 +1,80 @@
+/*
+ * LED flash manager of 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 _OF_LED_FLASH_MANAGER_H
+#define _OF_LED_FLASH_MANAGER_H
+
+#include <linux/sysfs.h>
+#include <linux/device.h>
+
+struct led_classdev_flash;
+struct device_node;
+struct list_head;
+
+#define MAX_ATTR_NAME 18 /* allows for strobe providers ids up to 999 */
+
+/**
+ * struct led_flash_strobe_gate - strobe signal gate
+ * @list:	links gates
+ * @mux_node:	gate's parent mux
+ * @line_id:	id of a mux line that the gate represents
+ */
+struct led_flash_strobe_gate {
+	struct list_head list;
+	struct device_node *mux_node;
+	u32 line_id;
+};
+
+/**
+ * struct led_flash_strobe_provider - external strobe signal provider that
+				      may be associated with the flash device
+ * @list:		links strobe providers
+ * @strobe_gates:	list of gates that route strobe signal
+			from the strobe provider to the flash device
+ * @node:		device node of the strobe provider device
+ */
+struct led_flash_strobe_provider {
+	struct list_head list;
+	struct list_head strobe_gates;
+	const char *name;
+	struct device_attribute attr;
+	char attr_name[MAX_ATTR_NAME];
+	bool attr_registered;
+};
+
+/**
+ * of_led_flash_manager_parse_dt - parse flash manager data of
+ *				   a LED Flash Class device DT node
+ *
+ * @flash: the LED Flash Class device to store the parsed data in
+ * @node: device node to parse
+ *
+ * Parse the multiplexers' settings that need to be applied for
+ * opening a route to all the flash strobe signals available for
+ * the device.
+ *
+ * Returns: 0 on success or negative error value on failure
+ */
+int of_led_flash_manager_parse_dt(struct led_classdev_flash *flash,
+					 struct device_node *node);
+
+
+/**
+ * of_led_flash_manager_release_dt_data - release parsed DT data
+ *
+ * @flash: the LED Flash Class device to release data from
+ *
+ * Release data structures containing information about strobe
+ * source routes available for the device.
+ */
+void of_led_flash_manager_release_dt_data(struct led_classdev_flash *flash);
+
+#endif /* _OF_LED_FLASH_MANAGER_H */
-- 
1.7.9.5


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

* [PATCH/RFC v4 09/21] Documentation: leds: Add description of LED Flash Class extension
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
                   ` (7 preceding siblings ...)
  2014-07-11 14:04 ` [PATCH/RFC v4 08/21] leds: Add sysfs and kernel internal API for flash LEDs Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-11 14:04 ` [PATCH/RFC v4 10/21] Documentation: leds: add exemplary asynchronous mux driver Jacek Anaszewski
                   ` (13 subsequent siblings)
  22 siblings, 0 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Bryan Wu, Richard Purdie

The documentation being added contains overall description of the
LED Flash Class and the related sysfs attributes. There are also
chapters devoted specifically to the Flash Manager feature.

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-flash.txt |  126 +++++++++++++++++++++++++++++++
 1 file changed, 126 insertions(+)
 create mode 100644 Documentation/leds/leds-class-flash.txt

diff --git a/Documentation/leds/leds-class-flash.txt b/Documentation/leds/leds-class-flash.txt
new file mode 100644
index 0000000..0927804
--- /dev/null
+++ b/Documentation/leds/leds-class-flash.txt
@@ -0,0 +1,126 @@
+
+Flash LED handling under Linux
+==============================
+
+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 register in the LED subsystem
+with led_classdev_flash_register to gain flash capabilities.
+
+Following sysfs attributes are exposed for controlling flash led devices:
+
+	- flash_brightness - flash LED brightness in microamperes (RW)
+	- max_flash_brightness - maximum available flash LED brightness (RO)
+	- indicator_brightness - privacy LED brightness in microamperes (RW)
+	- max_indicator_brightness - maximum privacy LED brightness in
+				     microamperes (RO)
+	- flash_timeout - flash strobe duration in microseconds (RW)
+	- max_flash_timeout - maximum available flash strobe duration (RO)
+	- flash_strobe - flash strobe state (RW)
+	- external_strobe - some devices expose dedicated hardware pins for
+			    triggering a flash LED - this attribute allows to
+			    set this mode (RW)
+	- flash_fault - bitmask of flash faults that may have occurred,
+			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
+
+The LED subsystem driver can be controlled also from the level of VideoForLinux2
+subsystem. In order to enable this the CONFIG_V4L2_FLASH_LED_CLASS symbol has to
+be defined in the kernel config. The driver must call v4l2_flash_init function
+to get registered in the V4L2 subsystem. On remove v4l2_flash_release function
+has to be called (see <media/v4l2-flash.h>).
+
+After proper initialization V4L2 Flash sub-device is created. The 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.
+
+
+Flash Manager
+=============
+
+Flash LED devices often provide two ways of strobing the flash: software
+(e.g. by setting a bit in a register) and hardware, by asserting dedicated pin,
+which is usually connected to a camera sensor device. There are also flash led
+devices which support only hardware strobing - in such cases a multiplexer
+device is employed to route the flash strobe signal either from the SoC GPIO or
+from the external strobe signal provider, e.g. camera sensor.
+Use of multiplexers allows also to change the flash-sensor connection
+dynamically if there is more than one flash or external strobe signal provider
+available on the board.
+
+In order to address the aforementioned cases the Flash Manager functionality
+has been introduced. Flash Manager is a part of LED Flash Class. It maintains
+information about flashes, software and external strobe signal providers and
+multiplexers that route strobe signals among them.
+
+To register a LED Flash Class device in the flash manager the device_node
+of a flash device has to be passed as the third argument to the
+led_classdev_flash_register function. The device_node is expected to include one
+gate-software-strobe subnode and at least one gate-external-strobeN subnode.
+Besides that there must defined a flash_muxes node aggregating all the
+multiplexers that can be referenced to by the flash led devices.
+(for mote details see Documentation/devicetree/bindings/leds/
+leds-flash-manager.txt).
+
+Flash manager adds following sysfs attributes to the LED Flash Clash
+device sysfs interface:
+
+	- strobe_provider - id of the external strobe signal provider associated
+                            with the flash led device. It is created only if
+			    there is more than one external strobe signal
+			    provider defined (RW).
+	- strobe_providerN - names of the strobe signal providers available
+			     for the flash led device, where N is the
+			     identifier of a strobe signal provider (RO)
+	- blocking_strobe - informs if enabling either software or external
+			    strobe will block the caller for the expected
+			    time of strobing (1 - true, 0 - false). The call
+			    needs to be blocking if the multiplexers involved
+			    in the strobe signal routing are connected to more
+			    than one flash led device. In such a case flash
+			    manager becomes locked for the expected time of
+			    strobing to prevent reconfigurarion of multiplexers
+			    by the other flash led devices willing to strobe
+			    in the same time (RO).
+
+
+LED Flash Class multiplexers
+============================
+
+Multiplexers are an indispensable part of the Flash Manager. Flash Manager has
+its own led_flash_gpio_mux* helpers for creating, releasing and operating on
+the simple gpio driven multiplexers (the ones whose lines are selected by
+changing the state of its selector pins) and the user doesn't have to bother
+with it.
+
+It is however possible that a more advanced device will be used for routing
+strobe signals. This kind of devices are known to the Flash Manager as
+"asynchronous muxes" and can be registered in runtime with use of
+led_flash_manager_bind_async_mux API and unregistered with
+led_flash_manager_unbind_async_mux. (see Documentation/leds/leds-async-mux.c
+for the exemplary implementation of the async mux driver).
+
+If a LED Flash Class device declares dependency on an async mux, then strobing
+the flash, or setting external strobe, will succeed only wnen the async mux
+has been bound to the Flash Manager. Async mux module, once bound, can be
+removed only after all LED Flash Class devices depending on it are unregistered
+from the Flash Manager.
-- 
1.7.9.5


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

* [PATCH/RFC v4 10/21] Documentation: leds: add exemplary asynchronous mux driver
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
                   ` (8 preceding siblings ...)
  2014-07-11 14:04 ` [PATCH/RFC v4 09/21] Documentation: leds: Add description of LED Flash Class extension Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-11 14:04 ` [PATCH/RFC v4 11/21] DT: leds: Add flash led devices related properties Jacek Anaszewski
                   ` (12 subsequent siblings)
  22 siblings, 0 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Bryan Wu, Richard Purdie

Exemplary driver showing usage of the Flash Manager API
for registering/unregistering asynchronous multiplexers

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-async-mux.c |   65 +++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)
 create mode 100644 Documentation/leds/leds-async-mux.c

diff --git a/Documentation/leds/leds-async-mux.c b/Documentation/leds/leds-async-mux.c
new file mode 100644
index 0000000..ee35d2f
--- /dev/null
+++ b/Documentation/leds/leds-async-mux.c
@@ -0,0 +1,65 @@
+/*
+ * Exemplary driver showing usage of the Flash Manager API
+ * for registering/unregistering asynchronous multiplexers.
+ *
+ *	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/platform_device.h>
+#include <linux/module.h>
+#include <linux/led-class-flash.h>
+#include <linux/led-flash-manager.h>
+#include <linux/leds.h>
+#include <linux/of.h>
+
+static int led_async_mux_select_line(u32 line_id, void *mux)
+{
+	pr_info("led_async_mux_select_line line_id: %d\n", line_id);
+	return 0;
+}
+
+struct led_flash_mux_ops mux_ops = {
+	.select_line = led_async_mux_select_line,
+};
+
+static int led_async_mux_probe(struct platform_device *pdev)
+{
+	struct led_flash_mux mux;
+
+	mux.ops = &mux_ops;
+	mux.owner = THIS_MODULE;
+	mux.node = pdev->dev->of_node;
+
+	return led_flash_manager_bind_async_mux(&mux);
+}
+
+static int led_async_mux_remove(struct platform_device *pdev)
+{
+	return led_flash_manager_unbind_async_mux(pdev->dev->of_node);
+}
+
+static struct of_device_id led_async_mux_dt_match[] = {
+	{.compatible = "led-async-mux"},
+	{},
+};
+
+static struct platform_driver led_async_mux_driver = {
+	.probe		= led_async_mux_probe,
+	.remove		= led_async_mux_remove,
+	.driver		= {
+		.name		= "led-async-mux",
+		.owner		= THIS_MODULE,
+		.of_match_table = led_async_mux_dt_match,
+	},
+};
+
+module_platform_driver(led_async_mux_driver);
+
+MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
+MODULE_DESCRIPTION("LED async mux");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5


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

* [PATCH/RFC v4 11/21] DT: leds: Add flash led devices related properties
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
                   ` (9 preceding siblings ...)
  2014-07-11 14:04 ` [PATCH/RFC v4 10/21] Documentation: leds: add exemplary asynchronous mux driver Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-11 14:04 ` [PATCH/RFC v4 12/21] DT: Add documentation for LED Class Flash Manger Jacek Anaszewski
                   ` (11 subsequent siblings)
  22 siblings, 0 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Stephen Warren,
	Grant Likely, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala

Addition of a LED Flash Class extension entails the need for flash led
specific device tree properties. The properties being added are:
iout-torch, iout-flash, iout-indicator and flash-timeout.

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Stephen Warren <swarren@nvidia.com>
Cc: Grant Likely <grant.likely@secretlab.ca>
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/leds/common.txt |   16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/Documentation/devicetree/bindings/leds/common.txt b/Documentation/devicetree/bindings/leds/common.txt
index 2d88816..40f4b9a 100644
--- a/Documentation/devicetree/bindings/leds/common.txt
+++ b/Documentation/devicetree/bindings/leds/common.txt
@@ -3,6 +3,17 @@ Common leds properties.
 Optional properties for child nodes:
 - label : The label for this LED.  If omitted, the label is
   taken from the node name (excluding the unit address).
+- iout-torch : Array of maximum intensities in microamperes of the torch
+	led currents in order from sub-led 0 to N-1, where N is the number
+	of torch sub-leds exposed by the device
+- iout-flash : Array of maximum intensities in microamperes of the flash
+	led currents in order from sub-led 0 to N-1, where N is the number
+	of flash sub-leds exposed by the device
+- iout-indicator : Array of maximum intensities in microamperes of
+	the indicator led currents in order from sub-led 0 to N-1,
+	where N is the number of indicator sub-leds exposed by the device
+- flash-timeout : timeout in microseconds after which flash led
+	is turned off
 
 - linux,default-trigger :  This parameter, if present, is a
     string defining the trigger assigned to the LED.  Current triggers are:
@@ -19,5 +30,10 @@ Examples:
 system-status {
 	label = "Status";
 	linux,default-trigger = "heartbeat";
+	iout-torch = <500 500>;
+	iout-flash = <1000 1000>;
+	iout-indicator = <100 100>;
+	flash-timeout = <1000>;
+
 	...
 };
-- 
1.7.9.5


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

* [PATCH/RFC v4 12/21] DT: Add documentation for LED Class Flash Manger
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
                   ` (10 preceding siblings ...)
  2014-07-11 14:04 ` [PATCH/RFC v4 11/21] DT: leds: Add flash led devices related properties Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-11 14:04 ` [PATCH/RFC v4 13/21] v4l2-device: add v4l2_device_register_subdev_node API Jacek Anaszewski
                   ` (10 subsequent siblings)
  22 siblings, 0 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Bryan Wu,
	Richard Purdie, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala

This patch documents LED Class Flash Manager
related bindings.

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: 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>
---
 .../bindings/leds/leds-flash-manager.txt           |  165 ++++++++++++++++++++
 1 file changed, 165 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-flash-manager.txt

diff --git a/Documentation/devicetree/bindings/leds/leds-flash-manager.txt b/Documentation/devicetree/bindings/leds/leds-flash-manager.txt
new file mode 100644
index 0000000..c98ee2e
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-flash-manager.txt
@@ -0,0 +1,165 @@
+* LED Flash Manager
+
+Flash manager is a part of LED Flash Class. It maintains
+all the flash led devices which have their external strobe
+signals routed through multiplexing devices.
+The multiplexers are aggregated in the standalone 'flash_muxes'
+node as subnodes which are referenced by the flash led devices.
+
+
+flash_muxes node
+----------------
+
+muxN subnode
+------------
+
+There must be at least one muxN subnode, where N is the identifier
+of the node, present in the flash_muxes node. One muxN node
+represents one multiplexer.
+
+Required properties (mutually exclusive):
+- gpios		: specifies the gpio pins used to set the states
+		  of mux selectors, LSB first
+- mux-async	: phandle to the node of the multiplexing device
+
+
+
+flash led device node
+---------------------
+
+Following subnodes must be added to the LED Flash Class device
+tree node described in Documentation/devicetree/bindings/leds/common.txt.
+
+
+gate-software-strobe subnode
+----------------------------
+
+The node defines configuration of multiplexers that needs
+to be applied to route software strobe signal to the flash
+led device.
+
+Required properties:
+- mux		: phandle to the muxN node defined
+		  in the flash_muxes node
+- mux-line-id	: mux line identifier
+
+Optional subnodes:
+- gate-software-strobe : if there are many multiplexers to configure,
+			 they can be recursively nested.
+
+
+gate-external-strobeN subnode
+-----------------------------
+
+The node defines configuration of multiplexers that needs
+to be applied to route external strobe signal to the flash
+led device. A flash led device can have many external strobe
+signal sources.
+
+Required properties:
+- mux			: phandle to the muxN node defined
+			  in the flash_muxes node
+- mux-line-id		: mux line identifier
+Optional properties:
+- strobe-provider	: phandle to the device providing the
+			  strobe signal. It is expected only
+			  on the first level node. The referenced
+			  node is expected to have 'compatible'
+			  property, as providers are labelled
+			  with it in the LED subsystem
+
+Optional subnodes:
+- gate-external-strobeN	: if there are many multiplexers to configure,
+			  they can be recursively nested.
+
+
+Example:
+
+Following board configuration is assumed in this example:
+
+    ---------- ----------
+    | FLASH1 | | FLASH2 |
+    ---------- ----------
+           \(0)   /(1)
+          ----------
+          |  MUX1  |
+          ----------
+              |
+          ----------
+          |  MUX2  |
+          ----------
+           /(0)   \(1)
+      ----------  --------------------
+      |  MUX3  |  | SOC FLASHEN GPIO |
+      ----------  --------------------
+       /(0)   \(1)
+----------- -----------
+| SENSOR1 | | SENSOR2 |
+----------- -----------
+
+
+dummy_mux: led_mux {
+	compatible = "led-async-mux";
+};
+
+flash_muxes {
+	flash_mux1: mux1 {
+                gpios = <&gpj1 1 0>, <&gpj1 2 0>;
+	};
+
+	flash_mux2: mux2 {
+		mux-async = <&dummy_mux>;
+	};
+
+	flash_mux3: mux3 {
+                gpios = <&gpl1 1 0>, <&gpl1 2 0>;
+	};
+};
+
+max77693-flash {
+	compatible = "maxim,max77693-flash";
+
+	//other device specific properties here
+
+	gate-software-strobe {
+		mux = <&flash_mux1>;
+		mux-line-id = <0>;
+
+		gate-software-strobe {
+			mux = <&flash_mux2>;
+			mux-line-id = <1>;
+		};
+	};
+
+	gate-external-strobe1 {
+		strobe-provider = <&s5c73m3_spi>;
+		mux = <&flash_mux1>;
+		mux-line-id = <0>;
+
+		gate-external-strobe1 {
+			mux = <&flash_mux2>;
+			mux-line-id = <0>;
+
+			gate-external-strobe1 {
+				mux = <&flash_mux3>;
+				mux-line-id = <0>;
+			};
+		};
+	};
+
+	gate-external-strobe2 {
+		strobe-provider = <&s5k6a3>;
+		mux = <&flash_mux1>;
+		mux-line-id = <0>;
+
+		gate-external-strobe2 {
+			mux = <&flash_mux2>;
+			mux-line-id = <0>;
+
+			gate-external-strobe2 {
+				mux = <&flash_mux3>;
+				mux-line-id = <1>;
+			};
+		};
+	};
+};
-- 
1.7.9.5


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

* [PATCH/RFC v4 13/21] v4l2-device: add v4l2_device_register_subdev_node API
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
                   ` (11 preceding siblings ...)
  2014-07-11 14:04 ` [PATCH/RFC v4 12/21] DT: Add documentation for LED Class Flash Manger Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-11 14:04 ` [PATCH/RFC v4 14/21] v4l2-ctrls: add control for flash strobe signal providers Jacek Anaszewski
                   ` (9 subsequent siblings)
  22 siblings, 0 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Hans Verkuil,
	Laurent Pinchart

Extract the code executed for each entry of the subdev list
and put it to the separate function. Export it as a public API.
It allows for registering single sub-device at a time.

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Hans Verkuil <hans.verkuil@cisco.com>
Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/media/v4l2-core/v4l2-device.c |   63 +++++++++++++++++++--------------
 include/media/v4l2-device.h           |    7 ++++
 2 files changed, 44 insertions(+), 26 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c
index 015f92a..0e91ef7 100644
--- a/drivers/media/v4l2-core/v4l2-device.c
+++ b/drivers/media/v4l2-core/v4l2-device.c
@@ -216,9 +216,43 @@ static void v4l2_device_release_subdev_node(struct video_device *vdev)
 	kfree(vdev);
 }
 
-int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
+int v4l2_device_register_subdev_node(struct v4l2_subdev *sd,
+				     struct v4l2_device *v4l2_dev)
 {
 	struct video_device *vdev;
+	int err;
+
+	if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
+		return 0;
+
+	vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
+	if (!vdev)
+		return -ENOMEM;
+
+	video_set_drvdata(vdev, sd);
+	strlcpy(vdev->name, sd->name, sizeof(vdev->name));
+	vdev->v4l2_dev = v4l2_dev;
+	vdev->fops = &v4l2_subdev_fops;
+	vdev->release = v4l2_device_release_subdev_node;
+	vdev->ctrl_handler = sd->ctrl_handler;
+	err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
+				      sd->owner);
+	if (err < 0) {
+		kfree(vdev);
+		return err;
+	}
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	sd->entity.info.v4l.major = VIDEO_MAJOR;
+	sd->entity.info.v4l.minor = vdev->minor;
+#endif
+	sd->devnode = vdev;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_node);
+
+int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
+{
 	struct v4l2_subdev *sd;
 	int err;
 
@@ -226,32 +260,9 @@ int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
 	 * V4L2_SUBDEV_FL_HAS_DEVNODE flag.
 	 */
 	list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
-		if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
-			continue;
-
-		vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
-		if (!vdev) {
-			err = -ENOMEM;
-			goto clean_up;
-		}
-
-		video_set_drvdata(vdev, sd);
-		strlcpy(vdev->name, sd->name, sizeof(vdev->name));
-		vdev->v4l2_dev = v4l2_dev;
-		vdev->fops = &v4l2_subdev_fops;
-		vdev->release = v4l2_device_release_subdev_node;
-		vdev->ctrl_handler = sd->ctrl_handler;
-		err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
-					      sd->owner);
-		if (err < 0) {
-			kfree(vdev);
+		err = v4l2_device_register_subdev_node(sd, v4l2_dev);
+		if (err < 0)
 			goto clean_up;
-		}
-#if defined(CONFIG_MEDIA_CONTROLLER)
-		sd->entity.info.v4l.major = VIDEO_MAJOR;
-		sd->entity.info.v4l.minor = vdev->minor;
-#endif
-		sd->devnode = vdev;
 	}
 	return 0;
 
diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h
index ffb69da..76594fc 100644
--- a/include/media/v4l2-device.h
+++ b/include/media/v4l2-device.h
@@ -114,6 +114,13 @@ int __must_check v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
    wasn't registered. In that case it will do nothing. */
 void v4l2_device_unregister_subdev(struct v4l2_subdev *sd);
 
+/* Register device node for the subdev of the v4l2 device if it is marked with
+ * the V4L2_SUBDEV_FL_HAS_DEVNODE flag.
+ */
+int __must_check
+v4l2_device_register_subdev_node(struct v4l2_subdev *sd,
+					struct v4l2_device *v4l2_dev);
+
 /* Register device nodes for all subdev of the v4l2 device that are marked with
  * the V4L2_SUBDEV_FL_HAS_DEVNODE flag.
  */
-- 
1.7.9.5


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

* [PATCH/RFC v4 14/21] v4l2-ctrls: add control for flash strobe signal providers
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
                   ` (12 preceding siblings ...)
  2014-07-11 14:04 ` [PATCH/RFC v4 13/21] v4l2-device: add v4l2_device_register_subdev_node API Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-11 14:04 ` [PATCH/RFC v4 15/21] media: Add registration helpers for V4L2 flash Jacek Anaszewski
                   ` (8 subsequent siblings)
  22 siblings, 0 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Sakari Ailus,
	Hans Verkuil

Add V4L2_CID_STROBE_PROVIDER of type menu, which allows
for enumerating of available external flash strobe signal
providers and setting the active one.

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Sakari Ailus <sakari.ailus@iki.fi>
Cc: Hans Verkuil <hans.verkuil@cisco.com>
---
 Documentation/DocBook/media/v4l/controls.xml |   11 +++++++++++
 drivers/media/v4l2-core/v4l2-ctrls.c         |    2 ++
 include/uapi/linux/v4l2-controls.h           |    2 ++
 3 files changed, 15 insertions(+)

diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml
index 47198ee..d9f6c3f 100644
--- a/Documentation/DocBook/media/v4l/controls.xml
+++ b/Documentation/DocBook/media/v4l/controls.xml
@@ -4300,6 +4300,17 @@ interface and may change in the future.</para>
     	    is strobing at the moment or not. This is a read-only
     	    control.</entry>
     	  </row>
+          <row>
+            <entry spanname="id"><constant>V4L2_CID_FLASH_STROBE_PROVIDER</constant></entry>
+            <entry>menu</entry>
+          </row>
+          <row>
+            <entry spanname="descr">Provider of the external strobe signal. If a flash
+            device declares more than one available external strobe signal provider then
+            this control allows to select the active one. &VIDIOC-QUERYCTRL; has to be
+            used to get the list of available strobe providers.
+            </entry>
+          </row>
     	  <row>
     	    <entry spanname="id"><constant>V4L2_CID_FLASH_TIMEOUT</constant></entry>
     	    <entry>integer</entry>
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 55c6832..f298f7e 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -825,6 +825,7 @@ const char *v4l2_ctrl_get_name(u32 id)
 	case V4L2_CID_FLASH_FAULT:		return "Faults";
 	case V4L2_CID_FLASH_CHARGE:		return "Charge";
 	case V4L2_CID_FLASH_READY:		return "Ready to Strobe";
+	case V4L2_CID_FLASH_STROBE_PROVIDER:	return "Strobe Provider";
 
 	/* JPEG encoder controls */
 	/* Keep the order of the 'case's the same as in videodev2.h! */
@@ -988,6 +989,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
 	case V4L2_CID_TEST_PATTERN:
 	case V4L2_CID_TUNE_DEEMPHASIS:
 	case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL:
+	case V4L2_CID_FLASH_STROBE_PROVIDER:
 		*type = V4L2_CTRL_TYPE_MENU;
 		break;
 	case V4L2_CID_LINK_FREQ:
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 2ac5597..1f05c7c 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -822,6 +822,8 @@ enum v4l2_flash_strobe_source {
 #define V4L2_CID_FLASH_CHARGE			(V4L2_CID_FLASH_CLASS_BASE + 11)
 #define V4L2_CID_FLASH_READY			(V4L2_CID_FLASH_CLASS_BASE + 12)
 
+#define V4L2_CID_FLASH_STROBE_PROVIDER		(V4L2_CID_FLASH_CLASS_BASE + 13)
+
 
 /* JPEG-class control IDs */
 
-- 
1.7.9.5


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

* [PATCH/RFC v4 15/21] media: Add registration helpers for V4L2 flash
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
                   ` (13 preceding siblings ...)
  2014-07-11 14:04 ` [PATCH/RFC v4 14/21] v4l2-ctrls: add control for flash strobe signal providers Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-21 11:12   ` Sakari Ailus
  2014-07-11 14:04 ` [PATCH/RFC v4 16/21] leds: Add support for max77693 mfd flash cell Jacek Anaszewski
                   ` (7 subsequent siblings)
  22 siblings, 1 reply; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Sakari Ailus,
	Hans Verkuil

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
support for V4L2 Flash sub-devices 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>
Cc: Sakari Ailus <sakari.ailus@iki.fi>
Cc: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/Kconfig      |   11 +
 drivers/media/v4l2-core/Makefile     |    2 +
 drivers/media/v4l2-core/v4l2-flash.c |  580 ++++++++++++++++++++++++++++++++++
 include/media/v4l2-flash.h           |  137 ++++++++
 4 files changed, 730 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 9ca0f8d..3ae3f0f 100644
--- a/drivers/media/v4l2-core/Kconfig
+++ b/drivers/media/v4l2-core/Kconfig
@@ -35,6 +35,17 @@ config V4L2_MEM2MEM_DEV
         tristate
         depends on VIDEOBUF2_CORE
 
+# Used by LED subsystem flash drivers
+config V4L2_FLASH_LED_CLASS
+	bool "Enable support for Flash sub-devices"
+	depends on VIDEO_V4L2_SUBDEV_API
+	depends on LEDS_CLASS_FLASH
+	---help---
+	  Say Y here to enable support for Flash sub-devices, which allow
+	  to control LED class devices with use of V4L2 Flash controls.
+
+	  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 63d29f2..44e858c 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_LED_CLASS) += 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..21080c6
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-flash.c
@@ -0,0 +1,580 @@
+/*
+ * V4L2 Flash LED sub-device 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 <linux/led-class-flash.h>
+#include <linux/mutex.h>
+#include <linux/of_led_flash_manager.h>
+#include <linux/slab.h>
+#include <media/v4l2-flash.h>
+
+#define call_flash_op(v4l2_flash, op, args...)			\
+		(v4l2_flash->ops->op  ?				\
+			v4l2_flash->ops->op(args) :		\
+			-EINVAL)
+
+static struct v4l2_device *v4l2_dev;
+static int registered_flashes;
+
+static inline enum led_brightness v4l2_flash_intensity_to_led_brightness(
+					struct v4l2_ctrl_config *config,
+					s32 intensity)
+{
+	return ((intensity - config->min) / config->step) + 1;
+}
+
+static inline s32 v4l2_flash_led_brightness_to_intensity(
+					struct v4l2_ctrl_config *config,
+					enum led_brightness brightness)
+{
+	return ((brightness - 1) * config->step) + config->min;
+}
+
+static int v4l2_flash_g_volatile_ctrl(struct v4l2_ctrl *c)
+
+{
+	struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c);
+	struct led_classdev_flash *flash = v4l2_flash->flash;
+	struct led_classdev *led_cdev = &flash->led_cdev;
+	struct v4l2_flash_ctrl_config *config = &v4l2_flash->config;
+	struct v4l2_flash_ctrl *ctrl = &v4l2_flash->ctrl;
+	bool is_strobing;
+	int ret;
+
+	switch (c->id) {
+	case V4L2_CID_FLASH_TORCH_INTENSITY:
+		/*
+		 * Update torch brightness only if in TORCH_MODE,
+		 * as otherwise brightness_update op returns 0,
+		 * which would spuriously inform user space that
+		 * V4L2_CID_FLASH_TORCH_INTENSITY control value
+		 * has changed.
+		 */
+		if (ctrl->led_mode->val == V4L2_FLASH_LED_MODE_TORCH) {
+			ret = call_flash_op(v4l2_flash, torch_brightness_update,
+							led_cdev);
+			if (ret < 0)
+				return ret;
+			ctrl->torch_intensity->val =
+				v4l2_flash_led_brightness_to_intensity(
+						&config->torch_intensity,
+						led_cdev->brightness);
+		}
+		return 0;
+	case V4L2_CID_FLASH_INTENSITY:
+		ret = call_flash_op(v4l2_flash, flash_brightness_update,
+					flash);
+		if (ret < 0)
+			return ret;
+		/* no conversion is needed */
+		c->val = flash->brightness.val;
+		return 0;
+	case V4L2_CID_FLASH_INDICATOR_INTENSITY:
+		ret = call_flash_op(v4l2_flash, indicator_brightness_update,
+						flash);
+		if (ret < 0)
+			return ret;
+		/* no conversion is needed */
+		c->val = flash->indicator_brightness->val;
+		return 0;
+	case V4L2_CID_FLASH_STROBE_STATUS:
+		ret = call_flash_op(v4l2_flash, strobe_get, flash,
+							&is_strobing);
+		if (ret < 0)
+			return ret;
+		c->val = is_strobing;
+		return 0;
+	case V4L2_CID_FLASH_FAULT:
+		/* led faults map directly to V4L2 flash faults */
+		ret = call_flash_op(v4l2_flash, fault_get, flash, &c->val);
+		return ret;
+	case V4L2_CID_FLASH_STROBE_SOURCE:
+		c->val = flash->external_strobe;
+		return 0;
+	case V4L2_CID_FLASH_STROBE_PROVIDER:
+		c->val = flash->strobe_provider_id;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int v4l2_flash_s_ctrl(struct v4l2_ctrl *c)
+{
+	struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c);
+	struct led_classdev_flash *flash = v4l2_flash->flash;
+	struct v4l2_flash_ctrl *ctrl = &v4l2_flash->ctrl;
+	struct v4l2_flash_ctrl_config *config = &v4l2_flash->config;
+	enum led_brightness torch_brightness;
+	bool external_strobe;
+	int ret;
+
+	switch (c->id) {
+	case V4L2_CID_FLASH_LED_MODE:
+		switch (c->val) {
+		case V4L2_FLASH_LED_MODE_NONE:
+			call_flash_op(v4l2_flash, torch_brightness_set,
+							&flash->led_cdev, 0);
+			return call_flash_op(v4l2_flash, strobe_set, flash,
+							false);
+		case V4L2_FLASH_LED_MODE_FLASH:
+			/* Turn off torch LED */
+			call_flash_op(v4l2_flash, torch_brightness_set,
+							&flash->led_cdev, 0);
+			external_strobe = (ctrl->source->val ==
+					V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
+			return call_flash_op(v4l2_flash, external_strobe_set,
+						flash, external_strobe);
+		case V4L2_FLASH_LED_MODE_TORCH:
+			/* Stop flash strobing */
+			ret = call_flash_op(v4l2_flash, strobe_set, flash,
+							false);
+			if (ret)
+				return ret;
+
+			torch_brightness =
+				v4l2_flash_intensity_to_led_brightness(
+						&config->torch_intensity,
+						ctrl->torch_intensity->val);
+			call_flash_op(v4l2_flash, torch_brightness_set,
+					&flash->led_cdev, torch_brightness);
+			return ret;
+		}
+		break;
+	case V4L2_CID_FLASH_STROBE_SOURCE:
+		external_strobe = (c->val == V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
+
+		return call_flash_op(v4l2_flash, external_strobe_set, flash,
+							external_strobe);
+	case V4L2_CID_FLASH_STROBE:
+		if (ctrl->led_mode->val != V4L2_FLASH_LED_MODE_FLASH ||
+		    ctrl->source->val != V4L2_FLASH_STROBE_SOURCE_SOFTWARE)
+			return -EINVAL;
+		return call_flash_op(v4l2_flash, strobe_set, flash, true);
+	case V4L2_CID_FLASH_STROBE_STOP:
+		return call_flash_op(v4l2_flash, strobe_set, flash, false);
+	case V4L2_CID_FLASH_TIMEOUT:
+		return call_flash_op(v4l2_flash, timeout_set, flash, c->val);
+	case V4L2_CID_FLASH_INTENSITY:
+		/* no conversion is needed */
+		return call_flash_op(v4l2_flash, flash_brightness_set, flash,
+								c->val);
+	case V4L2_CID_FLASH_INDICATOR_INTENSITY:
+		/* no conversion is needed */
+		return call_flash_op(v4l2_flash, indicator_brightness_set,
+						flash, c->val);
+	case V4L2_CID_FLASH_TORCH_INTENSITY:
+		/*
+		 * If not in MODE_TORCH don't call led-class brightness_set
+		 * op, as it would result in turning the torch led on.
+		 * Instead the value is cached only and will be written
+		 * to the device upon transition to MODE_TORCH.
+		 */
+		if (ctrl->led_mode->val == V4L2_FLASH_LED_MODE_TORCH) {
+			torch_brightness =
+				v4l2_flash_intensity_to_led_brightness(
+						&config->torch_intensity,
+						ctrl->torch_intensity->val);
+			call_flash_op(v4l2_flash, torch_brightness_set,
+					&flash->led_cdev, torch_brightness);
+		}
+		return 0;
+	case V4L2_CID_FLASH_STROBE_PROVIDER:
+		flash->strobe_provider_id = c->val;
+		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_strobe_providers_menu(struct v4l2_flash *v4l2_flash)
+{
+	struct led_classdev_flash *flash = v4l2_flash->flash;
+	struct led_flash_strobe_provider *provider;
+	struct v4l2_ctrl *ctrl;
+	int i = 0;
+
+	v4l2_flash->strobe_providers_menu =
+			kzalloc(sizeof(char *) * (flash->num_strobe_providers),
+					GFP_KERNEL);
+	if (!v4l2_flash->strobe_providers_menu)
+		return -ENOMEM;
+
+	list_for_each_entry(provider, &flash->strobe_providers, list)
+		v4l2_flash->strobe_providers_menu[i++] =
+						(char *) provider->name;
+
+	ctrl = v4l2_ctrl_new_std_menu_items(
+		&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
+		V4L2_CID_FLASH_STROBE_PROVIDER,
+		flash->num_strobe_providers - 1,
+		0, 0,
+		(const char * const *) v4l2_flash->strobe_providers_menu);
+
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+	return 0;
+}
+
+static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash)
+
+{
+	struct led_classdev_flash *flash = v4l2_flash->flash;
+	const struct led_flash_ops *flash_ops = flash->ops;
+	struct led_classdev *led_cdev = &flash->led_cdev;
+	struct v4l2_flash_ctrl_config *config = &v4l2_flash->config;
+	struct v4l2_ctrl *ctrl;
+	struct v4l2_ctrl_config *ctrl_cfg;
+	bool has_flash = led_cdev->flags & LED_DEV_CAP_FLASH;
+	bool has_indicator = led_cdev->flags & LED_DEV_CAP_INDICATOR;
+	bool has_strobe_providers = (flash->num_strobe_providers > 1);
+	unsigned int mask;
+	int ret, max, num_ctrls;
+
+	num_ctrls = has_flash ? 5 : 2;
+	if (has_flash) {
+		if (flash_ops->flash_brightness_set)
+			++num_ctrls;
+		if (flash_ops->timeout_set)
+			++num_ctrls;
+		if (flash_ops->strobe_get)
+			++num_ctrls;
+		if (has_indicator)
+			++num_ctrls;
+		if (config->flash_faults)
+			++num_ctrls;
+		if (has_strobe_providers)
+			++num_ctrls;
+	}
+
+	v4l2_ctrl_handler_init(&v4l2_flash->hdl, num_ctrls);
+
+	mask = 1 << V4L2_FLASH_LED_MODE_NONE |
+	       1 << V4L2_FLASH_LED_MODE_TORCH;
+	if (flash)
+		mask |= 1 << V4L2_FLASH_LED_MODE_FLASH;
+
+	/* Configure FLASH_LED_MODE ctrl */
+	v4l2_flash->ctrl.led_mode = v4l2_ctrl_new_std_menu(
+			&v4l2_flash->hdl,
+			&v4l2_flash_ctrl_ops, V4L2_CID_FLASH_LED_MODE,
+			V4L2_FLASH_LED_MODE_TORCH, ~mask,
+			V4L2_FLASH_LED_MODE_NONE);
+
+	/* Configure TORCH_INTENSITY ctrl */
+	ctrl_cfg = &config->torch_intensity;
+	ctrl = v4l2_ctrl_new_std(&v4l2_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;
+	v4l2_flash->ctrl.torch_intensity = ctrl;
+
+	if (has_flash) {
+		/* Configure FLASH_STROBE_SOURCE ctrl */
+		mask = 1 << V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
+
+		if (flash->has_external_strobe) {
+			mask |= 1 << V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
+			max = V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
+		} else {
+			max = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
+		}
+
+		v4l2_flash->ctrl.source = v4l2_ctrl_new_std_menu(
+					&v4l2_flash->hdl,
+					&v4l2_flash_ctrl_ops,
+					V4L2_CID_FLASH_STROBE_SOURCE,
+					max,
+					~mask,
+					V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
+		if (v4l2_flash->ctrl.source)
+			v4l2_flash->ctrl.source->flags |=
+						V4L2_CTRL_FLAG_VOLATILE;
+
+		/* Configure FLASH_STROBE ctrl */
+		ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
+					  V4L2_CID_FLASH_STROBE, 0, 1, 1, 0);
+
+		/* Configure FLASH_STROBE_STOP ctrl */
+		ctrl = v4l2_ctrl_new_std(&v4l2_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(&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
+					 V4L2_CID_FLASH_STROBE_STATUS,
+					 0, 1, 1, 1);
+
+		if (flash_ops->strobe_get)
+			if (ctrl)
+				ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
+					       V4L2_CTRL_FLAG_READ_ONLY;
+
+		if (flash_ops->timeout_set) {
+			/* Configure FLASH_TIMEOUT ctrl */
+			ctrl_cfg = &config->flash_timeout;
+			ctrl = v4l2_ctrl_new_std(
+					&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
+					V4L2_CID_FLASH_TIMEOUT, ctrl_cfg->min,
+					ctrl_cfg->max, ctrl_cfg->step,
+					ctrl_cfg->def);
+			if (ctrl)
+				ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+		}
+
+		if (flash_ops->flash_brightness_set) {
+			/* Configure FLASH_INTENSITY ctrl */
+			ctrl_cfg = &config->flash_intensity;
+			ctrl = v4l2_ctrl_new_std(
+					&v4l2_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->flash_faults) {
+			/* Configure FLASH_FAULT ctrl */
+			ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl,
+						 &v4l2_flash_ctrl_ops,
+						 V4L2_CID_FLASH_FAULT, 0,
+						 config->flash_faults,
+						 0, 0);
+			if (ctrl)
+				ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
+					       V4L2_CTRL_FLAG_READ_ONLY;
+		}
+		if (has_indicator) {
+			/* Configure FLASH_INDICATOR_INTENSITY ctrl */
+			ctrl_cfg = &config->indicator_intensity;
+			ctrl = v4l2_ctrl_new_std(
+					&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
+					V4L2_CID_FLASH_INDICATOR_INTENSITY,
+					ctrl_cfg->min, ctrl_cfg->max,
+					ctrl_cfg->step, ctrl_cfg->def);
+			if (ctrl)
+				ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+		}
+
+		if (has_strobe_providers) {
+			/* Configure V4L2_CID_FLASH_STROBE_PROVIDERS ctrl */
+			ret = v4l2_flash_init_strobe_providers_menu(v4l2_flash);
+			if (ret < 0)
+				goto error_free_handler;
+		}
+	}
+
+	if (v4l2_flash->hdl.error) {
+		ret = v4l2_flash->hdl.error;
+		goto error_free_handler;
+	}
+
+	ret = v4l2_ctrl_handler_setup(&v4l2_flash->hdl);
+	if (ret < 0)
+		goto error_free_handler;
+
+	v4l2_flash->sd.ctrl_handler = &v4l2_flash->hdl;
+
+	return 0;
+
+error_free_handler:
+	v4l2_ctrl_handler_free(&v4l2_flash->hdl);
+	return ret;
+}
+
+/*
+ * V4L2 subdev internal operations
+ */
+
+static int v4l2_flash_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd);
+	struct led_classdev_flash *flash = v4l2_flash->flash;
+	struct led_classdev *led_cdev = &flash->led_cdev;
+
+	mutex_lock(&led_cdev->led_lock);
+	call_flash_op(v4l2_flash, sysfs_lock, led_cdev);
+	mutex_unlock(&led_cdev->led_lock);
+
+	return 0;
+}
+
+static int v4l2_flash_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd);
+	struct led_classdev_flash *flash = v4l2_flash->flash;
+	struct led_classdev *led_cdev = &flash->led_cdev;
+
+	mutex_lock(&led_cdev->led_lock);
+	call_flash_op(v4l2_flash, sysfs_unlock, led_cdev);
+	mutex_unlock(&led_cdev->led_lock);
+
+	return 0;
+}
+
+int v4l2_flash_register(struct v4l2_flash *v4l2_flash)
+{
+	struct led_classdev_flash *flash = v4l2_flash->flash;
+	struct led_classdev *led_cdev = &flash->led_cdev;
+	int ret;
+
+	if (!v4l2_dev) {
+		v4l2_dev = kzalloc(sizeof(*v4l2_dev), GFP_KERNEL);
+		if (!v4l2_dev)
+			return -ENOMEM;
+
+		strlcpy(v4l2_dev->name, "v4l2-flash-manager",
+						sizeof(v4l2_dev->name));
+		ret = v4l2_device_register(NULL, v4l2_dev);
+		if (ret < 0) {
+			dev_err(led_cdev->dev->parent,
+				 "Failed to register v4l2_device: %d\n", ret);
+			goto err_v4l2_device_register;
+		}
+	}
+
+	ret = v4l2_device_register_subdev(v4l2_dev, &v4l2_flash->sd);
+	if (ret < 0) {
+		dev_err(led_cdev->dev->parent,
+			 "Failed to register v4l2_subdev: %d\n", ret);
+		goto err_v4l2_device_register;
+	}
+
+	ret = v4l2_device_register_subdev_node(&v4l2_flash->sd, v4l2_dev);
+	if (ret < 0) {
+		dev_err(led_cdev->dev->parent,
+			 "Failed to register v4l2_subdev node: %d\n", ret);
+		goto err_register_subdev_node;
+	}
+
+	++registered_flashes;
+
+	return 0;
+
+err_register_subdev_node:
+	v4l2_device_unregister_subdev(&v4l2_flash->sd);
+err_v4l2_device_register:
+	kfree(v4l2_flash->strobe_providers_menu);
+	if (v4l2_dev && registered_flashes == 0) {
+		v4l2_device_unregister(v4l2_dev);
+		kfree(v4l2_dev);
+		v4l2_dev = NULL;
+	}
+
+	return ret;
+}
+
+static void v4l2_flash_unregister(struct v4l2_flash *v4l2_flash)
+{
+	if (registered_flashes == 0)
+		return;
+
+	v4l2_device_unregister_subdev(&v4l2_flash->sd);
+
+	--registered_flashes;
+
+	if (registered_flashes == 0) {
+		v4l2_device_unregister(v4l2_dev);
+		kfree(v4l2_dev);
+		v4l2_dev = NULL;
+	}
+}
+
+static const struct v4l2_subdev_internal_ops v4l2_flash_subdev_internal_ops = {
+	.open = v4l2_flash_open,
+	.close = v4l2_flash_close,
+};
+
+static const struct v4l2_subdev_core_ops v4l2_flash_core_ops = {
+	.queryctrl = v4l2_subdev_queryctrl,
+	.querymenu = v4l2_subdev_querymenu,
+};
+
+static const struct v4l2_subdev_ops v4l2_flash_subdev_ops = {
+	.core = &v4l2_flash_core_ops,
+};
+
+int v4l2_flash_init(struct led_classdev_flash *flash,
+		    struct v4l2_flash_ctrl_config *config,
+		    const struct v4l2_flash_ops *flash_ops,
+		    struct v4l2_flash **out_flash)
+{
+	struct v4l2_flash *v4l2_flash;
+	struct led_classdev *led_cdev = &flash->led_cdev;
+	struct v4l2_subdev *sd;
+	int ret;
+
+	if (!flash || !config || !out_flash)
+		return -EINVAL;
+
+	v4l2_flash = kzalloc(sizeof(*v4l2_flash), GFP_KERNEL);
+	if (!v4l2_flash)
+		return -ENOMEM;
+
+	sd = &v4l2_flash->sd;
+	v4l2_flash->flash = flash;
+	v4l2_flash->ops = flash_ops;
+	sd->dev = led_cdev->dev->parent;
+	v4l2_subdev_init(sd, &v4l2_flash_subdev_ops);
+	sd->internal_ops = &v4l2_flash_subdev_internal_ops;
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	snprintf(sd->name, sizeof(sd->name), led_cdev->name);
+
+	v4l2_flash->config = *config;
+	ret = v4l2_flash_init_controls(v4l2_flash);
+	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_flash_register(v4l2_flash);
+	if (ret < 0)
+		goto err_init_entity;
+
+	*out_flash = v4l2_flash;
+
+	return 0;
+
+err_init_entity:
+	media_entity_cleanup(&sd->entity);
+err_init_controls:
+	v4l2_ctrl_handler_free(sd->ctrl_handler);
+	kfree(v4l2_flash);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_flash_init);
+
+void v4l2_flash_release(struct v4l2_flash *v4l2_flash)
+{
+	if (!v4l2_flash)
+		return;
+
+	v4l2_flash_unregister(v4l2_flash);
+	v4l2_ctrl_handler_free(v4l2_flash->sd.ctrl_handler);
+	media_entity_cleanup(&v4l2_flash->sd.entity);
+	kfree(v4l2_flash->strobe_providers_menu);
+	kfree(v4l2_flash);
+}
+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..effa46b
--- /dev/null
+++ b/include/media/v4l2-flash.h
@@ -0,0 +1,137 @@
+/*
+ * V4L2 Flash LED sub-device 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 <media/v4l2-ioctl.h>
+
+struct led_classdev_flash;
+struct led_classdev;
+enum led_brightness;
+
+struct v4l2_flash_ops {
+	int (*torch_brightness_set)(struct led_classdev *led_cdev,
+					enum led_brightness brightness);
+	int (*torch_brightness_update)(struct led_classdev *led_cdev);
+	int (*flash_brightness_set)(struct led_classdev_flash *flash,
+					u32 brightness);
+	int (*flash_brightness_update)(struct led_classdev_flash *flash);
+	int (*strobe_set)(struct led_classdev_flash *flash, bool state);
+	int (*strobe_get)(struct led_classdev_flash *flash, bool *state);
+	int (*timeout_set)(struct led_classdev_flash *flash, u32 timeout);
+	int (*indicator_brightness_set)(struct led_classdev_flash *flash,
+					u32 brightness);
+	int (*indicator_brightness_update)(struct led_classdev_flash *flash);
+	int (*external_strobe_set)(struct led_classdev_flash *flash,
+					bool enable);
+	int (*fault_get)(struct led_classdev_flash *flash, u32 *fault);
+	void (*sysfs_lock)(struct led_classdev *led_cdev);
+	void (*sysfs_unlock)(struct led_classdev *led_cdev);
+};
+
+/**
+ * struct v4l2_flash_ctrl - controls that define the sub-dev's state
+ * @source:		V4L2_CID_FLASH_STROBE_SOURCE control
+ * @led_mode:		V4L2_CID_FLASH_LED_MODE control
+ * @torch_intensity:	V4L2_CID_FLASH_TORCH_INTENSITY control
+ */
+struct v4l2_flash_ctrl {
+	struct v4l2_ctrl *source;
+	struct v4l2_ctrl *led_mode;
+	struct v4l2_ctrl *torch_intensity;
+};
+
+/**
+ * struct v4l2_flash_ctrl_config - V4L2 Flash controls initialization data
+ * @torch_intensity:		V4L2_CID_FLASH_TORCH_INTENSITY constraints
+ * @flash_intensity:		V4L2_CID_FLASH_INTENSITY constraints
+ * @indicator_intensity:	V4L2_CID_FLASH_INDICATOR_INTENSITY constraints
+ * @flash_timeout:		V4L2_CID_FLASH_TIMEOUT constraints
+ * @flash_fault:		possible flash faults
+ */
+struct v4l2_flash_ctrl_config {
+	struct v4l2_ctrl_config torch_intensity;
+	struct v4l2_ctrl_config flash_intensity;
+	struct v4l2_ctrl_config indicator_intensity;
+	struct v4l2_ctrl_config flash_timeout;
+	u32 flash_faults;
+};
+
+/**
+ * struct v4l2_flash - Flash sub-device context
+ * @flash:		LED Flash Class device controlled by this sub-device
+ * @ops:		LED Flash Class device ops
+ * @sd:			V4L2 sub-device
+ * @hdl:		flash controls handler
+ * @ctrl:		state defining controls
+ * @config:		V4L2 Flash controlsrconfiguration data
+ * @software_strobe_gates: route to the software strobe signal
+ * @external_strobe_gates: route to the external strobe signal
+ * @sensors:		available external strobe sources
+ */
+struct v4l2_flash {
+	struct led_classdev_flash *flash;
+	const struct v4l2_flash_ops *ops;
+
+	struct v4l2_subdev sd;
+	struct v4l2_ctrl_handler hdl;
+	struct v4l2_flash_ctrl ctrl;
+	struct v4l2_flash_ctrl_config config;
+	char **strobe_providers_menu;
+};
+
+static inline struct v4l2_flash *v4l2_subdev_to_v4l2_flash(
+							struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct v4l2_flash, sd);
+}
+
+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_LED_CLASS
+/**
+ * v4l2_flash_init - initialize V4L2 flash led sub-device
+ * @led_fdev:	the LED Flash Class device to wrap
+ * @config:	initialization data for V4L2 Flash controls
+ * @flash_ops:	V4L2 Flash device ops
+ * @out_flash:	handler to the new V4L2 Flash device
+ *
+ * Create V4L2 subdev wrapping given LED subsystem device.
+
+ * Returns: 0 on success or negative error value on failure
+ */
+int v4l2_flash_init(struct led_classdev_flash *led_fdev,
+		    struct v4l2_flash_ctrl_config *config,
+		    const struct v4l2_flash_ops *flash_ops,
+		    struct v4l2_flash **out_flash);
+
+/**
+ * v4l2_flash_release - release V4L2 Flash sub-device
+ * @flash: the V4L2 Flash device to release
+ *
+ * Release V4L2 flash led subdev.
+ */
+void v4l2_flash_release(struct v4l2_flash *v4l2_flash);
+
+#else
+#define v4l2_flash_init(led_cdev, config, flash_ops, out_flash) (0)
+#define v4l2_flash_release(v4l2_flash)
+#endif /* CONFIG_V4L2_FLASH_LED_CLASS */
+
+#endif /* _V4L2_FLASH_H */
-- 
1.7.9.5


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

* [PATCH/RFC v4 16/21] leds: Add support for max77693 mfd flash cell
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
                   ` (14 preceding siblings ...)
  2014-07-11 14:04 ` [PATCH/RFC v4 15/21] media: Add registration helpers for V4L2 flash Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-21 14:12   ` Sakari Ailus
  2014-07-11 14:04 ` [PATCH/RFC v4 17/21] DT: Add documentation for the mfd Maxim max77693 Jacek Anaszewski
                   ` (6 subsequent siblings)
  22 siblings, 1 reply; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Andrzej Hajda,
	Bryan Wu, Richard Purdie, SangYoung Son, Samuel Ortiz

This patch adds led-flash support to Maxim max77693 chipset.
A device can be exposed to user space through LED subsystem
sysfs interface or through V4L2 subdevice when the support
for V4L2 Flash sub-devices 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: Jacek Anaszewski <j.anaszewski@samsung.com>
Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Acked-by: Lee Jones <lee.jones@linaro.org>
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>
---
 drivers/leds/Kconfig         |    9 +
 drivers/leds/Makefile        |    1 +
 drivers/leds/leds-max77693.c | 1070 ++++++++++++++++++++++++++++++++++++++++++
 drivers/mfd/max77693.c       |    5 +-
 include/linux/mfd/max77693.h |   40 ++
 5 files changed, 1124 insertions(+), 1 deletion(-)
 create mode 100644 drivers/leds/leds-max77693.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 5032c6f..794055e 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -457,6 +457,15 @@ 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
+	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 237c5ba..da1a4ba 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -55,6 +55,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..38a2398
--- /dev/null
+++ b/drivers/leds/leds-max77693.c
@@ -0,0 +1,1070 @@
+/*
+ * LED Flash Class driver for the flash cell of max77693 mfd.
+ *
+ *	Copyright (C) 2014, Samsung Electronics Co., Ltd.
+ *
+ *	Authors: Jacek Anaszewski <j.anaszewski@samsung.com>
+ *		 Andrzej Hajda <a.hajda@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 <asm/div64.h>
+#include <linux/led-class-flash.h>
+#include <linux/led-flash-manager.h>
+#include <linux/mfd/max77693.h>
+#include <linux/mfd/max77693-private.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-flash.h>
+
+#define MAX77693_LED_NAME_1		"max77693-flash_1"
+#define MAX77693_LED_NAME_2		"max77693-flash_2"
+
+#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_FLASH1			(1 << 0)
+#define MAX77693_MODE_FLASH2			(1 << 1)
+#define MAX77693_MODE_TORCH1			(1 << 2)
+#define MAX77693_MODE_TORCH2			(1 << 3)
+#define MAX77693_MODE_FLASH_EXTERNAL1		(1 << 4)
+#define MAX77693_MODE_FLASH_EXTERNAL2		(1 << 5)
+
+enum {
+	FLED1,
+	FLED2
+};
+
+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_flash ldev1;
+	struct work_struct work1_brightness_set;
+	struct v4l2_flash *v4l2_flash1;
+
+	struct led_classdev_flash ldev2;
+	struct work_struct work2_brightness_set;
+	struct v4l2_flash *v4l2_flash2;
+
+	unsigned int torch1_brightness;
+	unsigned int torch2_brightness;
+	unsigned int flash1_timeout;
+	unsigned int flash2_timeout;
+	unsigned int current_flash_timeout;
+	unsigned int mode_flags;
+	u8 torch_iout_reg;
+	bool iout_joint;
+};
+
+struct max77693_led_settings {
+	struct led_flash_setting torch_brightness;
+	struct led_flash_setting flash_brightness;
+	struct led_flash_setting flash_timeout;
+};
+
+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 inline struct max77693_led *ldev1_to_led(
+					struct led_classdev_flash *ldev1)
+{
+	return container_of(ldev1, struct max77693_led, ldev1);
+}
+
+static inline struct max77693_led *ldev2_to_led(
+					struct led_classdev_flash *ldev2)
+{
+	return container_of(ldev2, struct max77693_led, ldev2);
+}
+
+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_TORCH1) {
+		if (p->trigger[FLED1] & MAX77693_LED_TRIG_SOFT)
+			v |= MAX77693_FLASH_EN_ON << MAX77693_TORCH_EN1_SHIFT;
+	}
+
+	if (mode & MAX77693_MODE_TORCH2) {
+		if (p->trigger[FLED2] & MAX77693_LED_TRIG_SOFT)
+			v |= MAX77693_FLASH_EN_ON << MAX77693_TORCH_EN2_SHIFT;
+	}
+
+	if (mode & MAX77693_MODE_FLASH1) {
+		if (p->trigger[FLED1] & MAX77693_LED_TRIG_SOFT)
+			v |= MAX77693_FLASH_EN_ON << MAX77693_FLASH_EN1_SHIFT;
+	} else if (mode & MAX77693_MODE_FLASH_EXTERNAL1) {
+		if (p->trigger[FLED1] & MAX77693_LED_TRIG_EXT)
+			v |= MAX77693_FLASH_EN_FLASH << MAX77693_FLASH_EN1_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[FLED1] & MAX77693_LED_TRIG_EXT)
+			v |= MAX77693_FLASH_EN_TORCH << MAX77693_TORCH_EN1_SHIFT;
+	}
+
+	if (mode & MAX77693_MODE_FLASH2) {
+		if (p->trigger[FLED2] & MAX77693_LED_TRIG_SOFT)
+			v |= MAX77693_FLASH_EN_ON << MAX77693_FLASH_EN2_SHIFT;
+	} else if (mode & MAX77693_MODE_FLASH_EXTERNAL1) {
+		if (p->trigger[FLED2] & 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[FLED2] & 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_TORCH1 | MAX77693_MODE_TORCH2)) {
+		ret = regmap_write(rmap, MAX77693_LED_REG_FLASH_EN, 0);
+		if (ret < 0)
+			return ret;
+	}
+
+	return regmap_write(rmap, MAX77693_LED_REG_FLASH_EN, v);
+}
+
+static int max77693_add_mode(struct max77693_led *led, unsigned int mode)
+{
+	int ret;
+
+	/* Span mode on FLED2 for joint iouts case */
+	if (led->iout_joint)
+		mode |= (mode << 1);
+
+	/*
+	 * Torch mode once enabled remains active until turned off,
+	 * and thus no action is required in this case.
+	 */
+	if ((mode & MAX77693_MODE_TORCH1) &&
+	    (led->mode_flags & MAX77693_MODE_TORCH1))
+		return 0;
+	if ((mode & MAX77693_MODE_TORCH2) &&
+	    (led->mode_flags & MAX77693_MODE_TORCH2))
+		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_EXTERNAL1)
+		led->mode_flags &= (~MAX77693_MODE_TORCH1 &
+				    ~MAX77693_MODE_FLASH1);
+	if (mode & MAX77693_MODE_FLASH_EXTERNAL2)
+		led->mode_flags &= (~MAX77693_MODE_TORCH2 &
+				    ~MAX77693_MODE_FLASH2);
+
+	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 every subsequent torch mode
+	 * setting.
+	 */
+	if (mode & MAX77693_MODE_FLASH1 ||
+	    mode & MAX77693_MODE_FLASH_EXTERNAL1 ||
+	    mode & MAX77693_MODE_FLASH2 ||
+	    mode & MAX77693_MODE_FLASH_EXTERNAL2)
+		led->mode_flags &= ~mode;
+
+	return ret;
+}
+
+static int max77693_clear_mode(struct max77693_led *led, unsigned int mode)
+{
+	int ret;
+
+	/* Span mode on FLED2 for joint iouts case */
+	if (led->iout_joint)
+		mode |= (mode << 1);
+
+	led->mode_flags &= ~mode;
+
+	ret = max77693_set_mode(led, led->mode_flags);
+
+	return ret;
+}
+
+static int max77693_set_torch_current(struct max77693_led *led,
+					int led_id,
+					u32 micro_amp)
+{
+	struct max77693_led_platform_data *p = led->pdata;
+	struct regmap *rmap = led->regmap;
+	u32 iout[2], iout_max[2];
+	u8 iout1_reg = 0, iout2_reg = 0;
+
+	iout_max[FLED1] = p->iout_torch[FLED1];
+	iout_max[FLED2] = p->iout_torch[FLED2];
+
+	if (led_id == FLED1) {
+		/*
+		 * Preclude splitting current to FLED2 if we
+		 * are driving two separate leds.
+		 */
+		if (!led->iout_joint)
+			iout_max[FLED2] = 0;
+		max77693_calc_iout(iout, micro_amp, iout_max);
+	} else if (led_id == FLED2) {
+		iout_max[FLED1] = 0;
+		max77693_calc_iout(iout, micro_amp, iout_max);
+	}
+
+	if (led_id == FLED1 || led->iout_joint) {
+		iout1_reg = max77693_led_iout_to_reg(iout[FLED1]);
+		led->torch_iout_reg &= 0xf0;
+	}
+	if (led_id == FLED2 || led->iout_joint) {
+		iout2_reg = max77693_led_iout_to_reg(iout[FLED2]);
+		led->torch_iout_reg &= 0x0f;
+	}
+
+	led->torch_iout_reg |= (iout1_reg | iout2_reg <<
+					MAX77693_TORCH_IOUT_BITS);
+
+	return regmap_write(rmap, MAX77693_LED_REG_ITORCH,
+						led->torch_iout_reg);
+}
+
+static int max77693_set_flash_current(struct max77693_led *led,
+					int led_id,
+					u32 micro_amp)
+{
+	struct max77693_led_platform_data *p = led->pdata;
+	struct regmap *rmap = led->regmap;
+	u32 iout[2], iout_max[2];
+	u8 iout1_reg, iout2_reg;
+	int ret = -EINVAL;
+
+	iout_max[FLED1] = p->iout_flash[FLED1];
+	iout_max[FLED2] = p->iout_flash[FLED2];
+
+	if (led_id == FLED1) {
+		/*
+		 * Preclude splitting current to FLED2 if we
+		 * are driving two separate leds.
+		 */
+		if (!led->iout_joint)
+			iout_max[FLED2] = 0;
+		max77693_calc_iout(iout, micro_amp, iout_max);
+	} else if (led_id == FLED2) {
+		iout_max[FLED1] = 0;
+		max77693_calc_iout(iout, micro_amp, iout_max);
+	}
+
+	if (led_id == FLED1 || led->iout_joint) {
+		iout1_reg = max77693_led_iout_to_reg(iout[FLED1]);
+		ret = regmap_write(rmap, MAX77693_LED_REG_IFLASH1,
+							iout1_reg);
+		if (ret < 0)
+			return ret;
+	}
+	if (led_id == FLED2 || led->iout_joint) {
+		iout2_reg = max77693_led_iout_to_reg(iout[FLED2]);
+		ret = regmap_write(rmap, MAX77693_LED_REG_IFLASH2,
+							iout2_reg);
+	}
+
+	return ret;
+}
+
+static int max77693_set_timeout(struct max77693_led *led, u32 timeout)
+{
+	struct max77693_led_platform_data *p = led->pdata;
+	struct regmap *rmap = led->regmap;
+	u8 v;
+	int ret;
+
+	v = max77693_flash_timeout_to_reg(timeout);
+
+	if (p->trigger_type[FLASH] == MAX77693_LED_TRIG_TYPE_LEVEL)
+		v |= MAX77693_FLASH_TIMER_LEVEL;
+
+	ret = regmap_write(rmap, MAX77693_LED_REG_FLASH_TIMER, v);
+	if (ret < 0)
+		return ret;
+
+	led->current_flash_timeout = timeout;
+
+	return 0;
+}
+
+static int max77693_strobe_status_get(struct max77693_led *led, bool *state)
+{
+	struct regmap *rmap = led->regmap;
+	unsigned int v;
+	int ret;
+
+	ret = regmap_read(rmap, MAX77693_LED_REG_FLASH_INT_STATUS, &v);
+	if (ret < 0)
+		return ret;
+
+	*state = v & MAX77693_LED_STATUS_FLASH_ON;
+
+	return ret;
+}
+
+static int max77693_int_flag_get(struct max77693_led *led, unsigned int *v)
+{
+	struct regmap *rmap = led->regmap;
+
+	return regmap_read(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 i, firstLed, lastLed, ret;
+	u32 max_flash_curr[2];
+	u8 v;
+
+	/*
+	 * Initialize only flash current. Torch current doesn't
+	 * require initialization as ITORCH register is written with
+	 * new value each time brightness_set op is called.
+	 */
+	if (led->iout_joint) {
+		firstLed = FLED1;
+		lastLed = FLED1;
+		max_flash_curr[FLED1] = p->iout_flash[FLED1] +
+					p->iout_flash[FLED2];
+	} else {
+		firstLed = p->fleds[FLED1] ? FLED1 : FLED2;
+		lastLed = p->num_leds == 2 ? FLED2 : firstLed;
+		max_flash_curr[FLED1] = p->iout_flash[FLED1];
+		max_flash_curr[FLED2] = p->iout_flash[FLED2];
+	}
+
+	for (i = firstLed; i <= lastLed; ++i) {
+		ret = max77693_set_flash_current(led, i,
+					max_flash_curr[i]);
+		if (ret < 0)
+			return ret;
+	}
+
+	v = MAX77693_TORCH_NO_TIMER | MAX77693_LED_TRIG_TYPE_LEVEL;
+	ret = regmap_write(rmap, MAX77693_LED_REG_ITORCHTIMER, v);
+	if (ret < 0)
+		return ret;
+
+	ret = max77693_set_timeout(led,	p->flash_timeout);
+	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 = regmap_write(rmap, MAX77693_LED_REG_MAX_FLASH1, v);
+	if (ret < 0)
+		return ret;
+	ret = regmap_write(rmap, MAX77693_LED_REG_MAX_FLASH2, 0);
+	if (ret < 0)
+		return ret;
+
+	if (p->boost_mode == MAX77693_LED_BOOST_FIXED)
+		v = MAX77693_FLASH_BOOST_FIXED;
+	else
+		v = p->boost_mode | p->boost_mode << 1;
+	if (p->fleds[FLED1] && p->fleds[FLED2])
+		v |= MAX77693_FLASH_BOOST_LEDNUM_2;
+	ret = regmap_write(rmap, MAX77693_LED_REG_VOUT_CNTL, v);
+	if (ret < 0)
+		return ret;
+
+	v = max77693_led_vout_to_reg(p->boost_vout);
+	ret = regmap_write(rmap, MAX77693_LED_REG_VOUT_FLASH1, v);
+	if (ret < 0)
+		return ret;
+
+	return max77693_set_mode(led, MAX77693_MODE_OFF);
+}
+
+static int max77693_led_brightness_set(struct max77693_led *led,
+					int led_id, enum led_brightness value)
+{
+	int ret;
+
+	mutex_lock(&led->lock);
+
+	if (value == 0) {
+		ret = max77693_clear_mode(led, MAX77693_MODE_TORCH1 << led_id);
+		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_id, value *
+					     MAX77693_TORCH_IOUT_STEP);
+	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_TORCH1 << led_id);
+	if (ret < 0)
+		dev_dbg(&led->pdev->dev,
+			"Failed to set torch mode (%d)\n",
+			ret);
+unlock:
+	mutex_unlock(&led->lock);
+	return ret;
+}
+
+#define MAX77693_LED_BRIGHTNESS_SET_WORK(ID)				\
+static void max77693_led##ID##_brightness_set_work(			\
+					struct work_struct *work)	\
+{									\
+	struct max77693_led *led = container_of(work,			\
+					struct max77693_led,		\
+					work##ID##_brightness_set);	\
+									\
+	max77693_led_brightness_set(led, FLED##ID,			\
+					led->torch##ID##_brightness);	\
+}
+
+/* LED subsystem callbacks */
+
+#define MAX77693_LED_TORCH_BRIGHTNESS_SET(ID)				\
+static int max77693_led##ID##_torch_brightness_set(			\
+				struct led_classdev *led_cdev,		\
+				enum led_brightness value)		\
+{									\
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);	\
+	struct max77693_led *led = ldev##ID##_to_led(flash);		\
+									\
+	return max77693_led_brightness_set(led, FLED##ID, value);	\
+}
+
+#define MAX77693_LED_BRIGHTNESS_SET(ID)					\
+static void max77693_led##ID##_brightness_set(				\
+				struct led_classdev *led_cdev,		\
+				enum led_brightness value)		\
+{									\
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);	\
+	struct max77693_led *led = ldev##ID##_to_led(flash);		\
+									\
+	led->torch##ID##_brightness = value;				\
+	schedule_work(&led->work##ID##_brightness_set);			\
+}
+
+#define MAX77693_LED_FLASH_BRIGHTNESS_SET(ID)				\
+static int max77693_led##ID##_flash_brightness_set(			\
+				struct led_classdev_flash *flash,	\
+				u32 brightness)				\
+{									\
+	struct max77693_led *led = ldev##ID##_to_led(flash);		\
+	int ret;							\
+									\
+	mutex_lock(&led->lock);						\
+	ret = max77693_set_flash_current(led, FLED##ID, brightness);	\
+	mutex_unlock(&led->lock);					\
+									\
+	return ret;							\
+}
+
+#define MAX77693_LED_FLASH_STROBE_SET(ID)				\
+static int max77693_led##ID##_flash_strobe_set(				\
+				struct led_classdev_flash *flash,	\
+				bool state)				\
+{									\
+	struct max77693_led *led = ldev##ID##_to_led(flash);		\
+	int ret;							\
+									\
+	mutex_lock(&led->lock);						\
+									\
+	if (!state) {							\
+		ret = max77693_clear_mode(led,				\
+					  MAX77693_MODE_FLASH##ID);	\
+		goto unlock;						\
+	}								\
+									\
+	if (led->flash##ID##_timeout != led->current_flash_timeout) {	\
+		ret = max77693_set_timeout(led,				\
+					   led->flash##ID##_timeout);	\
+		if (ret < 0)						\
+			goto unlock;					\
+	}								\
+									\
+	ret = max77693_add_mode(led, MAX77693_MODE_FLASH##ID);		\
+									\
+unlock:									\
+	mutex_unlock(&led->lock);					\
+	return ret;							\
+}
+
+#define MAX77693_LED_FLASH_EXTERNAL_STROBE_SET(ID)			\
+static int max77693_led##ID##_external_strobe_set(			\
+				struct led_classdev_flash *flash,	\
+				bool enable)				\
+{									\
+	struct max77693_led *led = ldev##ID##_to_led(flash);		\
+	int ret;							\
+									\
+	mutex_lock(&led->lock);						\
+									\
+	if (enable)							\
+		ret = max77693_add_mode(led,				\
+				MAX77693_MODE_FLASH_EXTERNAL##ID);	\
+	else								\
+		ret = max77693_clear_mode(led,				\
+				MAX77693_MODE_FLASH_EXTERNAL##ID);	\
+									\
+	mutex_unlock(&led->lock);					\
+									\
+	return ret;							\
+}
+
+#define MAX77693_LED_FLASH_FAULT_GET(ID)				\
+static int max77693_led##ID##_flash_fault_get(				\
+				struct led_classdev_flash *flash,	\
+				u32 *fault)				\
+{									\
+	struct max77693_led *led = ldev##ID##_to_led(flash);		\
+	unsigned int v;							\
+	int ret;							\
+									\
+	ret = max77693_int_flag_get(led, &v);				\
+	if (ret < 0)							\
+		return ret;						\
+									\
+	*fault = 0;							\
+									\
+	if (v & MAX77693_LED_FLASH_INT_FLED##ID##_OPEN)			\
+		*fault |= LED_FAULT_OVER_VOLTAGE;			\
+	if (v & MAX77693_LED_FLASH_INT_FLED##ID##_SHORT)		\
+		*fault |= LED_FAULT_SHORT_CIRCUIT;			\
+	if (v & MAX77693_LED_FLASH_INT_OVER_CURRENT)			\
+		*fault |= LED_FAULT_OVER_CURRENT;			\
+									\
+	return 0;							\
+}
+
+#define MAX77693_LED_FLASH_STROBE_GET(ID)				\
+static int max77693_led##ID##_flash_strobe_get(				\
+				struct led_classdev_flash *flash,	\
+				bool *state)				\
+{									\
+	struct max77693_led *led = ldev##ID##_to_led(flash);		\
+									\
+	if (!state)							\
+		return -EINVAL;						\
+									\
+	return max77693_strobe_status_get(led, state);			\
+}
+
+#define MAX77693_LED_FLASH_TIMEOUT_SET(ID)				\
+static int max77693_led##ID##_flash_timeout_set(			\
+				struct led_classdev_flash *flash,	\
+				u32 timeout)				\
+{									\
+	struct max77693_led *led = ldev##ID##_to_led(flash);		\
+									\
+	mutex_lock(&led->lock);						\
+	led->flash##ID##_timeout = timeout;				\
+	mutex_unlock(&led->lock);					\
+									\
+	return 0;							\
+}
+
+MAX77693_LED_BRIGHTNESS_SET(1)
+MAX77693_LED_BRIGHTNESS_SET_WORK(1)
+MAX77693_LED_TORCH_BRIGHTNESS_SET(1)
+MAX77693_LED_FLASH_BRIGHTNESS_SET(1)
+MAX77693_LED_FLASH_STROBE_SET(1)
+MAX77693_LED_FLASH_STROBE_GET(1)
+MAX77693_LED_FLASH_EXTERNAL_STROBE_SET(1)
+MAX77693_LED_FLASH_TIMEOUT_SET(1)
+MAX77693_LED_FLASH_FAULT_GET(1)
+
+MAX77693_LED_BRIGHTNESS_SET(2)
+MAX77693_LED_BRIGHTNESS_SET_WORK(2)
+MAX77693_LED_TORCH_BRIGHTNESS_SET(2)
+MAX77693_LED_FLASH_BRIGHTNESS_SET(2)
+MAX77693_LED_FLASH_STROBE_SET(2)
+MAX77693_LED_FLASH_STROBE_GET(2)
+MAX77693_LED_FLASH_EXTERNAL_STROBE_SET(2)
+MAX77693_LED_FLASH_TIMEOUT_SET(2)
+MAX77693_LED_FLASH_FAULT_GET(2)
+
+static void max77693_led_parse_dt(struct max77693_led_platform_data *p,
+			    struct device_node *node)
+{
+	of_property_read_u32_array(node, "iout-torch", p->iout_torch, 2);
+	of_property_read_u32_array(node, "iout-flash", p->iout_flash, 2);
+	of_property_read_u32_array(node, "maxim,fleds", p->fleds, 2);
+	of_property_read_u32_array(node, "maxim,trigger", p->trigger, 2);
+	of_property_read_u32_array(node, "maxim,trigger-type", p->trigger_type,
+									2);
+	of_property_read_u32(node, "flash-timeout", &p->flash_timeout);
+	of_property_read_u32(node, "maxim,boost-mode", &p->boost_mode);
+	of_property_read_u32(node, "maxim,boost-vout", &p->boost_vout);
+	of_property_read_u32(node, "maxim,num-leds", &p->num_leds);
+	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;
+
+	p->boost_mode = clamp_val(p->boost_mode, MAX77693_LED_BOOST_NONE,
+			    MAX77693_LED_BOOST_FIXED);
+	p->num_leds = clamp_val(p->num_leds, 1, 2);
+
+	for (i = 0; i < ARRAY_SIZE(p->fleds); ++i)
+		p->fleds[i] = clamp_val(p->fleds[i], 0, 1);
+
+	/* Ensure fleds configuration is sane */
+	if (!p->fleds[FLED1] && !p->fleds[FLED2]) {
+		p->fleds[FLED1] = p->fleds[FLED2] = 1;
+		p->num_leds = 1;
+	}
+
+	/* Ensure num_leds is consistent with fleds configuration */
+	if ((!p->fleds[FLED1] || !p->fleds[FLED2]) && p->num_leds == 2)
+		p->num_leds = 1;
+
+	/*
+	 * boost must be enabled if current outputs
+	 * are connected to separate leds.
+	 */
+	if ((p->num_leds == 2 || (p->fleds[FLED1] && p->fleds[FLED2])) &&
+	    p->boost_mode == MAX77693_LED_BOOST_NONE)
+		p->boost_mode = MAX77693_LED_BOOST_FIXED;
+
+	max = p->boost_mode ? MAX77693_FLASH_IOUT_MAX_2LEDS :
+				MAX77693_FLASH_IOUT_MAX_1LED;
+
+	if (p->fleds[FLED1]) {
+		clamp_align(&p->iout_torch[FLED1], MAX77693_TORCH_IOUT_MIN,
+			    MAX77693_TORCH_IOUT_MAX, MAX77693_TORCH_IOUT_STEP);
+		clamp_align(&p->iout_flash[FLED1], MAX77693_FLASH_IOUT_MIN,
+			    max, MAX77693_FLASH_IOUT_STEP);
+	} else {
+		p->iout_torch[FLED1] = p->iout_flash[FLED1] = 0;
+	}
+	if (p->fleds[FLED2]) {
+		clamp_align(&p->iout_torch[FLED2], MAX77693_TORCH_IOUT_MIN,
+			    MAX77693_TORCH_IOUT_MAX, MAX77693_TORCH_IOUT_STEP);
+		clamp_align(&p->iout_flash[FLED2], MAX77693_FLASH_IOUT_MIN,
+			    max, MAX77693_FLASH_IOUT_STEP);
+	} else {
+		p->iout_torch[FLED2] = p->iout_flash[FLED2] = 0;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(p->trigger); ++i)
+		p->trigger[i] = clamp_val(p->trigger[i], 0, 7);
+	for (i = 0; i < ARRAY_SIZE(p->trigger_type); ++i)
+		p->trigger_type[i] = clamp_val(p->trigger_type[i],
+					MAX77693_LED_TRIG_TYPE_EDGE,
+					MAX77693_LED_TRIG_TYPE_LEVEL);
+
+	clamp_align(&p->flash_timeout, MAX77693_FLASH_TIMEOUT_MIN,
+		    MAX77693_FLASH_TIMEOUT_MAX, MAX77693_FLASH_TIMEOUT_STEP);
+
+	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;
+}
+
+#define MAX77693_LED_INIT_FLASH_OPS(ID)						\
+const struct led_flash_ops flash_ops##ID = {					\
+										\
+	.flash_brightness_set	= max77693_led##ID##_flash_brightness_set,	\
+	.strobe_set 		= max77693_led##ID##_flash_strobe_set,		\
+	.strobe_get		= max77693_led##ID##_flash_strobe_get,		\
+	.timeout_set		= max77693_led##ID##_flash_timeout_set,		\
+	.external_strobe_set	= max77693_led##ID##_external_strobe_set,	\
+	.fault_get		= max77693_led##ID##_flash_fault_get,		\
+};
+
+MAX77693_LED_INIT_FLASH_OPS(1)
+MAX77693_LED_INIT_FLASH_OPS(2)
+
+static void max77693_init_flash_settings(struct max77693_led *led,
+					 struct max77693_led_settings *s,
+					 int led_id)
+{
+	struct max77693_led_platform_data *p = led->pdata;
+	struct led_flash_setting *setting;
+
+	/* Init torch intensity setting */
+	setting = &s->torch_brightness;
+	setting->min = led->iout_joint ?
+				MAX77693_TORCH_IOUT_MIN * 2 :
+				MAX77693_TORCH_IOUT_MIN;
+	setting->max = led->iout_joint ?
+			p->iout_torch[FLED1] + p->iout_torch[FLED2] :
+			p->iout_torch[led_id];
+	setting->step = MAX77693_TORCH_IOUT_STEP;
+	setting->val = setting->max;
+
+	/* Init flash intensity setting */
+	setting = &s->flash_brightness;
+	setting->min = led->iout_joint ?
+				MAX77693_FLASH_IOUT_MIN * 2 :
+				MAX77693_FLASH_IOUT_MIN;
+	setting->max = led->iout_joint ?
+			p->iout_flash[FLED1] + p->iout_flash[FLED2] :
+			p->iout_flash[led_id];
+	setting->step = MAX77693_FLASH_IOUT_STEP;
+	setting->val = setting->max;
+
+	/* Init flash timeout setting */
+	setting = &s->flash_timeout;
+	setting->min = MAX77693_FLASH_TIMEOUT_MIN;
+	setting->max = MAX77693_FLASH_TIMEOUT_MAX;
+	setting->step = MAX77693_FLASH_TIMEOUT_STEP;
+	setting->val = p->flash_timeout;
+}
+
+#ifdef CONFIG_V4L2_FLASH_LED_CLASS
+static void max77693_init_v4l2_ctrl_config(struct max77693_led_settings *s,
+					struct v4l2_flash_ctrl_config *config)
+{
+	struct led_flash_setting *setting;
+	struct v4l2_ctrl_config *c;
+
+	c = &config->torch_intensity;
+	setting = &s->torch_brightness;
+	c->min = setting->min;
+	c->max = setting->max;
+	c->step = setting->step;
+	c->def = setting->val;
+
+	c = &config->flash_intensity;
+	setting = &s->flash_brightness;
+	c->min = setting->min;
+	c->max = setting->max;
+	c->step = setting->step;
+	c->def = setting->val;
+
+	c = &config->flash_timeout;
+	setting = &s->flash_timeout;
+	c->min = setting->min;
+	c->max = setting->max;
+	c->step = setting->step;
+	c->def = setting->val;
+
+	/* Init flash faults config */
+	config->flash_faults =	V4L2_FLASH_FAULT_OVER_VOLTAGE |
+				V4L2_FLASH_FAULT_SHORT_CIRCUIT |
+				V4L2_FLASH_FAULT_OVER_CURRENT;
+}
+#else
+#define max77693_init_v4l2_ctrl_config(s, config)
+#endif
+
+#define MAX77693_LED_REGISTER_FLASH(ID)					\
+static int max77693_register_led##ID(struct max77693_led *led)		\
+{									\
+	struct platform_device *pdev = led->pdev;			\
+	struct device *dev = &pdev->dev;				\
+	struct led_classdev_flash *flash;				\
+	struct led_classdev *led_cdev;					\
+	struct max77693_led_platform_data *p = led->pdata;		\
+	struct v4l2_flash_ctrl_config v4l2_flash_config;		\
+	struct max77693_led_settings settings;				\
+	int ret;							\
+									\
+	flash = &led->ldev##ID;						\
+									\
+	/* Init flash settings */					\
+	max77693_init_flash_settings(led, &settings, FLED##ID);		\
+	/* Init V4L2 Flash controls basing on initialized settings */	\
+	max77693_init_v4l2_ctrl_config(&settings, &v4l2_flash_config);	\
+									\
+	/* Init led class */						\
+	led_cdev = &flash->led_cdev;					\
+	led_cdev->name = MAX77693_LED_NAME_##ID;			\
+	led_cdev->brightness_set = max77693_led##ID##_brightness_set;	\
+	led_cdev->torch_brightness_set =				\
+			max77693_led##ID##_torch_brightness_set;	\
+	led_cdev->max_brightness = settings.torch_brightness.val /	\
+					MAX77693_TORCH_IOUT_STEP;	\
+	led_cdev->flags |= LED_DEV_CAP_FLASH;				\
+									\
+	INIT_WORK(&led->work##ID##_brightness_set,			\
+			max77693_led##ID##_brightness_set_work);	\
+									\
+	flash->ops = &flash_ops##ID;					\
+	flash->brightness = settings.flash_brightness;			\
+	flash->timeout = settings.flash_timeout;			\
+	led->flash##ID##_timeout = flash->timeout.val;			\
+									\
+	if (p->trigger[FLED##ID] & MAX77693_LED_TRIG_FLASH)		\
+		flash->has_external_strobe = true;			\
+									\
+	/* Register in the LED subsystem. */				\
+	ret = led_classdev_flash_register(&pdev->dev, flash,		\
+						dev->of_node);		\
+	if (ret < 0)							\
+		return ret;						\
+									\
+	ret = v4l2_flash_init(flash,					\
+			      &v4l2_flash_config,			\
+			      led_get_v4l2_flash_ops(),			\
+			      &led->v4l2_flash##ID);			\
+	if (ret < 0)							\
+		goto err_v4l2_flash_init;				\
+									\
+	return 0;							\
+									\
+err_v4l2_flash_init:							\
+	led_classdev_flash_unregister(flash);				\
+									\
+	return ret;							\
+}
+
+MAX77693_LED_REGISTER_FLASH(1)
+MAX77693_LED_REGISTER_FLASH(2)
+
+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;
+	int ret;
+
+	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	led->pdev = pdev;
+	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);
+
+	if (p->num_leds == 1 && p->fleds[FLED1] && p->fleds[FLED2])
+		led->iout_joint = true;
+
+	ret = max77693_setup(led);
+	if (ret < 0)
+		return ret;
+
+	if (led->iout_joint || p->fleds[FLED1]) {
+		ret = max77693_register_led1(led);
+		if (ret < 0)
+			goto err_register_led1;
+	}
+
+	if (!led->iout_joint && p->fleds[FLED2]) {
+		ret = max77693_register_led2(led);
+		if (ret < 0)
+			goto err_register_led2;
+	}
+
+	return 0;
+
+err_register_led2:
+	if (!p->fleds[FLED1])
+		goto err_register_led1;
+	v4l2_flash_release(led->v4l2_flash1);
+	led_classdev_flash_unregister(&led->ldev1);
+err_register_led1:
+	mutex_destroy(&led->lock);
+
+	return ret;
+}
+
+static int max77693_led_remove(struct platform_device *pdev)
+{
+	struct max77693_led *led = platform_get_drvdata(pdev);
+	struct max77693_led_platform_data *p = led->pdata;
+
+	if (led->iout_joint || p->fleds[FLED1]) {
+		v4l2_flash_release(led->v4l2_flash1);
+		led_classdev_flash_unregister(&led->ldev1);
+		cancel_work_sync(&led->work1_brightness_set);
+	}
+	if (!led->iout_joint && p->fleds[FLED2]) {
+		v4l2_flash_release(led->v4l2_flash2);
+		led_classdev_flash_unregister(&led->ldev2);
+		cancel_work_sync(&led->work2_brightness_set);
+	}
+	mutex_destroy(&led->lock);
+
+	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("Jacek Anaszewski <j.anaszewski@samsung.com>");
+MODULE_AUTHOR("Andrzej Hajda <a.hajda@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 249c139..cf008f4 100644
--- a/drivers/mfd/max77693.c
+++ b/drivers/mfd/max77693.c
@@ -44,9 +44,12 @@
 static const struct mfd_cell max77693_devs[] = {
 	{ .name = "max77693-pmic", },
 	{ .name = "max77693-charger", },
-	{ .name = "max77693-flash", },
 	{ .name = "max77693-muic", },
 	{ .name = "max77693-haptic", },
+	{
+		.name = "max77693-flash",
+		.of_compatible = "maxim,max77693-flash",
+	},
 };
 
 static const struct regmap_config max77693_regmap_config = {
diff --git a/include/linux/mfd/max77693.h b/include/linux/mfd/max77693.h
index 3f3dc45..f0b6585 100644
--- a/include/linux/mfd/max77693.h
+++ b/include/linux/mfd/max77693.h
@@ -63,6 +63,45 @@ struct max77693_muic_platform_data {
 	int path_uart;
 };
 
+/* MAX77693 led flash */
+
+/* triggers */
+enum max77693_led_trigger {
+	MAX77693_LED_TRIG_OFF,
+	MAX77693_LED_TRIG_FLASH,
+	MAX77693_LED_TRIG_TORCH,
+	MAX77693_LED_TRIG_EXT,
+	MAX77693_LED_TRIG_SOFT,
+};
+
+/* trigger types */
+enum max77693_led_trigger_type {
+	MAX77693_LED_TRIG_TYPE_EDGE,
+	MAX77693_LED_TRIG_TYPE_LEVEL,
+};
+
+/* boost modes */
+enum max77693_led_boost_mode {
+	MAX77693_LED_BOOST_NONE,
+	MAX77693_LED_BOOST_ADAPTIVE,
+	MAX77693_LED_BOOST_FIXED,
+};
+
+struct max77693_led_platform_data {
+	u32 fleds[2];
+	u32 iout_torch[2];
+	u32 iout_flash[2];
+	u32 trigger[2];
+	u32 trigger_type[2];
+	u32 num_leds;
+	u32 boost_mode;
+	u32 flash_timeout;
+	u32 boost_vout;
+	u32 low_vsys;
+};
+
+/* MAX77693 */
+
 struct max77693_platform_data {
 	/* regulator data */
 	struct max77693_regulator_data *regulators;
@@ -70,5 +109,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] 55+ messages in thread

* [PATCH/RFC v4 17/21] DT: Add documentation for the mfd Maxim max77693
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
                   ` (15 preceding siblings ...)
  2014-07-11 14:04 ` [PATCH/RFC v4 16/21] leds: Add support for max77693 mfd flash cell Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-11 14:04 ` [PATCH/RFC v4 18/21] leds: Add driver for AAT1290 current regulator Jacek Anaszewski
                   ` (5 subsequent siblings)
  22 siblings, 0 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Andrzej Hajda,
	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 |   62 ++++++++++++++++++++
 1 file changed, 62 insertions(+)

diff --git a/Documentation/devicetree/bindings/mfd/max77693.txt b/Documentation/devicetree/bindings/mfd/max77693.txt
index 11921cc..0c3db3d 100644
--- a/Documentation/devicetree/bindings/mfd/max77693.txt
+++ b/Documentation/devicetree/bindings/mfd/max77693.txt
@@ -27,6 +27,55 @@ 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"
+- maxim,num-leds : number of connected leds
+	Possible values: 1 or 2.
+- maxim,fleds : array of current outputs in order: fled1, fled2
+	Note: both current outputs can be connected to a single led
+	Possible values:
+		0 - the output is left disconnected,
+		1 - a diode is connected to the output.
+
+Optional properties of "led-flash" node:
+- maxim,boost-mode :
+	In boost mode the device can produce up to 1.2A of total current
+	on both outputs. The maximum current on each output is reduced
+	to 625mA then. If maxim,num-leds == <2> boost must be enabled
+	(it defaults to 1 if not set):
+	Possible values:
+		0 - no boost,
+		1 - adaptive mode,
+		2 - fixed mode.
+- iout-torch : Array of maximum intensities in microamperes of the torch
+	led currents in order: fled1, fled2.
+		15625 - 250000
+- iout-flash : Array of maximum intensities in microamperes of the flash
+	led currents in order: fled1, fled2.
+	Range:
+		15625 - 1000000 (max 625000 if boost mode is enabled)
+- flash-timeout : timeout in microseconds after which flash led
+		  is turned off
+	Range:
+		62500 - 1000000
+- maxim,trigger : Array of flags indicating which trigger can activate given led
+	in order: fled1, fled2
+	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,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 +101,17 @@ Example:
 					regulator-boot-on;
 			};
 		};
+		led_flash: led-flash {
+			compatible = "maxim,max77693-flash";
+			iout-torch = <500000 0>;
+			iout-flash = <1250000 0>;
+			flash-timeout = <1000000 1000000>;
+			maxim,num-leds = <1>;
+			maxim,fleds = <1 1>;
+			maxim,trigger = <7 7>;
+			maxim,trigger-type = <0 1>;
+			maxim,boost-mode = <1>;
+			maxim,boost-vout = <5000>;
+			maxim,vsys-min = <2400>;
+		};
 	};
-- 
1.7.9.5


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

* [PATCH/RFC v4 18/21] leds: Add driver for AAT1290 current regulator
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
                   ` (16 preceding siblings ...)
  2014-07-11 14:04 ` [PATCH/RFC v4 17/21] DT: Add documentation for the mfd Maxim max77693 Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-11 14:04 ` [PATCH/RFC v4 19/21] of: Add Skyworks Solutions, Inc. vendor prefix Jacek Anaszewski
                   ` (4 subsequent siblings)
  22 siblings, 0 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Bryan Wu, Richard Purdie

This patch adds a driver for the 1.5A Step-Up
Current Regulator for Flash LEDs. The device is
programmed through a Skyworks' proprietary AS2Cwire
serial digital interface.

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        |    6 +
 drivers/leds/Makefile       |    1 +
 drivers/leds/leds-aat1290.c |  455 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 462 insertions(+)
 create mode 100644 drivers/leds/leds-aat1290.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 794055e..ef774fd 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -42,6 +42,12 @@ config LEDS_88PM860X
 	  This option enables support for on-chip LED drivers found on Marvell
 	  Semiconductor 88PM8606 PMIC.
 
+config LEDS_AAT1290
+	tristate "LED support for the AAT1290"
+	depends on LEDS_CLASS_FLASH
+	help
+	 This option enables support for the LEDs on the AAT1290.
+
 config LEDS_ATMEL_PWM
 	tristate "LED Support using Atmel PWM outputs"
 	depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index da1a4ba..41911aa 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_LEDS_TRIGGERS)		+= led-triggers.o
 
 # LED Platform Drivers
 obj-$(CONFIG_LEDS_88PM860X)		+= leds-88pm860x.o
+obj-$(CONFIG_LEDS_AAT1290)		+= leds-aat1290.o
 obj-$(CONFIG_LEDS_ATMEL_PWM)		+= leds-atmel-pwm.o
 obj-$(CONFIG_LEDS_BD2802)		+= leds-bd2802.o
 obj-$(CONFIG_LEDS_LOCOMO)		+= leds-locomo.o
diff --git a/drivers/leds/leds-aat1290.c b/drivers/leds/leds-aat1290.c
new file mode 100644
index 0000000..07d7384
--- /dev/null
+++ b/drivers/leds/leds-aat1290.c
@@ -0,0 +1,455 @@
+/*
+ *	LED Flash Class driver for the AAT1290
+ *	1.5A Step-Up Current Regulator for Flash LEDs
+ *
+ *	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/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/led-class-flash.h>
+#include <linux/led-flash-manager.h>
+#include <linux/leds.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <media/v4l2-flash.h>
+#include <linux/workqueue.h>
+
+#define AAT1290_LED_NAME		"aat1290"
+#define AAT1290_MOVIE_MODE_CURRENT_ADDR	17
+#define AAT1290_FLASH_SAFETY_TIMER_ADDR	18
+#define AAT1290_MOVIE_MODE_CONFIG_ADDR	19
+#define AAT1290_MM_CURRENT_RATIO_ADDR	20
+#define AAT1290_LATCH_TIME_US		500
+#define AAT1290_EN_SET_TICK_TIME_US	1
+#define AAT1290_MOVIE_MODE_OFF		1
+#define AAT1290_MOVIE_MODE_ON		3
+#define AAT1290_MAX_MM_CURR_PERCENT_0	16
+#define AAT1290_MAX_MM_CURR_PERCENT_100 1
+#define AAT1290_FLASH_TM_RATIO_STEP	16
+
+#define AAT1290_MM_TO_FL_1_92	1
+#define AAT1290_MM_TO_FL_3_7	2
+#define AAT1290_MM_TO_FL_5_5	3
+#define AAT1290_MM_TO_FL_7_3	4
+#define AAT1290_MM_TO_FL_9	5
+#define AAT1290_MM_TO_FL_10_7	6
+#define AAT1290_MM_TO_FL_12_4	7
+#define AAT1290_MM_TO_FL_14	8
+#define AAT1290_MM_TO_FL_15_9	9
+#define AAT1290_MM_TO_FL_17_5	10
+#define AAT1290_MM_TO_FL_19_1	11
+#define AAT1290_MM_TO_FL_20_8	12
+#define AAT1290_MM_TO_FL_22_4	13
+#define AAT1290_MM_TO_FL_24	14
+#define AAT1290_MM_TO_FL_25_6	15
+#define AAT1290_MM_TO_FL_OFF	16
+
+static const unsigned int flash_level_to_reg[] = {
+	16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
+};
+
+enum aat1290_led_mode {
+	MODE_OFF,
+	MODE_TORCH,
+	MODE_FLASH,
+	MODE_FLASH_EXTERNAL,
+};
+
+struct aat1290_led_settings {
+	struct led_flash_setting torch_brightness;
+	struct led_flash_setting flash_brightness;
+	struct led_flash_setting flash_timeout;
+};
+
+struct aat1290_led {
+	struct platform_device *pdev;
+	struct mutex lock;
+
+	struct led_classdev_flash ldev;
+	struct v4l2_flash *v4l2_flash;
+
+	int flen_gpio;
+	int en_set_gpio;
+
+	u32 max_flash_tm;
+	bool movie_mode;
+
+	unsigned int torch_brightness;
+	unsigned int flash_timeout;
+	struct work_struct work_brightness_set;
+};
+
+static struct aat1290_led *ldev_to_led(struct led_classdev_flash *ldev)
+{
+	return container_of(ldev, struct aat1290_led, ldev);
+}
+
+static void aat1290_as2cwire_write(struct aat1290_led *led, int addr, int value)
+{
+	int i;
+
+	gpio_set_value(led->flen_gpio, 0);
+	gpio_set_value(led->en_set_gpio, 0);
+
+	udelay(10);
+
+	/* write address */
+	for (i = 0; i < addr; ++i) {
+		udelay(AAT1290_EN_SET_TICK_TIME_US);
+		gpio_set_value(led->en_set_gpio, 0);
+		udelay(AAT1290_EN_SET_TICK_TIME_US);
+		gpio_set_value(led->en_set_gpio, 1);
+	}
+
+	udelay(AAT1290_LATCH_TIME_US);
+
+	/* write data */
+	for (i = 0; i < value; ++i) {
+		udelay(AAT1290_EN_SET_TICK_TIME_US);
+		gpio_set_value(led->en_set_gpio, 0);
+		udelay(AAT1290_EN_SET_TICK_TIME_US);
+		gpio_set_value(led->en_set_gpio, 1);
+	}
+
+	udelay(AAT1290_LATCH_TIME_US);
+}
+
+static void aat1290_set_flash_safety_timer(struct aat1290_led *led,
+					unsigned int micro_sec)
+{
+	struct led_classdev_flash *flash = &led->ldev;
+	struct led_flash_setting *flash_tm = &flash->timeout;
+	int flash_tm_divisor_reg =
+		flash_level_to_reg[(micro_sec / flash_tm->step) - 1];
+
+	aat1290_as2cwire_write(led, AAT1290_FLASH_SAFETY_TIMER_ADDR,
+						flash_tm_divisor_reg);
+}
+
+static void aat1290_brightness_set(struct aat1290_led *led,
+					enum led_brightness brightness)
+{
+	mutex_lock(&led->lock);
+
+	if (brightness == 0) {
+		gpio_set_value(led->flen_gpio, 0);
+		gpio_set_value(led->en_set_gpio, 0);
+		goto unlock;
+	}
+
+	if (!led->movie_mode) {
+		aat1290_as2cwire_write(led, AAT1290_MM_CURRENT_RATIO_ADDR,
+					AAT1290_MM_TO_FL_1_92);
+		led->movie_mode = true;
+	}
+
+	aat1290_as2cwire_write(led, AAT1290_MOVIE_MODE_CURRENT_ADDR,
+				AAT1290_MAX_MM_CURR_PERCENT_0 - brightness);
+	aat1290_as2cwire_write(led, AAT1290_MOVIE_MODE_CONFIG_ADDR,
+				AAT1290_MOVIE_MODE_ON);
+unlock:
+	mutex_unlock(&led->lock);
+}
+
+/* LED subsystem callbacks */
+
+static void aat1290_brightness_set_work(struct work_struct *work)
+{
+	struct aat1290_led *led =
+		container_of(work, struct aat1290_led, work_brightness_set);
+
+	aat1290_brightness_set(led, led->torch_brightness);
+}
+
+static void aat1290_led_brightness_set(struct led_classdev *led_cdev,
+					enum led_brightness brightness)
+{
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+	struct aat1290_led *led = ldev_to_led(flash);
+
+	led->torch_brightness = brightness;
+	schedule_work(&led->work_brightness_set);
+}
+
+static int aat1290_torch_brightness_set(struct led_classdev *led_cdev,
+					enum led_brightness brightness)
+{
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);
+	struct aat1290_led *led = ldev_to_led(flash);
+
+	aat1290_brightness_set(led, brightness);
+
+	return 0;
+}
+
+static int aat1290_led_flash_strobe_set(struct led_classdev_flash *flash,
+					 bool state)
+
+{
+	struct aat1290_led *led = ldev_to_led(flash);
+	struct led_classdev *led_cdev = &flash->led_cdev;
+	struct led_flash_setting *timeout = &flash->timeout;
+
+	mutex_lock(&led->lock);
+
+	if (state == 0) {
+		gpio_set_value(led->flen_gpio, 0);
+		gpio_set_value(led->en_set_gpio, 0);
+		goto unlock;
+	}
+
+	aat1290_set_flash_safety_timer(led, timeout->val);
+
+	/*
+	 * To reenter movie mode after a flash event the part
+	 * must be cycled off and back on to reset the movie
+	 * mode and reprogrammed via the AS2Cwire. Therefore
+	 * the brightness value needs to be updated here to
+	 * reflect the actual state.
+	 */
+	led_cdev->brightness = 0;
+	led->movie_mode = false;
+
+	gpio_set_value(led->flen_gpio, 1);
+
+unlock:
+	mutex_unlock(&led->lock);
+
+	return 0;
+}
+
+static int aat1290_led_flash_timeout_set(struct led_classdev_flash *flash,
+						u32 timeout)
+{
+	/*
+	 * Don't do anything - flash timeout is cached in the led-class-flash
+	 * core and will be applied in the strobe_set op, as writing the
+	 * safety timer register spuriously turns the torch mode on.
+	 */
+
+	return 0;
+}
+
+static int aat1290_led_parse_dt(struct aat1290_led *led,
+				struct device *dev)
+{
+	int ret;
+	char *pname = "flash-timeout";
+
+	ret = of_property_read_u32(dev->of_node, pname, &led->max_flash_tm);
+	if (ret) {
+		dev_err(dev, "Unable to get property '%s'(%d)\n", pname, ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static void aat1290_init_flash_settings(struct aat1290_led *led,
+					 struct aat1290_led_settings *s)
+{
+	struct led_flash_setting *setting;
+
+	/* Init flash intensity setting */
+	setting = &s->torch_brightness;
+	/*
+	 * Torch current is adjustable in logarithmic fashion and thus
+	 * it is not possible to define fixed step in microamperes.
+	 * Instead led brightness levels are used to make possible
+	 * setting all the supported levels from V4L2 Flash sub-device.
+	 */
+	setting->min = 1;
+	setting->max = 16;
+	setting->step = 1;
+	setting->val = setting->max;
+
+	/* Init flash timeout setting */
+	setting = &s->flash_timeout;
+	setting->min = led->max_flash_tm / AAT1290_FLASH_TM_RATIO_STEP;
+	setting->max = setting->min * AAT1290_FLASH_TM_RATIO_STEP;
+	setting->step = setting->min;
+	setting->val = setting->max;
+}
+
+#ifdef CONFIG_V4L2_FLASH_LED_CLASS
+static void aat1290_init_v4l2_ctrl_config(struct aat1290_led_settings *s,
+					struct v4l2_flash_ctrl_config *config)
+{
+	struct led_flash_setting *setting;
+	struct v4l2_ctrl_config *c;
+
+	c = &config->torch_intensity;
+	setting = &s->torch_brightness;
+	c->min = setting->min;
+	c->max = setting->max;
+	c->step = setting->step;
+	c->def = setting->val;
+
+	c = &config->flash_timeout;
+	setting = &s->flash_timeout;
+	c->min = setting->min;
+	c->max = setting->max;
+	c->step = setting->step;
+	c->def = setting->val;
+}
+#else
+#define aat1290_init_v4l2_ctrl_config(s, config)
+#endif
+
+const struct led_flash_ops flash_ops = {
+	.strobe_set = aat1290_led_flash_strobe_set,
+	.timeout_set = aat1290_led_flash_timeout_set,
+};
+
+static int aat1290_led_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *dev_node = pdev->dev.of_node;
+	struct aat1290_led *led;
+	struct led_classdev *led_cdev;
+	struct led_classdev_flash *flash;
+	struct v4l2_flash_ctrl_config v4l2_flash_config;
+	struct aat1290_led_settings settings;
+	int flen_gpio, enset_gpio, ret;
+
+	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	led->pdev = pdev;
+	platform_set_drvdata(pdev, led);
+
+	if (!dev_node)
+		return -ENXIO;
+
+	flen_gpio = of_get_gpio(dev_node, 0);
+	if (gpio_is_valid(flen_gpio)) {
+		ret = gpio_request_one(flen_gpio, GPIOF_DIR_OUT,
+						"aat1290_flen");
+		if (ret < 0) {
+			dev_err(dev,
+				"failed to request GPIO %d, error %d\n",
+							flen_gpio, ret);
+			goto error_gpio_flen;
+		}
+	}
+	led->flen_gpio = flen_gpio;
+
+	enset_gpio = of_get_gpio(dev_node, 1);
+	if (gpio_is_valid(enset_gpio)) {
+		ret = gpio_request_one(enset_gpio, GPIOF_DIR_OUT,
+						"aat1290_en_set");
+		if (ret < 0) {
+			dev_err(dev,
+				"failed to request GPIO %d, error %d\n",
+							enset_gpio, ret);
+			goto error_gpio_en_set;
+		}
+	}
+	led->en_set_gpio = enset_gpio;
+
+	ret = aat1290_led_parse_dt(led, &pdev->dev);
+	if (ret < 0)
+		goto error_gpio_en_set;
+
+	mutex_init(&led->lock);
+
+	flash = &led->ldev;
+
+	/* Init flash settings */
+	aat1290_init_flash_settings(led, &settings);
+	/* Init V4L2 Flash controls basing on initialized settings */
+	aat1290_init_v4l2_ctrl_config(&settings, &v4l2_flash_config);
+
+	/* Init led class */
+	led_cdev = &flash->led_cdev;
+	led_cdev->name = AAT1290_LED_NAME;
+	led_cdev->brightness_set = aat1290_led_brightness_set;
+	led_cdev->torch_brightness_set = aat1290_torch_brightness_set;
+	led_cdev->max_brightness = AAT1290_MAX_MM_CURR_PERCENT_0 -
+				   AAT1290_MAX_MM_CURR_PERCENT_100;
+	led_cdev->flags |= LED_DEV_CAP_FLASH;
+
+	INIT_WORK(&led->work_brightness_set, aat1290_brightness_set_work);
+
+	flash->ops = &flash_ops;
+
+	flash->timeout = settings.flash_timeout;
+
+	/* Register in the LED subsystem. */
+	ret = led_classdev_flash_register(&pdev->dev, flash, dev->of_node);
+	if (ret < 0)
+		goto error_gpio_en_set;
+
+	/* Create V4L2 Flash subdev. */
+	ret = v4l2_flash_init(flash,
+			      &v4l2_flash_config,
+			      led_get_v4l2_flash_ops(),
+			      &led->v4l2_flash);
+	if (ret < 0)
+		goto error_v4l2_flash_init;
+
+	return ret;
+
+error_v4l2_flash_init:
+	led_classdev_flash_unregister(flash);
+error_gpio_en_set:
+	if (gpio_is_valid(enset_gpio))
+		gpio_free(enset_gpio);
+error_gpio_flen:
+	if (gpio_is_valid(flen_gpio))
+		gpio_free(flen_gpio);
+	mutex_destroy(&led->lock);
+
+	return ret;
+}
+
+static int aat1290_led_remove(struct platform_device *pdev)
+{
+	struct aat1290_led *led = platform_get_drvdata(pdev);
+
+	v4l2_flash_release(led->v4l2_flash);
+	led_classdev_flash_unregister(&led->ldev);
+	cancel_work_sync(&led->work_brightness_set);
+
+	if (gpio_is_valid(led->en_set_gpio))
+		gpio_free(led->en_set_gpio);
+	if (gpio_is_valid(led->flen_gpio))
+		gpio_free(led->flen_gpio);
+
+	mutex_destroy(&led->lock);
+
+	return 0;
+}
+
+static struct of_device_id aat1290_led_dt_match[] = {
+	{.compatible = "skyworks,aat1290"},
+	{},
+};
+
+static struct platform_driver aat1290_led_driver = {
+	.probe		= aat1290_led_probe,
+	.remove		= aat1290_led_remove,
+	.driver		= {
+		.name	= "aat1290-led",
+		.owner	= THIS_MODULE,
+		.of_match_table = aat1290_led_dt_match,
+	},
+};
+
+module_platform_driver(aat1290_led_driver);
+
+MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
+MODULE_DESCRIPTION("Skyworks Current Regulator for Flash LEDs");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5


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

* [PATCH/RFC v4 19/21] of: Add Skyworks Solutions, Inc. vendor prefix
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
                   ` (17 preceding siblings ...)
  2014-07-11 14:04 ` [PATCH/RFC v4 18/21] leds: Add driver for AAT1290 current regulator Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-11 14:04 ` [PATCH/RFC v4 20/21] DT: Add documentation for the Skyworks AAT1290 Jacek Anaszewski
                   ` (3 subsequent siblings)
  22 siblings, 0 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

Use "skyworks" as the vendor prefix for the
Skyworks Solutions, Inc.

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/vendor-prefixes.txt        |    1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 5d27e5a..8d0df4e 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -113,6 +113,7 @@ renesas	Renesas Electronics Corporation
 ricoh	Ricoh Co. Ltd.
 rockchip	Fuzhou Rockchip Electronics Co., Ltd
 samsung	Samsung Semiconductor
+skyworks	Skyworks Solutions, Inc.
 sbs	Smart Battery System
 schindler	Schindler
 seagate	Seagate Technology PLC
-- 
1.7.9.5


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

* [PATCH/RFC v4 20/21] DT: Add documentation for the Skyworks AAT1290
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
                   ` (18 preceding siblings ...)
  2014-07-11 14:04 ` [PATCH/RFC v4 19/21] of: Add Skyworks Solutions, Inc. vendor prefix Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-11 14:04 ` [PATCH/RFC v4 21/21] ARM: dts: add aat1290 current regulator device node Jacek Anaszewski
                   ` (2 subsequent siblings)
  22 siblings, 0 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

This patch adds device tree binding documentation for
1.5A Step-Up Current Regulator for Flash LEDs.

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/leds/leds-aat1290.txt      |   17 +++++++++++++++++
 1 file changed, 17 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-aat1290.txt

diff --git a/Documentation/devicetree/bindings/leds/leds-aat1290.txt b/Documentation/devicetree/bindings/leds/leds-aat1290.txt
new file mode 100644
index 0000000..9a9ad15
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-aat1290.txt
@@ -0,0 +1,17 @@
+* Skyworks Solutions, Inc. AAT1290 Current Regulator for Flash LEDs
+
+Required properties:
+
+- compatible : should be "skyworks,aat1290"
+- gpios : two gpio pins in order FLEN, EN/SET
+- skyworks,flash-timeout : maximum flash timeout in microseconds -
+			   it can be calculated using following formula:
+			   T = 8.82 * 10^9 * Ct
+
+Example:
+
+flash_led: flash-led {
+	compatible = "skyworks,aat1290";
+	gpios = <&gpj1 1 0>, <&gpj1 2 0>;
+	flash-timeout = <1940000>;
+}
-- 
1.7.9.5


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

* [PATCH/RFC v4 21/21] ARM: dts: add aat1290 current regulator device node
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
                   ` (19 preceding siblings ...)
  2014-07-11 14:04 ` [PATCH/RFC v4 20/21] DT: Add documentation for the Skyworks AAT1290 Jacek Anaszewski
@ 2014-07-11 14:04 ` Jacek Anaszewski
  2014-07-16 17:19 ` [PATCH/RFC v4 00/21] LED / flash API integration Bryan Wu
  2014-08-06  6:53 ` Sakari Ailus
  22 siblings, 0 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-07-11 14:04 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Kukjin Kim

Add device node for AAT1290 1.5A Step-Up Current Regulator
for Flash LEDs along with flash_muxes node containing
information about a multiplexer that is used for switching
between software and external strobe signal source.

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Kukjin Kim <kgene.kim@samsung.com>
---
 arch/arm/boot/dts/exynos4412-trats2.dts |   24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/arch/arm/boot/dts/exynos4412-trats2.dts b/arch/arm/boot/dts/exynos4412-trats2.dts
index 7787844..cbb76ba 100644
--- a/arch/arm/boot/dts/exynos4412-trats2.dts
+++ b/arch/arm/boot/dts/exynos4412-trats2.dts
@@ -785,4 +785,28 @@
 		pulldown-ohm = <100000>; /* 100K */
 		io-channels = <&adc 2>;  /* Battery temperature */
 	};
+
+	flash_muxes {
+		flash_mux1: mux1 {
+			gpios = <&gpj1 0 0>;
+		};
+	};
+
+	aat1290 {
+		compatible = "skyworks,aat1290";
+		gpios = <&gpj1 1 0>, <&gpj1 2 0>;
+		flash-timeout = <1940000>;
+		status = "okay";
+
+		gate-software-strobe {
+			mux = <&flash_mux1>;
+			mux-line-id = <0>;
+		};
+
+		gate-external-strobe {
+			strobe-provider = <&s5c73m3_spi>;
+			mux = <&flash_mux1>;
+			mux-line-id = <1>;
+		};
+	};
 };
-- 
1.7.9.5


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

* Re: [PATCH/RFC v4 02/21] leds: implement sysfs interface locking mechanism
  2014-07-11 14:04 ` [PATCH/RFC v4 02/21] leds: implement sysfs interface locking mechanism Jacek Anaszewski
@ 2014-07-16 15:35   ` Sakari Ailus
  0 siblings, 0 replies; 55+ messages in thread
From: Sakari Ailus @ 2014-07-16 15:35 UTC (permalink / raw)
  To: Jacek Anaszewski, linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Bryan Wu, Richard Purdie

Hi Jacek,

Thank you for the update!

Jacek Anaszewski wrote:
...
> diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
> index 71b40d3..4d7cb31 100644
> --- a/drivers/leds/led-core.c
> +++ b/drivers/leds/led-core.c
> @@ -126,3 +126,21 @@ void led_set_brightness(struct led_classdev *led_cdev,
>   	__led_set_brightness(led_cdev, brightness);
>   }
>   EXPORT_SYMBOL(led_set_brightness);
> +
> +/* Caller must ensure led_cdev->led_lock held */
> +void led_sysfs_lock(struct led_classdev *led_cdev)
> +{
> +	WARN_ON(!mutex_is_locked(&led_cdev->led_lock));

How about lockdep_assert_held() instead?

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi     XMPP: sailus@retiisi.org.uk

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

* Re: [PATCH/RFC v4 04/21] leds: Reorder include directives
  2014-07-11 14:04 ` [PATCH/RFC v4 04/21] leds: Reorder include directives Jacek Anaszewski
@ 2014-07-16 15:42   ` Sakari Ailus
  0 siblings, 0 replies; 55+ messages in thread
From: Sakari Ailus @ 2014-07-16 15:42 UTC (permalink / raw)
  To: Jacek Anaszewski, linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Bryan Wu, Richard Purdie

Hi Jacek,

Jacek Anaszewski wrote:
> Reorder include directives so that they are arranged
> in alphabetical order.
>
> 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 |   13 +++++++------
>   drivers/leds/led-core.c  |    3 ++-
>   include/linux/leds.h     |    2 +-
>   3 files changed, 10 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
> index da79bbb..0127783 100644
> --- a/drivers/leds/led-class.c
> +++ b/drivers/leds/led-class.c
> @@ -9,16 +9,17 @@
>    * 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/leds.h>
>   #include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>

Do you also need 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 "leds.h"
>
>   static struct class *leds_class;
> diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
> index 0ac06ed..d156fb6 100644
> --- a/drivers/leds/led-core.c
> +++ b/drivers/leds/led-core.c
> @@ -12,10 +12,11 @@
>    */
>
>   #include <linux/kernel.h>
> +#include <linux/leds.h>
>   #include <linux/list.h>
>   #include <linux/module.h>
> +#include <linux/mutex.h>

And mutex.h here?

With that fixed, if there are some other patches that are essentially 
cleanups that could go in well before the rest of the set. Some of the 
patches will take a little longer, I presume.

I'll let you know if/when there's an update regarding the compound 
controls patchset.

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi     XMPP: sailus@retiisi.org.uk

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

* Re: [PATCH/RFC v4 05/21] leds: avoid using deprecated DEVICE_ATTR macro
  2014-07-11 14:04 ` [PATCH/RFC v4 05/21] leds: avoid using deprecated DEVICE_ATTR macro Jacek Anaszewski
@ 2014-07-16 15:46   ` Sakari Ailus
  0 siblings, 0 replies; 55+ messages in thread
From: Sakari Ailus @ 2014-07-16 15:46 UTC (permalink / raw)
  To: Jacek Anaszewski, linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Bryan Wu, Richard Purdie

Hi Jacek,

Jacek Anaszewski wrote:
> Make the sysfs attributes definition consistent in the whole file.
> The modification entails change of the function name:
> led_max_brightness_show -> max_brightness_show

I'm not sure whether DEVICE_ATTR() is really deprecated but nevertheless 
this is cleaner.

Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi     XMPP: sailus@retiisi.org.uk

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

* Re: [PATCH/RFC v4 00/21] LED / flash API integration
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
                   ` (20 preceding siblings ...)
  2014-07-11 14:04 ` [PATCH/RFC v4 21/21] ARM: dts: add aat1290 current regulator device node Jacek Anaszewski
@ 2014-07-16 17:19 ` Bryan Wu
  2014-07-16 17:21   ` Bryan Wu
  2014-08-06  6:53 ` Sakari Ailus
  22 siblings, 1 reply; 55+ messages in thread
From: Bryan Wu @ 2014-07-16 17:19 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: Linux LED Subsystem, devicetree, linux-media, lkml,
	Kyungmin Park, b.zolnierkie

On Fri, Jul 11, 2014 at 7:04 AM, Jacek Anaszewski
<j.anaszewski@samsung.com> wrote:
> This is is the fourth 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-20140707
>

I really apologize that I missed your discussion email in my Gmail
inbox, it was archived some where. Even in this series some of these
patches are archived in different tab.

I will start to review and help to push this.

Thanks,
-Bryan


> 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, if the driver
>           declares related features:
>             - flash_brightness
>             - max_flash_brightness
>             - indicator_brightness
>             - max_indicator_brightness
>             - flash_timeout
>             - max_flash_timeout
>             - flash_strobe
>             - external_strobe
>             - flash_fault
>           and exposes kernel internal API:
>             - led_classdev_flash_register
>             - led_classdev_flash_unregister
>             - led_set_flash_strobe
>             - led_get_flash_strobe
>             - led_set_flash_brightness
>             - led_update_flash_brightness
>             - led_set_indicator_brightness
>             - led_update_indicator_brightness
>             - led_set_flash_timeout
>             - led_get_flash_fault
>             - led_set_external_strobe
>             - led_sysfs_lock
>             - led_sysfs_unlock
>         * added Flash Manager functionality, available when
>           led_flash module is enable in the config;
>           if the device tree node of a flash led device contains
>           relevant subnodes, it registers following sysfs attributes:
>             - strobe_provider
>             - strobe_providerN
>             - blocking_strobe
>           following kernel internal API is exposed by the flash manager:
>             - led_flash_manager_register_flash
>             - led_flash_manager_unregister_flash
>             - led_flash_manager_setup_strobe
>             - led_flash_manager_bind_async_mux
>             - led_flash_manager_unbind_async_mux
>     - 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
>     - Addition of a LED Flash Class driver for the flash cell of
>       the MAX77693 mfd
>     - Addition of a LED Flash Class driver for the AAT1290 current
>       regulator for flash leds along with its DT binding for the
>       exynos4412-trats2 board, where standalone multiplexer is
>       used for modifying strobe signal routing - either from the SoC
>       GPIO or from a camera sensor. This arrangment is handled
>       by the newly introduced Flash Manager functionality.
>     - Update of the max77693.txt DT bindings documentation
>     - Update of the common leds DT bindings documentation
>
> ================
> Changes since v2
> ================
>
>     - refactored the code so that it is possible to build
>       led-core without led-flash module
>     - added v4l2-flash ops which slackens dependency from
>       the led-flash module
>     - implemented led_clamp_align_val function and led_ctrl
>       structure which allows to align led control values
>       in the manner compatible with V4L2 Flash controls;
>       the flash brightness and timeout units have been defined
>       as microamperes and microseconds respectively to properly
>       support devices which define current and time levels
>       as fractions of 1/1000.
>     - added support for the flash privacy leds
>     - modified LED sysfs locking mechanism - now it locks/unlocks
>       the interface on V4L2 Flash sub-device file open/close
>     - changed hw_triggered attribute name to external_strobe,
>       which maps on the V4L2_FLASH_STROBE_SOURCE_EXTERNAL name
>       more intuitively
>     - made external_strobe and indicator related sysfs attributes
>       created optionally only if related features are declared
>       by the led device driver
>     - removed from the series patches modifying exynos4-is media
>       controller - a proposal for "flash manager" which will take
>       care of flash devices registration is due to be submitted
>     - removed modifications to the LED class devices documentation,
>       it will be covered after the whole functionality is accepted
>
> ================
> Changes since v3
> ================
>
>     - added Flash Manager feature
>       - added generic LED Flash Class gpio mux driver
>       - added sample async mux driver
>       - added of helpers for parsing Flash Manager related
>         device tree data
>     - added V4L2_CID_FLASH_STROBE_PROVIDER control
>     - introduced struct led_classdev_flash, which wrapps
>       struct led_classdev
>     - made all flash ops, except strobe_set, optional; if an op
>       is absent the related sysfs attribute isn't created
>     - added LED_DEV_CAP* flags
>     - modified v4l2-flash helpers to create v4l2_device
>       for v4l2_flash subdevices to register in it
>     - modified max77693-flash driver and its DT bindings
>       to allow for registering either one or two LED Flash
>       Class devices, depending on the device tree settings.
>     - added new API for setting torch_brightness
>     - extended leds common DT binding documentation
>
> Issues:
>
> 1) Who should register V4L2 Flash sub-device?
>
> LED Flash Class devices, after introduction of the Flash Manager,
> are not tightly coupled with any media controller. They are maintained
> by the Flash Manager and made available for dynamic assignment to
> any media system they are connected to through multiplexing devices.
>
> In the proposed rough solution, when support for V4L2 Flash sub-devices
> is enabled, there is a v4l2_device created for them to register in.
> This however implies that V4L2 Flash device will not be available
> in any media controller, which calls its existence into question.
>
> Therefore I'd like to consult possible ways of solving this issue.
> The option I see is implementing a mechanism for moving V4L2 Flash
> sub-devices between media controllers. A V4L2 Flash sub-device
> would initially be assigned to one media system in the relevant
> device tree binding, but it could be dynamically reassigned to
> the other one. However I'm not sure if media controller design
> is prepared for dynamic modifications of its graph and how many
> modifications in the existing drivers this solution would require.
>
> 2) Consequences of locking the Flash Manager during flash strobe
>
> In case a LED Flash Class device depends on muxes involved in
> routing the other LED Flash Class device's strobe signals,
> the Flash Manager must be locked for the time of strobing
> to prevent reconfiguration of the strobe signal routing
> by the other device.
>
> A blocking_strobe sysfs attribute was added to indicate whether
> this is the case for the device.
>
> Nonetheless, this modifies behaviour of led_set_external_strobe
> API, which also must block the caller for the time of strobing
> to protect the strobe signal routing while the external strobe
> signal provider asserts the flash_en pin.
>
> Use case for user space application would be following in this case:
>   - spawn a new thread in which external strobe is activated
>   - in the parent thread instruct the external strobe provider
>     to strobe the flash
>
> As an improvement there could be an op added for notifying
> the application that the strobe signal routing has been
> set up. It would have to be called from the function
> led_flash_manager_setup_strobe after led_flash_manager_set_external_strobe
> returns.
>
> The blocking_strobe attribute would have to be also
> mapped to a new V4L2 Flash control. Unfortunately it poses
> a problem for the existing users of the V4L2 Flash API
> which are not aware that setting V4L2_CID_FLASH_STROBE_SOURCE
> may be blocking.
>
> As a solution led_set_external_strobe API could be extended
> by a parameter telling whether the caller is prepared for
> the blocking call. led_flash_manager_setup_strobe should
> be extended accordingly then.
> In V4L2 "V4L2_FLASH_STROBE_SOURCE_EXTERNAL_BLOCKING" menu item
> could be added to handle this.
> With existing V4L2_FLASH_STROBE_SOURCE_EXTERNAL the flash manager
> wouldn't protect muxes against reconfiguration.
>
> TODO:
>   - switch to using V4L2 array controls
>   - add s_power op to the LED Flash Class
>
>
> I will be off-line for three weeks from now on and will respond
> to any questions after getting back.
>
>
> Thanks,
> Jacek Anaszewski
>
> Jacek Anaszewski (21):
>   leds: make brightness type consistent across whole subsystem
>   leds: implement sysfs interface locking mechanism
>   leds: Improve and export led_update_brightness
>   leds: Reorder include directives
>   leds: avoid using deprecated DEVICE_ATTR macro
>   leds: add API for setting torch brightness
>   of: add of_node_ncmp wrapper
>   leds: Add sysfs and kernel internal API for flash LEDs
>   Documentation: leds: Add description of LED Flash Class extension
>   Documentation: leds: add exemplary asynchronous mux driver
>   DT: leds: Add flash led devices related properties
>   DT: Add documentation for LED Class Flash Manger
>   v4l2-device: add v4l2_device_register_subdev_node API
>   v4l2-ctrls: add control for flash strobe signal providers
>   media: Add registration helpers for V4L2 flash
>   leds: Add support for max77693 mfd flash cell
>   DT: Add documentation for the mfd Maxim max77693
>   leds: Add driver for AAT1290 current regulator
>   of: Add Skyworks Solutions, Inc. vendor prefix
>   DT: Add documentation for the Skyworks AAT1290
>   ARM: dts: add aat1290 current regulator device node
>
>  Documentation/DocBook/media/v4l/controls.xml       |   11 +
>  Documentation/devicetree/bindings/leds/common.txt  |   16 +
>  .../devicetree/bindings/leds/leds-aat1290.txt      |   17 +
>  .../bindings/leds/leds-flash-manager.txt           |  165 +++
>  Documentation/devicetree/bindings/mfd/max77693.txt |   62 ++
>  .../devicetree/bindings/vendor-prefixes.txt        |    1 +
>  Documentation/leds/leds-async-mux.c                |   65 ++
>  Documentation/leds/leds-class-flash.txt            |  126 +++
>  arch/arm/boot/dts/exynos4412-trats2.dts            |   24 +
>  drivers/leds/Kconfig                               |   28 +
>  drivers/leds/Makefile                              |    7 +
>  drivers/leds/led-class-flash.c                     |  715 +++++++++++++
>  drivers/leds/led-class.c                           |   58 +-
>  drivers/leds/led-core.c                            |   51 +-
>  drivers/leds/led-flash-gpio-mux.c                  |  102 ++
>  drivers/leds/led-flash-manager.c                   |  698 +++++++++++++
>  drivers/leds/led-triggers.c                        |   16 +-
>  drivers/leds/leds-aat1290.c                        |  455 +++++++++
>  drivers/leds/leds-max77693.c                       | 1070 ++++++++++++++++++++
>  drivers/leds/of_led_flash_manager.c                |  155 +++
>  drivers/media/v4l2-core/Kconfig                    |   11 +
>  drivers/media/v4l2-core/Makefile                   |    2 +
>  drivers/media/v4l2-core/v4l2-ctrls.c               |    2 +
>  drivers/media/v4l2-core/v4l2-device.c              |   63 +-
>  drivers/media/v4l2-core/v4l2-flash.c               |  580 +++++++++++
>  drivers/mfd/max77693.c                             |    5 +-
>  include/linux/led-class-flash.h                    |  290 ++++++
>  include/linux/led-flash-gpio-mux.h                 |   68 ++
>  include/linux/led-flash-manager.h                  |  121 +++
>  include/linux/leds.h                               |   73 +-
>  include/linux/mfd/max77693.h                       |   40 +
>  include/linux/of.h                                 |    1 +
>  include/linux/of_led_flash_manager.h               |   80 ++
>  include/media/v4l2-device.h                        |    7 +
>  include/media/v4l2-flash.h                         |  137 +++
>  include/uapi/linux/v4l2-controls.h                 |    2 +
>  36 files changed, 5272 insertions(+), 52 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/leds/leds-aat1290.txt
>  create mode 100644 Documentation/devicetree/bindings/leds/leds-flash-manager.txt
>  create mode 100644 Documentation/leds/leds-async-mux.c
>  create mode 100644 Documentation/leds/leds-class-flash.txt
>  create mode 100644 drivers/leds/led-class-flash.c
>  create mode 100644 drivers/leds/led-flash-gpio-mux.c
>  create mode 100644 drivers/leds/led-flash-manager.c
>  create mode 100644 drivers/leds/leds-aat1290.c
>  create mode 100644 drivers/leds/leds-max77693.c
>  create mode 100644 drivers/leds/of_led_flash_manager.c
>  create mode 100644 drivers/media/v4l2-core/v4l2-flash.c
>  create mode 100644 include/linux/led-class-flash.h
>  create mode 100644 include/linux/led-flash-gpio-mux.h
>  create mode 100644 include/linux/led-flash-manager.h
>  create mode 100644 include/linux/of_led_flash_manager.h
>  create mode 100644 include/media/v4l2-flash.h
>
> --
> 1.7.9.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH/RFC v4 00/21] LED / flash API integration
  2014-07-16 17:19 ` [PATCH/RFC v4 00/21] LED / flash API integration Bryan Wu
@ 2014-07-16 17:21   ` Bryan Wu
  2014-08-08  6:43     ` Jacek Anaszewski
  0 siblings, 1 reply; 55+ messages in thread
From: Bryan Wu @ 2014-07-16 17:21 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: Linux LED Subsystem, devicetree, linux-media, lkml,
	Kyungmin Park, b.zolnierkie

On Wed, Jul 16, 2014 at 10:19 AM, Bryan Wu <cooloney@gmail.com> wrote:
> On Fri, Jul 11, 2014 at 7:04 AM, Jacek Anaszewski
> <j.anaszewski@samsung.com> wrote:
>> This is is the fourth 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-20140707
>>
>
> I really apologize that I missed your discussion email in my Gmail
> inbox, it was archived some where. Even in this series some of these
> patches are archived in different tab.
>
> I will start to review and help to push this.
>

In the mean time, could you please provide an git tree for me to pull?
It's much easier for me to review in my git.

-Bryan

>
>
>> 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, if the driver
>>           declares related features:
>>             - flash_brightness
>>             - max_flash_brightness
>>             - indicator_brightness
>>             - max_indicator_brightness
>>             - flash_timeout
>>             - max_flash_timeout
>>             - flash_strobe
>>             - external_strobe
>>             - flash_fault
>>           and exposes kernel internal API:
>>             - led_classdev_flash_register
>>             - led_classdev_flash_unregister
>>             - led_set_flash_strobe
>>             - led_get_flash_strobe
>>             - led_set_flash_brightness
>>             - led_update_flash_brightness
>>             - led_set_indicator_brightness
>>             - led_update_indicator_brightness
>>             - led_set_flash_timeout
>>             - led_get_flash_fault
>>             - led_set_external_strobe
>>             - led_sysfs_lock
>>             - led_sysfs_unlock
>>         * added Flash Manager functionality, available when
>>           led_flash module is enable in the config;
>>           if the device tree node of a flash led device contains
>>           relevant subnodes, it registers following sysfs attributes:
>>             - strobe_provider
>>             - strobe_providerN
>>             - blocking_strobe
>>           following kernel internal API is exposed by the flash manager:
>>             - led_flash_manager_register_flash
>>             - led_flash_manager_unregister_flash
>>             - led_flash_manager_setup_strobe
>>             - led_flash_manager_bind_async_mux
>>             - led_flash_manager_unbind_async_mux
>>     - 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
>>     - Addition of a LED Flash Class driver for the flash cell of
>>       the MAX77693 mfd
>>     - Addition of a LED Flash Class driver for the AAT1290 current
>>       regulator for flash leds along with its DT binding for the
>>       exynos4412-trats2 board, where standalone multiplexer is
>>       used for modifying strobe signal routing - either from the SoC
>>       GPIO or from a camera sensor. This arrangment is handled
>>       by the newly introduced Flash Manager functionality.
>>     - Update of the max77693.txt DT bindings documentation
>>     - Update of the common leds DT bindings documentation
>>
>> ================
>> Changes since v2
>> ================
>>
>>     - refactored the code so that it is possible to build
>>       led-core without led-flash module
>>     - added v4l2-flash ops which slackens dependency from
>>       the led-flash module
>>     - implemented led_clamp_align_val function and led_ctrl
>>       structure which allows to align led control values
>>       in the manner compatible with V4L2 Flash controls;
>>       the flash brightness and timeout units have been defined
>>       as microamperes and microseconds respectively to properly
>>       support devices which define current and time levels
>>       as fractions of 1/1000.
>>     - added support for the flash privacy leds
>>     - modified LED sysfs locking mechanism - now it locks/unlocks
>>       the interface on V4L2 Flash sub-device file open/close
>>     - changed hw_triggered attribute name to external_strobe,
>>       which maps on the V4L2_FLASH_STROBE_SOURCE_EXTERNAL name
>>       more intuitively
>>     - made external_strobe and indicator related sysfs attributes
>>       created optionally only if related features are declared
>>       by the led device driver
>>     - removed from the series patches modifying exynos4-is media
>>       controller - a proposal for "flash manager" which will take
>>       care of flash devices registration is due to be submitted
>>     - removed modifications to the LED class devices documentation,
>>       it will be covered after the whole functionality is accepted
>>
>> ================
>> Changes since v3
>> ================
>>
>>     - added Flash Manager feature
>>       - added generic LED Flash Class gpio mux driver
>>       - added sample async mux driver
>>       - added of helpers for parsing Flash Manager related
>>         device tree data
>>     - added V4L2_CID_FLASH_STROBE_PROVIDER control
>>     - introduced struct led_classdev_flash, which wrapps
>>       struct led_classdev
>>     - made all flash ops, except strobe_set, optional; if an op
>>       is absent the related sysfs attribute isn't created
>>     - added LED_DEV_CAP* flags
>>     - modified v4l2-flash helpers to create v4l2_device
>>       for v4l2_flash subdevices to register in it
>>     - modified max77693-flash driver and its DT bindings
>>       to allow for registering either one or two LED Flash
>>       Class devices, depending on the device tree settings.
>>     - added new API for setting torch_brightness
>>     - extended leds common DT binding documentation
>>
>> Issues:
>>
>> 1) Who should register V4L2 Flash sub-device?
>>
>> LED Flash Class devices, after introduction of the Flash Manager,
>> are not tightly coupled with any media controller. They are maintained
>> by the Flash Manager and made available for dynamic assignment to
>> any media system they are connected to through multiplexing devices.
>>
>> In the proposed rough solution, when support for V4L2 Flash sub-devices
>> is enabled, there is a v4l2_device created for them to register in.
>> This however implies that V4L2 Flash device will not be available
>> in any media controller, which calls its existence into question.
>>
>> Therefore I'd like to consult possible ways of solving this issue.
>> The option I see is implementing a mechanism for moving V4L2 Flash
>> sub-devices between media controllers. A V4L2 Flash sub-device
>> would initially be assigned to one media system in the relevant
>> device tree binding, but it could be dynamically reassigned to
>> the other one. However I'm not sure if media controller design
>> is prepared for dynamic modifications of its graph and how many
>> modifications in the existing drivers this solution would require.
>>
>> 2) Consequences of locking the Flash Manager during flash strobe
>>
>> In case a LED Flash Class device depends on muxes involved in
>> routing the other LED Flash Class device's strobe signals,
>> the Flash Manager must be locked for the time of strobing
>> to prevent reconfiguration of the strobe signal routing
>> by the other device.
>>
>> A blocking_strobe sysfs attribute was added to indicate whether
>> this is the case for the device.
>>
>> Nonetheless, this modifies behaviour of led_set_external_strobe
>> API, which also must block the caller for the time of strobing
>> to protect the strobe signal routing while the external strobe
>> signal provider asserts the flash_en pin.
>>
>> Use case for user space application would be following in this case:
>>   - spawn a new thread in which external strobe is activated
>>   - in the parent thread instruct the external strobe provider
>>     to strobe the flash
>>
>> As an improvement there could be an op added for notifying
>> the application that the strobe signal routing has been
>> set up. It would have to be called from the function
>> led_flash_manager_setup_strobe after led_flash_manager_set_external_strobe
>> returns.
>>
>> The blocking_strobe attribute would have to be also
>> mapped to a new V4L2 Flash control. Unfortunately it poses
>> a problem for the existing users of the V4L2 Flash API
>> which are not aware that setting V4L2_CID_FLASH_STROBE_SOURCE
>> may be blocking.
>>
>> As a solution led_set_external_strobe API could be extended
>> by a parameter telling whether the caller is prepared for
>> the blocking call. led_flash_manager_setup_strobe should
>> be extended accordingly then.
>> In V4L2 "V4L2_FLASH_STROBE_SOURCE_EXTERNAL_BLOCKING" menu item
>> could be added to handle this.
>> With existing V4L2_FLASH_STROBE_SOURCE_EXTERNAL the flash manager
>> wouldn't protect muxes against reconfiguration.
>>
>> TODO:
>>   - switch to using V4L2 array controls
>>   - add s_power op to the LED Flash Class
>>
>>
>> I will be off-line for three weeks from now on and will respond
>> to any questions after getting back.
>>
>>
>> Thanks,
>> Jacek Anaszewski
>>
>> Jacek Anaszewski (21):
>>   leds: make brightness type consistent across whole subsystem
>>   leds: implement sysfs interface locking mechanism
>>   leds: Improve and export led_update_brightness
>>   leds: Reorder include directives
>>   leds: avoid using deprecated DEVICE_ATTR macro
>>   leds: add API for setting torch brightness
>>   of: add of_node_ncmp wrapper
>>   leds: Add sysfs and kernel internal API for flash LEDs
>>   Documentation: leds: Add description of LED Flash Class extension
>>   Documentation: leds: add exemplary asynchronous mux driver
>>   DT: leds: Add flash led devices related properties
>>   DT: Add documentation for LED Class Flash Manger
>>   v4l2-device: add v4l2_device_register_subdev_node API
>>   v4l2-ctrls: add control for flash strobe signal providers
>>   media: Add registration helpers for V4L2 flash
>>   leds: Add support for max77693 mfd flash cell
>>   DT: Add documentation for the mfd Maxim max77693
>>   leds: Add driver for AAT1290 current regulator
>>   of: Add Skyworks Solutions, Inc. vendor prefix
>>   DT: Add documentation for the Skyworks AAT1290
>>   ARM: dts: add aat1290 current regulator device node
>>
>>  Documentation/DocBook/media/v4l/controls.xml       |   11 +
>>  Documentation/devicetree/bindings/leds/common.txt  |   16 +
>>  .../devicetree/bindings/leds/leds-aat1290.txt      |   17 +
>>  .../bindings/leds/leds-flash-manager.txt           |  165 +++
>>  Documentation/devicetree/bindings/mfd/max77693.txt |   62 ++
>>  .../devicetree/bindings/vendor-prefixes.txt        |    1 +
>>  Documentation/leds/leds-async-mux.c                |   65 ++
>>  Documentation/leds/leds-class-flash.txt            |  126 +++
>>  arch/arm/boot/dts/exynos4412-trats2.dts            |   24 +
>>  drivers/leds/Kconfig                               |   28 +
>>  drivers/leds/Makefile                              |    7 +
>>  drivers/leds/led-class-flash.c                     |  715 +++++++++++++
>>  drivers/leds/led-class.c                           |   58 +-
>>  drivers/leds/led-core.c                            |   51 +-
>>  drivers/leds/led-flash-gpio-mux.c                  |  102 ++
>>  drivers/leds/led-flash-manager.c                   |  698 +++++++++++++
>>  drivers/leds/led-triggers.c                        |   16 +-
>>  drivers/leds/leds-aat1290.c                        |  455 +++++++++
>>  drivers/leds/leds-max77693.c                       | 1070 ++++++++++++++++++++
>>  drivers/leds/of_led_flash_manager.c                |  155 +++
>>  drivers/media/v4l2-core/Kconfig                    |   11 +
>>  drivers/media/v4l2-core/Makefile                   |    2 +
>>  drivers/media/v4l2-core/v4l2-ctrls.c               |    2 +
>>  drivers/media/v4l2-core/v4l2-device.c              |   63 +-
>>  drivers/media/v4l2-core/v4l2-flash.c               |  580 +++++++++++
>>  drivers/mfd/max77693.c                             |    5 +-
>>  include/linux/led-class-flash.h                    |  290 ++++++
>>  include/linux/led-flash-gpio-mux.h                 |   68 ++
>>  include/linux/led-flash-manager.h                  |  121 +++
>>  include/linux/leds.h                               |   73 +-
>>  include/linux/mfd/max77693.h                       |   40 +
>>  include/linux/of.h                                 |    1 +
>>  include/linux/of_led_flash_manager.h               |   80 ++
>>  include/media/v4l2-device.h                        |    7 +
>>  include/media/v4l2-flash.h                         |  137 +++
>>  include/uapi/linux/v4l2-controls.h                 |    2 +
>>  36 files changed, 5272 insertions(+), 52 deletions(-)
>>  create mode 100644 Documentation/devicetree/bindings/leds/leds-aat1290.txt
>>  create mode 100644 Documentation/devicetree/bindings/leds/leds-flash-manager.txt
>>  create mode 100644 Documentation/leds/leds-async-mux.c
>>  create mode 100644 Documentation/leds/leds-class-flash.txt
>>  create mode 100644 drivers/leds/led-class-flash.c
>>  create mode 100644 drivers/leds/led-flash-gpio-mux.c
>>  create mode 100644 drivers/leds/led-flash-manager.c
>>  create mode 100644 drivers/leds/leds-aat1290.c
>>  create mode 100644 drivers/leds/leds-max77693.c
>>  create mode 100644 drivers/leds/of_led_flash_manager.c
>>  create mode 100644 drivers/media/v4l2-core/v4l2-flash.c
>>  create mode 100644 include/linux/led-class-flash.h
>>  create mode 100644 include/linux/led-flash-gpio-mux.h
>>  create mode 100644 include/linux/led-flash-manager.h
>>  create mode 100644 include/linux/of_led_flash_manager.h
>>  create mode 100644 include/media/v4l2-flash.h
>>
>> --
>> 1.7.9.5
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-media" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH/RFC v4 06/21] leds: add API for setting torch brightness
  2014-07-11 14:04 ` [PATCH/RFC v4 06/21] leds: add API for setting torch brightness Jacek Anaszewski
@ 2014-07-16 21:54   ` Sakari Ailus
  2014-08-04 12:35     ` Jacek Anaszewski
  0 siblings, 1 reply; 55+ messages in thread
From: Sakari Ailus @ 2014-07-16 21:54 UTC (permalink / raw)
  To: Jacek Anaszewski, linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Bryan Wu, Richard Purdie

Hi Jacek,

Jacek Anaszewski wrote:
...
> diff --git a/include/linux/leds.h b/include/linux/leds.h
> index 1a130cc..9bea9e6 100644
> --- a/include/linux/leds.h
> +++ b/include/linux/leds.h
> @@ -44,11 +44,21 @@ struct led_classdev {
>   #define LED_BLINK_ONESHOT_STOP	(1 << 18)
>   #define LED_BLINK_INVERT	(1 << 19)
>   #define LED_SYSFS_LOCK		(1 << 20)
> +#define LED_DEV_CAP_TORCH	(1 << 21)
>
>   	/* Set LED brightness level */
>   	/* Must not sleep, use a workqueue if needed */
>   	void		(*brightness_set)(struct led_classdev *led_cdev,
>   					  enum led_brightness brightness);
> +	/*
> +	 * Set LED brightness immediately - it is required for flash led
> +	 * devices as they require setting torch brightness to have immediate
> +	 * effect. brightness_set op cannot be used for this purpose because
> +	 * the led drivers schedule a work queue task in it to allow for
> +	 * being called from led-triggers, i.e. from the timer irq context.
> +	 */

Do we need to classify actual devices based on this? I think it's rather 
a different API behaviour between the LED and the V4L2 APIs.

On devices that are slow to control, the behaviour should be asynchronous
over the LED API and synchronous when accessed through the V4L2 API. How
about implementing the work queue, as I have suggested, in the framework, so
that individual drivers don't need to care about this and just implement the
synchronous variant of this op? A flag could be added to distinguish devices
that are fast so that the work queue isn't needed.

It'd be nice to avoid individual drivers having to implement multiple ops to
do the same thing, just for differing user space interfacs.

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi     XMPP: sailus@retiisi.org.uk

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

* Re: [PATCH/RFC v4 07/21] of: add of_node_ncmp wrapper
  2014-07-11 14:04 ` [PATCH/RFC v4 07/21] of: add of_node_ncmp wrapper Jacek Anaszewski
@ 2014-07-16 22:00   ` Sakari Ailus
  2014-07-28 13:41   ` Grant Likely
  1 sibling, 0 replies; 55+ messages in thread
From: Sakari Ailus @ 2014-07-16 22:00 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie, Grant Likely, Benjamin Herrenschmidt, Michal Simek

On Fri, Jul 11, 2014 at 04:04:10PM +0200, Jacek Anaszewski wrote:
> The wrapper for strnicmp is required for checking whether a node has
> expected prefix.

Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [PATCH/RFC v4 15/21] media: Add registration helpers for V4L2 flash
  2014-07-11 14:04 ` [PATCH/RFC v4 15/21] media: Add registration helpers for V4L2 flash Jacek Anaszewski
@ 2014-07-21 11:12   ` Sakari Ailus
  2014-08-04 14:43     ` Jacek Anaszewski
  0 siblings, 1 reply; 55+ messages in thread
From: Sakari Ailus @ 2014-07-21 11:12 UTC (permalink / raw)
  To: Jacek Anaszewski, linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Hans Verkuil

Hi Jacek,

Jacek Anaszewski wrote:
> 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
> support for V4L2 Flash sub-devices 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>
> Cc: Sakari Ailus <sakari.ailus@iki.fi>
> Cc: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/v4l2-core/Kconfig      |   11 +
>  drivers/media/v4l2-core/Makefile     |    2 +
>  drivers/media/v4l2-core/v4l2-flash.c |  580 ++++++++++++++++++++++++++++++++++
>  include/media/v4l2-flash.h           |  137 ++++++++
>  4 files changed, 730 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 9ca0f8d..3ae3f0f 100644
> --- a/drivers/media/v4l2-core/Kconfig
> +++ b/drivers/media/v4l2-core/Kconfig
> @@ -35,6 +35,17 @@ config V4L2_MEM2MEM_DEV
>          tristate
>          depends on VIDEOBUF2_CORE
>  
> +# Used by LED subsystem flash drivers
> +config V4L2_FLASH_LED_CLASS
> +	bool "Enable support for Flash sub-devices"
> +	depends on VIDEO_V4L2_SUBDEV_API
> +	depends on LEDS_CLASS_FLASH
> +	---help---
> +	  Say Y here to enable support for Flash sub-devices, which allow
> +	  to control LED class devices with use of V4L2 Flash controls.
> +
> +	  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 63d29f2..44e858c 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_LED_CLASS) += 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..21080c6
> --- /dev/null
> +++ b/drivers/media/v4l2-core/v4l2-flash.c
> @@ -0,0 +1,580 @@
> +/*
> + * V4L2 Flash LED sub-device 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 <linux/led-class-flash.h>
> +#include <linux/mutex.h>
> +#include <linux/of_led_flash_manager.h>
> +#include <linux/slab.h>
> +#include <media/v4l2-flash.h>
> +
> +#define call_flash_op(v4l2_flash, op, args...)			\
> +		(v4l2_flash->ops->op  ?				\
> +			v4l2_flash->ops->op(args) :		\
> +			-EINVAL)
> +
> +static struct v4l2_device *v4l2_dev;
> +static int registered_flashes;
> +
> +static inline enum led_brightness v4l2_flash_intensity_to_led_brightness(
> +					struct v4l2_ctrl_config *config,
> +					s32 intensity)
> +{
> +	return ((intensity - config->min) / config->step) + 1;
> +}
> +
> +static inline s32 v4l2_flash_led_brightness_to_intensity(
> +					struct v4l2_ctrl_config *config,
> +					enum led_brightness brightness)
> +{
> +	return ((brightness - 1) * config->step) + config->min;
> +}
> +
> +static int v4l2_flash_g_volatile_ctrl(struct v4l2_ctrl *c)
> +
> +{
> +	struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c);
> +	struct led_classdev_flash *flash = v4l2_flash->flash;
> +	struct led_classdev *led_cdev = &flash->led_cdev;
> +	struct v4l2_flash_ctrl_config *config = &v4l2_flash->config;
> +	struct v4l2_flash_ctrl *ctrl = &v4l2_flash->ctrl;
> +	bool is_strobing;
> +	int ret;
> +
> +	switch (c->id) {
> +	case V4L2_CID_FLASH_TORCH_INTENSITY:
> +		/*
> +		 * Update torch brightness only if in TORCH_MODE,
> +		 * as otherwise brightness_update op returns 0,
> +		 * which would spuriously inform user space that
> +		 * V4L2_CID_FLASH_TORCH_INTENSITY control value
> +		 * has changed.
> +		 */
> +		if (ctrl->led_mode->val == V4L2_FLASH_LED_MODE_TORCH) {
> +			ret = call_flash_op(v4l2_flash, torch_brightness_update,
> +							led_cdev);
> +			if (ret < 0)
> +				return ret;
> +			ctrl->torch_intensity->val =
> +				v4l2_flash_led_brightness_to_intensity(
> +						&config->torch_intensity,
> +						led_cdev->brightness);
> +		}
> +		return 0;
> +	case V4L2_CID_FLASH_INTENSITY:
> +		ret = call_flash_op(v4l2_flash, flash_brightness_update,
> +					flash);
> +		if (ret < 0)
> +			return ret;
> +		/* no conversion is needed */
> +		c->val = flash->brightness.val;
> +		return 0;
> +	case V4L2_CID_FLASH_INDICATOR_INTENSITY:
> +		ret = call_flash_op(v4l2_flash, indicator_brightness_update,
> +						flash);
> +		if (ret < 0)
> +			return ret;
> +		/* no conversion is needed */
> +		c->val = flash->indicator_brightness->val;
> +		return 0;
> +	case V4L2_CID_FLASH_STROBE_STATUS:
> +		ret = call_flash_op(v4l2_flash, strobe_get, flash,
> +							&is_strobing);
> +		if (ret < 0)
> +			return ret;
> +		c->val = is_strobing;
> +		return 0;
> +	case V4L2_CID_FLASH_FAULT:
> +		/* led faults map directly to V4L2 flash faults */
> +		ret = call_flash_op(v4l2_flash, fault_get, flash, &c->val);
> +		return ret;
> +	case V4L2_CID_FLASH_STROBE_SOURCE:
> +		c->val = flash->external_strobe;
> +		return 0;
> +	case V4L2_CID_FLASH_STROBE_PROVIDER:
> +		c->val = flash->strobe_provider_id;
> +		return 0;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int v4l2_flash_s_ctrl(struct v4l2_ctrl *c)
> +{
> +	struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c);
> +	struct led_classdev_flash *flash = v4l2_flash->flash;
> +	struct v4l2_flash_ctrl *ctrl = &v4l2_flash->ctrl;
> +	struct v4l2_flash_ctrl_config *config = &v4l2_flash->config;
> +	enum led_brightness torch_brightness;
> +	bool external_strobe;
> +	int ret;
> +
> +	switch (c->id) {
> +	case V4L2_CID_FLASH_LED_MODE:
> +		switch (c->val) {
> +		case V4L2_FLASH_LED_MODE_NONE:
> +			call_flash_op(v4l2_flash, torch_brightness_set,
> +							&flash->led_cdev, 0);
> +			return call_flash_op(v4l2_flash, strobe_set, flash,
> +							false);
> +		case V4L2_FLASH_LED_MODE_FLASH:
> +			/* Turn off torch LED */
> +			call_flash_op(v4l2_flash, torch_brightness_set,
> +							&flash->led_cdev, 0);
> +			external_strobe = (ctrl->source->val ==
> +					V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
> +			return call_flash_op(v4l2_flash, external_strobe_set,
> +						flash, external_strobe);
> +		case V4L2_FLASH_LED_MODE_TORCH:
> +			/* Stop flash strobing */
> +			ret = call_flash_op(v4l2_flash, strobe_set, flash,
> +							false);
> +			if (ret)
> +				return ret;
> +
> +			torch_brightness =
> +				v4l2_flash_intensity_to_led_brightness(
> +						&config->torch_intensity,
> +						ctrl->torch_intensity->val);
> +			call_flash_op(v4l2_flash, torch_brightness_set,
> +					&flash->led_cdev, torch_brightness);
> +			return ret;
> +		}
> +		break;
> +	case V4L2_CID_FLASH_STROBE_SOURCE:
> +		external_strobe = (c->val == V4L2_FLASH_STROBE_SOURCE_EXTERNAL);

Is the external_strobe argument match exactly to the strobe source
control? You seem to assume that in g_volatile_ctrl() above. I think
having it the either way is fine but not both. :-)

> +		return call_flash_op(v4l2_flash, external_strobe_set, flash,
> +							external_strobe);
> +	case V4L2_CID_FLASH_STROBE:
> +		if (ctrl->led_mode->val != V4L2_FLASH_LED_MODE_FLASH ||
> +		    ctrl->source->val != V4L2_FLASH_STROBE_SOURCE_SOFTWARE)
> +			return -EINVAL;
> +		return call_flash_op(v4l2_flash, strobe_set, flash, true);
> +	case V4L2_CID_FLASH_STROBE_STOP:

Should we check the flash mode here? I guess so. How about strobe source
as well?

> +		return call_flash_op(v4l2_flash, strobe_set, flash, false);
> +	case V4L2_CID_FLASH_TIMEOUT:
> +		return call_flash_op(v4l2_flash, timeout_set, flash, c->val);
> +	case V4L2_CID_FLASH_INTENSITY:
> +		/* no conversion is needed */
> +		return call_flash_op(v4l2_flash, flash_brightness_set, flash,
> +								c->val);
> +	case V4L2_CID_FLASH_INDICATOR_INTENSITY:
> +		/* no conversion is needed */
> +		return call_flash_op(v4l2_flash, indicator_brightness_set,
> +						flash, c->val);
> +	case V4L2_CID_FLASH_TORCH_INTENSITY:
> +		/*
> +		 * If not in MODE_TORCH don't call led-class brightness_set
> +		 * op, as it would result in turning the torch led on.
> +		 * Instead the value is cached only and will be written
> +		 * to the device upon transition to MODE_TORCH.
> +		 */
> +		if (ctrl->led_mode->val == V4L2_FLASH_LED_MODE_TORCH) {
> +			torch_brightness =
> +				v4l2_flash_intensity_to_led_brightness(
> +						&config->torch_intensity,
> +						ctrl->torch_intensity->val);
> +			call_flash_op(v4l2_flash, torch_brightness_set,
> +					&flash->led_cdev, torch_brightness);
> +		}
> +		return 0;
> +	case V4L2_CID_FLASH_STROBE_PROVIDER:
> +		flash->strobe_provider_id = c->val;
> +		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_strobe_providers_menu(struct v4l2_flash *v4l2_flash)
> +{
> +	struct led_classdev_flash *flash = v4l2_flash->flash;
> +	struct led_flash_strobe_provider *provider;
> +	struct v4l2_ctrl *ctrl;
> +	int i = 0;
> +
> +	v4l2_flash->strobe_providers_menu =
> +			kzalloc(sizeof(char *) * (flash->num_strobe_providers),
> +					GFP_KERNEL);
> +	if (!v4l2_flash->strobe_providers_menu)
> +		return -ENOMEM;
> +
> +	list_for_each_entry(provider, &flash->strobe_providers, list)
> +		v4l2_flash->strobe_providers_menu[i++] =
> +						(char *) provider->name;
> +
> +	ctrl = v4l2_ctrl_new_std_menu_items(
> +		&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
> +		V4L2_CID_FLASH_STROBE_PROVIDER,
> +		flash->num_strobe_providers - 1,
> +		0, 0,
> +		(const char * const *) v4l2_flash->strobe_providers_menu);
> +
> +	if (ctrl)
> +		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
> +
> +	return 0;
> +}
> +
> +static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash)
> +
> +{
> +	struct led_classdev_flash *flash = v4l2_flash->flash;
> +	const struct led_flash_ops *flash_ops = flash->ops;
> +	struct led_classdev *led_cdev = &flash->led_cdev;
> +	struct v4l2_flash_ctrl_config *config = &v4l2_flash->config;
> +	struct v4l2_ctrl *ctrl;
> +	struct v4l2_ctrl_config *ctrl_cfg;
> +	bool has_flash = led_cdev->flags & LED_DEV_CAP_FLASH;
> +	bool has_indicator = led_cdev->flags & LED_DEV_CAP_INDICATOR;
> +	bool has_strobe_providers = (flash->num_strobe_providers > 1);
> +	unsigned int mask;
> +	int ret, max, num_ctrls;
> +
> +	num_ctrls = has_flash ? 5 : 2;
> +	if (has_flash) {
> +		if (flash_ops->flash_brightness_set)
> +			++num_ctrls;
> +		if (flash_ops->timeout_set)
> +			++num_ctrls;
> +		if (flash_ops->strobe_get)
> +			++num_ctrls;
> +		if (has_indicator)
> +			++num_ctrls;
> +		if (config->flash_faults)
> +			++num_ctrls;
> +		if (has_strobe_providers)
> +			++num_ctrls;
> +	}
> +
> +	v4l2_ctrl_handler_init(&v4l2_flash->hdl, num_ctrls);
> +
> +	mask = 1 << V4L2_FLASH_LED_MODE_NONE |
> +	       1 << V4L2_FLASH_LED_MODE_TORCH;
> +	if (flash)
> +		mask |= 1 << V4L2_FLASH_LED_MODE_FLASH;
> +
> +	/* Configure FLASH_LED_MODE ctrl */
> +	v4l2_flash->ctrl.led_mode = v4l2_ctrl_new_std_menu(
> +			&v4l2_flash->hdl,
> +			&v4l2_flash_ctrl_ops, V4L2_CID_FLASH_LED_MODE,
> +			V4L2_FLASH_LED_MODE_TORCH, ~mask,
> +			V4L2_FLASH_LED_MODE_NONE);
> +
> +	/* Configure TORCH_INTENSITY ctrl */
> +	ctrl_cfg = &config->torch_intensity;
> +	ctrl = v4l2_ctrl_new_std(&v4l2_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;
> +	v4l2_flash->ctrl.torch_intensity = ctrl;
> +
> +	if (has_flash) {
> +		/* Configure FLASH_STROBE_SOURCE ctrl */
> +		mask = 1 << V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
> +
> +		if (flash->has_external_strobe) {
> +			mask |= 1 << V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
> +			max = V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
> +		} else {
> +			max = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
> +		}
> +
> +		v4l2_flash->ctrl.source = v4l2_ctrl_new_std_menu(
> +					&v4l2_flash->hdl,
> +					&v4l2_flash_ctrl_ops,
> +					V4L2_CID_FLASH_STROBE_SOURCE,
> +					max,
> +					~mask,
> +					V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
> +		if (v4l2_flash->ctrl.source)
> +			v4l2_flash->ctrl.source->flags |=
> +						V4L2_CTRL_FLAG_VOLATILE;
> +
> +		/* Configure FLASH_STROBE ctrl */
> +		ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
> +					  V4L2_CID_FLASH_STROBE, 0, 1, 1, 0);
> +
> +		/* Configure FLASH_STROBE_STOP ctrl */
> +		ctrl = v4l2_ctrl_new_std(&v4l2_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(&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
> +					 V4L2_CID_FLASH_STROBE_STATUS,
> +					 0, 1, 1, 1);

I think you only should implement the strobe status control if you
really can know it, i.e. have strobe_get().

> +		if (flash_ops->strobe_get)
> +			if (ctrl)
> +				ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
> +					       V4L2_CTRL_FLAG_READ_ONLY;
> +
> +		if (flash_ops->timeout_set) {
> +			/* Configure FLASH_TIMEOUT ctrl */
> +			ctrl_cfg = &config->flash_timeout;
> +			ctrl = v4l2_ctrl_new_std(
> +					&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
> +					V4L2_CID_FLASH_TIMEOUT, ctrl_cfg->min,
> +					ctrl_cfg->max, ctrl_cfg->step,
> +					ctrl_cfg->def);
> +			if (ctrl)
> +				ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
> +		}
> +
> +		if (flash_ops->flash_brightness_set) {
> +			/* Configure FLASH_INTENSITY ctrl */
> +			ctrl_cfg = &config->flash_intensity;
> +			ctrl = v4l2_ctrl_new_std(
> +					&v4l2_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->flash_faults) {
> +			/* Configure FLASH_FAULT ctrl */
> +			ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl,
> +						 &v4l2_flash_ctrl_ops,
> +						 V4L2_CID_FLASH_FAULT, 0,
> +						 config->flash_faults,
> +						 0, 0);
> +			if (ctrl)
> +				ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
> +					       V4L2_CTRL_FLAG_READ_ONLY;
> +		}
> +		if (has_indicator) {
> +			/* Configure FLASH_INDICATOR_INTENSITY ctrl */
> +			ctrl_cfg = &config->indicator_intensity;
> +			ctrl = v4l2_ctrl_new_std(
> +					&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
> +					V4L2_CID_FLASH_INDICATOR_INTENSITY,
> +					ctrl_cfg->min, ctrl_cfg->max,
> +					ctrl_cfg->step, ctrl_cfg->def);
> +			if (ctrl)
> +				ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
> +		}

Could you implement the above in a loop? You're essentially repeating
the same but with different parameters.

> +		if (has_strobe_providers) {
> +			/* Configure V4L2_CID_FLASH_STROBE_PROVIDERS ctrl */
> +			ret = v4l2_flash_init_strobe_providers_menu(v4l2_flash);
> +			if (ret < 0)
> +				goto error_free_handler;
> +		}
> +	}
> +
> +	if (v4l2_flash->hdl.error) {
> +		ret = v4l2_flash->hdl.error;
> +		goto error_free_handler;
> +	}
> +
> +	ret = v4l2_ctrl_handler_setup(&v4l2_flash->hdl);
> +	if (ret < 0)
> +		goto error_free_handler;
> +
> +	v4l2_flash->sd.ctrl_handler = &v4l2_flash->hdl;
> +
> +	return 0;
> +
> +error_free_handler:
> +	v4l2_ctrl_handler_free(&v4l2_flash->hdl);
> +	return ret;
> +}
> +
> +/*
> + * V4L2 subdev internal operations
> + */
> +
> +static int v4l2_flash_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd);
> +	struct led_classdev_flash *flash = v4l2_flash->flash;
> +	struct led_classdev *led_cdev = &flash->led_cdev;
> +
> +	mutex_lock(&led_cdev->led_lock);
> +	call_flash_op(v4l2_flash, sysfs_lock, led_cdev);

What if you have the device open through multiple file handles? I
believe v4l2_subdev_fh_is_singular(&fh->vfh) would prove helpful here.

> +	mutex_unlock(&led_cdev->led_lock);
> +
> +	return 0;
> +}
> +
> +static int v4l2_flash_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd);
> +	struct led_classdev_flash *flash = v4l2_flash->flash;
> +	struct led_classdev *led_cdev = &flash->led_cdev;
> +
> +	mutex_lock(&led_cdev->led_lock);
> +	call_flash_op(v4l2_flash, sysfs_unlock, led_cdev);
> +	mutex_unlock(&led_cdev->led_lock);
> +
> +	return 0;
> +}
> +
> +int v4l2_flash_register(struct v4l2_flash *v4l2_flash)

Do you expect to call this from elsewhere than this file?

> +{
> +	struct led_classdev_flash *flash = v4l2_flash->flash;
> +	struct led_classdev *led_cdev = &flash->led_cdev;
> +	int ret;
> +
> +	if (!v4l2_dev) {
> +		v4l2_dev = kzalloc(sizeof(*v4l2_dev), GFP_KERNEL);
> +		if (!v4l2_dev)
> +			return -ENOMEM;
> +
> +		strlcpy(v4l2_dev->name, "v4l2-flash-manager",
> +						sizeof(v4l2_dev->name));
> +		ret = v4l2_device_register(NULL, v4l2_dev);
> +		if (ret < 0) {
> +			dev_err(led_cdev->dev->parent,
> +				 "Failed to register v4l2_device: %d\n", ret);
> +			goto err_v4l2_device_register;
> +		}
> +	}
> +
> +	ret = v4l2_device_register_subdev(v4l2_dev, &v4l2_flash->sd);
> +	if (ret < 0) {
> +		dev_err(led_cdev->dev->parent,
> +			 "Failed to register v4l2_subdev: %d\n", ret);
> +		goto err_v4l2_device_register;
> +	}
> +
> +	ret = v4l2_device_register_subdev_node(&v4l2_flash->sd, v4l2_dev);
> +	if (ret < 0) {
> +		dev_err(led_cdev->dev->parent,
> +			 "Failed to register v4l2_subdev node: %d\n", ret);
> +		goto err_register_subdev_node;
> +	}

This way you can create a V4L2 sub-device node. However, flash devices
are seldom alone in the system. They are physically close to a sensor,
and this connection is shown in the Media controller interface. This
means that the flash sub-device (entity) should be part of the Media
device created by the driver in control of it. This can be achieved by
the master driver creating the sub-device. You should register an async
sub-device here.

This results in the sub-device not being registered if there's no such
master driver, but I wouldn't expect that to be an issue since the V4L2
flash API is mostly relevant in such cases.

> +	++registered_flashes;
> +
> +	return 0;
> +
> +err_register_subdev_node:
> +	v4l2_device_unregister_subdev(&v4l2_flash->sd);
> +err_v4l2_device_register:
> +	kfree(v4l2_flash->strobe_providers_menu);
> +	if (v4l2_dev && registered_flashes == 0) {
> +		v4l2_device_unregister(v4l2_dev);
> +		kfree(v4l2_dev);
> +		v4l2_dev = NULL;
> +	}
> +
> +	return ret;
> +}
> +
> +static void v4l2_flash_unregister(struct v4l2_flash *v4l2_flash)
> +{
> +	if (registered_flashes == 0)
> +		return;
> +
> +	v4l2_device_unregister_subdev(&v4l2_flash->sd);
> +
> +	--registered_flashes;
> +
> +	if (registered_flashes == 0) {
> +		v4l2_device_unregister(v4l2_dev);
> +		kfree(v4l2_dev);
> +		v4l2_dev = NULL;
> +	}
> +}
> +
> +static const struct v4l2_subdev_internal_ops v4l2_flash_subdev_internal_ops = {
> +	.open = v4l2_flash_open,
> +	.close = v4l2_flash_close,
> +};
> +
> +static const struct v4l2_subdev_core_ops v4l2_flash_core_ops = {
> +	.queryctrl = v4l2_subdev_queryctrl,
> +	.querymenu = v4l2_subdev_querymenu,
> +};
> +
> +static const struct v4l2_subdev_ops v4l2_flash_subdev_ops = {
> +	.core = &v4l2_flash_core_ops,
> +};
> +
> +int v4l2_flash_init(struct led_classdev_flash *flash,
> +		    struct v4l2_flash_ctrl_config *config,
> +		    const struct v4l2_flash_ops *flash_ops,
> +		    struct v4l2_flash **out_flash)
> +{
> +	struct v4l2_flash *v4l2_flash;
> +	struct led_classdev *led_cdev = &flash->led_cdev;
> +	struct v4l2_subdev *sd;
> +	int ret;
> +
> +	if (!flash || !config || !out_flash)
> +		return -EINVAL;
> +
> +	v4l2_flash = kzalloc(sizeof(*v4l2_flash), GFP_KERNEL);
> +	if (!v4l2_flash)
> +		return -ENOMEM;
> +
> +	sd = &v4l2_flash->sd;
> +	v4l2_flash->flash = flash;
> +	v4l2_flash->ops = flash_ops;
> +	sd->dev = led_cdev->dev->parent;
> +	v4l2_subdev_init(sd, &v4l2_flash_subdev_ops);
> +	sd->internal_ops = &v4l2_flash_subdev_internal_ops;
> +	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	snprintf(sd->name, sizeof(sd->name), led_cdev->name);
> +
> +	v4l2_flash->config = *config;
> +	ret = v4l2_flash_init_controls(v4l2_flash);
> +	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_flash_register(v4l2_flash);
> +	if (ret < 0)
> +		goto err_init_entity;
> +
> +	*out_flash = v4l2_flash;

How about returning the pointer instead?

> +	return 0;
> +
> +err_init_entity:
> +	media_entity_cleanup(&sd->entity);
> +err_init_controls:
> +	v4l2_ctrl_handler_free(sd->ctrl_handler);
> +	kfree(v4l2_flash);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(v4l2_flash_init);
> +
> +void v4l2_flash_release(struct v4l2_flash *v4l2_flash)
> +{
> +	if (!v4l2_flash)
> +		return;
> +
> +	v4l2_flash_unregister(v4l2_flash);
> +	v4l2_ctrl_handler_free(v4l2_flash->sd.ctrl_handler);
> +	media_entity_cleanup(&v4l2_flash->sd.entity);
> +	kfree(v4l2_flash->strobe_providers_menu);
> +	kfree(v4l2_flash);
> +}
> +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..effa46b
> --- /dev/null
> +++ b/include/media/v4l2-flash.h
> @@ -0,0 +1,137 @@
> +/*
> + * V4L2 Flash LED sub-device 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 <media/v4l2-ioctl.h>
> +
> +struct led_classdev_flash;
> +struct led_classdev;
> +enum led_brightness;
> +
> +struct v4l2_flash_ops {
> +	int (*torch_brightness_set)(struct led_classdev *led_cdev,
> +					enum led_brightness brightness);
> +	int (*torch_brightness_update)(struct led_classdev *led_cdev);
> +	int (*flash_brightness_set)(struct led_classdev_flash *flash,
> +					u32 brightness);
> +	int (*flash_brightness_update)(struct led_classdev_flash *flash);
> +	int (*strobe_set)(struct led_classdev_flash *flash, bool state);
> +	int (*strobe_get)(struct led_classdev_flash *flash, bool *state);
> +	int (*timeout_set)(struct led_classdev_flash *flash, u32 timeout);
> +	int (*indicator_brightness_set)(struct led_classdev_flash *flash,
> +					u32 brightness);
> +	int (*indicator_brightness_update)(struct led_classdev_flash *flash);
> +	int (*external_strobe_set)(struct led_classdev_flash *flash,
> +					bool enable);
> +	int (*fault_get)(struct led_classdev_flash *flash, u32 *fault);
> +	void (*sysfs_lock)(struct led_classdev *led_cdev);
> +	void (*sysfs_unlock)(struct led_classdev *led_cdev);

These functions are not driver specific and there's going to be just one
implementation (I suppose). Could you refresh my memory regarding why
the LED framework functions aren't called directly?

> +};
> +
> +/**
> + * struct v4l2_flash_ctrl - controls that define the sub-dev's state
> + * @source:		V4L2_CID_FLASH_STROBE_SOURCE control
> + * @led_mode:		V4L2_CID_FLASH_LED_MODE control
> + * @torch_intensity:	V4L2_CID_FLASH_TORCH_INTENSITY control
> + */
> +struct v4l2_flash_ctrl {
> +	struct v4l2_ctrl *source;
> +	struct v4l2_ctrl *led_mode;
> +	struct v4l2_ctrl *torch_intensity;
> +};
> +
> +/**
> + * struct v4l2_flash_ctrl_config - V4L2 Flash controls initialization data
> + * @torch_intensity:		V4L2_CID_FLASH_TORCH_INTENSITY constraints
> + * @flash_intensity:		V4L2_CID_FLASH_INTENSITY constraints
> + * @indicator_intensity:	V4L2_CID_FLASH_INDICATOR_INTENSITY constraints
> + * @flash_timeout:		V4L2_CID_FLASH_TIMEOUT constraints
> + * @flash_fault:		possible flash faults
> + */
> +struct v4l2_flash_ctrl_config {
> +	struct v4l2_ctrl_config torch_intensity;
> +	struct v4l2_ctrl_config flash_intensity;
> +	struct v4l2_ctrl_config indicator_intensity;
> +	struct v4l2_ctrl_config flash_timeout;
> +	u32 flash_faults;
> +};
> +
> +/**
> + * struct v4l2_flash - Flash sub-device context
> + * @flash:		LED Flash Class device controlled by this sub-device
> + * @ops:		LED Flash Class device ops
> + * @sd:			V4L2 sub-device
> + * @hdl:		flash controls handler
> + * @ctrl:		state defining controls
> + * @config:		V4L2 Flash controlsrconfiguration data
> + * @software_strobe_gates: route to the software strobe signal
> + * @external_strobe_gates: route to the external strobe signal
> + * @sensors:		available external strobe sources
> + */
> +struct v4l2_flash {
> +	struct led_classdev_flash *flash;
> +	const struct v4l2_flash_ops *ops;
> +
> +	struct v4l2_subdev sd;
> +	struct v4l2_ctrl_handler hdl;
> +	struct v4l2_flash_ctrl ctrl;
> +	struct v4l2_flash_ctrl_config config;
> +	char **strobe_providers_menu;
> +};
> +
> +static inline struct v4l2_flash *v4l2_subdev_to_v4l2_flash(
> +							struct v4l2_subdev *sd)
> +{
> +	return container_of(sd, struct v4l2_flash, sd);
> +}
> +
> +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_LED_CLASS
> +/**
> + * v4l2_flash_init - initialize V4L2 flash led sub-device
> + * @led_fdev:	the LED Flash Class device to wrap
> + * @config:	initialization data for V4L2 Flash controls
> + * @flash_ops:	V4L2 Flash device ops
> + * @out_flash:	handler to the new V4L2 Flash device
> + *
> + * Create V4L2 subdev wrapping given LED subsystem device.
> +
> + * Returns: 0 on success or negative error value on failure
> + */
> +int v4l2_flash_init(struct led_classdev_flash *led_fdev,
> +		    struct v4l2_flash_ctrl_config *config,
> +		    const struct v4l2_flash_ops *flash_ops,
> +		    struct v4l2_flash **out_flash);
> +
> +/**
> + * v4l2_flash_release - release V4L2 Flash sub-device
> + * @flash: the V4L2 Flash device to release
> + *
> + * Release V4L2 flash led subdev.
> + */
> +void v4l2_flash_release(struct v4l2_flash *v4l2_flash);
> +
> +#else
> +#define v4l2_flash_init(led_cdev, config, flash_ops, out_flash) (0)
> +#define v4l2_flash_release(v4l2_flash)
> +#endif /* CONFIG_V4L2_FLASH_LED_CLASS */
> +
> +#endif /* _V4L2_FLASH_H */
> 

-- 
Kind regards,

Sakari Ailus
sakari.ailus@iki.fi

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

* Re: [PATCH/RFC v4 16/21] leds: Add support for max77693 mfd flash cell
  2014-07-11 14:04 ` [PATCH/RFC v4 16/21] leds: Add support for max77693 mfd flash cell Jacek Anaszewski
@ 2014-07-21 14:12   ` Sakari Ailus
  0 siblings, 0 replies; 55+ messages in thread
From: Sakari Ailus @ 2014-07-21 14:12 UTC (permalink / raw)
  To: Jacek Anaszewski, linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Andrzej Hajda, Bryan Wu,
	Richard Purdie, SangYoung Son, Samuel Ortiz

Hi Jacek,

Thanks for the patchset.

Jacek Anaszewski wrote:
> This patch adds led-flash support to Maxim max77693 chipset.
> A device can be exposed to user space through LED subsystem
> sysfs interface or through V4L2 subdevice when the support
> for V4L2 Flash sub-devices 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: Jacek Anaszewski <j.anaszewski@samsung.com>
> Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
> Acked-by: Lee Jones <lee.jones@linaro.org>
> 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>
> ---
>  drivers/leds/Kconfig         |    9 +
>  drivers/leds/Makefile        |    1 +
>  drivers/leds/leds-max77693.c | 1070 ++++++++++++++++++++++++++++++++++++++++++
>  drivers/mfd/max77693.c       |    5 +-
>  include/linux/mfd/max77693.h |   40 ++
>  5 files changed, 1124 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/leds/leds-max77693.c
> 
> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> index 5032c6f..794055e 100644
> --- a/drivers/leds/Kconfig
> +++ b/drivers/leds/Kconfig
> @@ -457,6 +457,15 @@ 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
> +	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 237c5ba..da1a4ba 100644
> --- a/drivers/leds/Makefile
> +++ b/drivers/leds/Makefile
> @@ -55,6 +55,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..38a2398
> --- /dev/null
> +++ b/drivers/leds/leds-max77693.c
> @@ -0,0 +1,1070 @@
> +/*
> + * LED Flash Class driver for the flash cell of max77693 mfd.
> + *
> + *	Copyright (C) 2014, Samsung Electronics Co., Ltd.
> + *
> + *	Authors: Jacek Anaszewski <j.anaszewski@samsung.com>
> + *		 Andrzej Hajda <a.hajda@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 <asm/div64.h>
> +#include <linux/led-class-flash.h>
> +#include <linux/led-flash-manager.h>
> +#include <linux/mfd/max77693.h>
> +#include <linux/mfd/max77693-private.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +#include <linux/workqueue.h>
> +#include <media/v4l2-flash.h>
> +
> +#define MAX77693_LED_NAME_1		"max77693-flash_1"
> +#define MAX77693_LED_NAME_2		"max77693-flash_2"
> +
> +#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

Which registers are these definitions related to? Could they be defined
next to the registers instead?

You could parameterise these macros, e.g.

#define MAX77693_FLASH_EN_SHIFT(a)	(6 - ((a) - 1) * 2)

> +#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_FLASH1			(1 << 0)
> +#define MAX77693_MODE_FLASH2			(1 << 1)
> +#define MAX77693_MODE_TORCH1			(1 << 2)
> +#define MAX77693_MODE_TORCH2			(1 << 3)
> +#define MAX77693_MODE_FLASH_EXTERNAL1		(1 << 4)
> +#define MAX77693_MODE_FLASH_EXTERNAL2		(1 << 5)
> +
> +enum {
> +	FLED1,
> +	FLED2
> +};
> +
> +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_flash ldev1;
> +	struct work_struct work1_brightness_set;
> +	struct v4l2_flash *v4l2_flash1;
> +
> +	struct led_classdev_flash ldev2;
> +	struct work_struct work2_brightness_set;
> +	struct v4l2_flash *v4l2_flash2;
> +
> +	unsigned int torch1_brightness;
> +	unsigned int torch2_brightness;
> +	unsigned int flash1_timeout;
> +	unsigned int flash2_timeout;

Could you use e.g. an array of structs instead? You could remove a lot
of redundant code operating on the other led.

> +	unsigned int current_flash_timeout;
> +	unsigned int mode_flags;
> +	u8 torch_iout_reg;
> +	bool iout_joint;
> +};

...

> +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;
> +	int ret;
> +
> +	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
> +	if (!led)
> +		return -ENOMEM;
> +
> +	led->pdev = pdev;
> +	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);
> +
> +	if (p->num_leds == 1 && p->fleds[FLED1] && p->fleds[FLED2])
> +		led->iout_joint = true;
> +
> +	ret = max77693_setup(led);
> +	if (ret < 0)
> +		return ret;

goto err_register_led1;

> +	if (led->iout_joint || p->fleds[FLED1]) {
> +		ret = max77693_register_led1(led);
> +		if (ret < 0)
> +			goto err_register_led1;
> +	}
> +
> +	if (!led->iout_joint && p->fleds[FLED2]) {
> +		ret = max77693_register_led2(led);
> +		if (ret < 0)
> +			goto err_register_led2;
> +	}
> +
> +	return 0;
> +
> +err_register_led2:
> +	if (!p->fleds[FLED1])
> +		goto err_register_led1;
> +	v4l2_flash_release(led->v4l2_flash1);
> +	led_classdev_flash_unregister(&led->ldev1);
> +err_register_led1:
> +	mutex_destroy(&led->lock);
> +
> +	return ret;
> +}

...

-- 
Kind regards,

Sakari Ailus
sakari.ailus@iki.fi

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

* Re: [PATCH/RFC v4 07/21] of: add of_node_ncmp wrapper
  2014-07-11 14:04 ` [PATCH/RFC v4 07/21] of: add of_node_ncmp wrapper Jacek Anaszewski
  2014-07-16 22:00   ` Sakari Ailus
@ 2014-07-28 13:41   ` Grant Likely
  1 sibling, 0 replies; 55+ messages in thread
From: Grant Likely @ 2014-07-28 13:41 UTC (permalink / raw)
  To: Jacek Anaszewski, linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski,
	Benjamin Herrenschmidt, Michal Simek

On Fri, 11 Jul 2014 16:04:10 +0200, Jacek Anaszewski <j.anaszewski@samsung.com> wrote:
> The wrapper for strnicmp is required for checking whether a node has
> expected prefix.
> 
> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> Cc: Grant Likely <grant.likely@linaro.org>
> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> Cc: Michal Simek <monstr@monstr.eu>
> ---
>  include/linux/of.h |    1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/include/linux/of.h b/include/linux/of.h
> index 692b56c..9a53eea 100644
> --- a/include/linux/of.h
> +++ b/include/linux/of.h
> @@ -199,6 +199,7 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size)
>  #define of_compat_cmp(s1, s2, l)	strcasecmp((s1), (s2))
>  #define of_prop_cmp(s1, s2)		strcmp((s1), (s2))
>  #define of_node_cmp(s1, s2)		strcasecmp((s1), (s2))
> +#define of_node_ncmp(s1, s2, n)		strnicmp((s1), (s2), (n))
>  #endif

Don't forget to add an of_node_ncmp() define to
arch/sparc/include/asm/prom.h. Sparc has its own rules.

g.


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

* Re: [PATCH/RFC v4 06/21] leds: add API for setting torch brightness
  2014-07-16 21:54   ` Sakari Ailus
@ 2014-08-04 12:35     ` Jacek Anaszewski
  2014-08-04 12:50       ` Sakari Ailus
  0 siblings, 1 reply; 55+ messages in thread
From: Jacek Anaszewski @ 2014-08-04 12:35 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie, Bryan Wu, Richard Purdie

Hi Sakari,

On 07/16/2014 11:54 PM, Sakari Ailus wrote:
> Hi Jacek,
>
> Jacek Anaszewski wrote:
> ...
>> diff --git a/include/linux/leds.h b/include/linux/leds.h
>> index 1a130cc..9bea9e6 100644
>> --- a/include/linux/leds.h
>> +++ b/include/linux/leds.h
>> @@ -44,11 +44,21 @@ struct led_classdev {
>>   #define LED_BLINK_ONESHOT_STOP    (1 << 18)
>>   #define LED_BLINK_INVERT    (1 << 19)
>>   #define LED_SYSFS_LOCK        (1 << 20)
>> +#define LED_DEV_CAP_TORCH    (1 << 21)
>>
>>       /* Set LED brightness level */
>>       /* Must not sleep, use a workqueue if needed */
>>       void        (*brightness_set)(struct led_classdev *led_cdev,
>>                         enum led_brightness brightness);
>> +    /*
>> +     * Set LED brightness immediately - it is required for flash led
>> +     * devices as they require setting torch brightness to have
>> immediate
>> +     * effect. brightness_set op cannot be used for this purpose because
>> +     * the led drivers schedule a work queue task in it to allow for
>> +     * being called from led-triggers, i.e. from the timer irq context.
>> +     */
>
> Do we need to classify actual devices based on this? I think it's rather
> a different API behaviour between the LED and the V4L2 APIs.
>
> On devices that are slow to control, the behaviour should be asynchronous
> over the LED API and synchronous when accessed through the V4L2 API. How
> about implementing the work queue, as I have suggested, in the
> framework, so
> that individual drivers don't need to care about this and just implement
> the
> synchronous variant of this op? A flag could be added to distinguish
> devices
> that are fast so that the work queue isn't needed.
>
> It'd be nice to avoid individual drivers having to implement multiple
> ops to
> do the same thing, just for differing user space interfacs.
>

It is not only the matter of a device controller speed. If a flash
device is to be made accessible from the LED subsystem, then it
should be also compatible with led-triggers. Some of led-triggers
call brightness_set op from the timer irq context and thus no
locking in the callback can occur. This requirement cannot be
met i.e. if i2c bus is to be used. This is probably the primary
reason for scheduling work queue tasks in brightness_set op.

Having the above in mind, setting a brightness in a work queue
task must be possible for all LED Class Flash drivers, regardless
whether related devices have fast or slow controller.

Let's recap the cost of possible solutions then:

1) Moving the work queues to the LED framework

   - it would probably require extending led_set_brightness and
     __led_set_brightness functions by a parameter indicating whether it
     should call brightness_set op in the work queue task or directly;
   - all existing triggers would have to be updated accordingly;
   - work queues would have to be removed from all the LED drivers;

2) adding led_set_torch_brightness API

   - no modifications in existing drivers and triggers would be required
   - instead, only the modifications from the discussed patch would
     be required

Solution 1 looks cleaner but requires much more modifications.

Best Regards,
Jacek Anaszewski

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

* Re: [PATCH/RFC v4 06/21] leds: add API for setting torch brightness
  2014-08-04 12:35     ` Jacek Anaszewski
@ 2014-08-04 12:50       ` Sakari Ailus
  2014-08-07 13:12         ` Jacek Anaszewski
  0 siblings, 1 reply; 55+ messages in thread
From: Sakari Ailus @ 2014-08-04 12:50 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie, Bryan Wu, Richard Purdie

Hi Jacek,

Thank you for your continued efforts on this!

On Mon, Aug 04, 2014 at 02:35:26PM +0200, Jacek Anaszewski wrote:
> On 07/16/2014 11:54 PM, Sakari Ailus wrote:
> >Hi Jacek,
> >
> >Jacek Anaszewski wrote:
> >...
> >>diff --git a/include/linux/leds.h b/include/linux/leds.h
> >>index 1a130cc..9bea9e6 100644
> >>--- a/include/linux/leds.h
> >>+++ b/include/linux/leds.h
> >>@@ -44,11 +44,21 @@ struct led_classdev {
> >>  #define LED_BLINK_ONESHOT_STOP    (1 << 18)
> >>  #define LED_BLINK_INVERT    (1 << 19)
> >>  #define LED_SYSFS_LOCK        (1 << 20)
> >>+#define LED_DEV_CAP_TORCH    (1 << 21)
> >>
> >>      /* Set LED brightness level */
> >>      /* Must not sleep, use a workqueue if needed */
> >>      void        (*brightness_set)(struct led_classdev *led_cdev,
> >>                        enum led_brightness brightness);
> >>+    /*
> >>+     * Set LED brightness immediately - it is required for flash led
> >>+     * devices as they require setting torch brightness to have
> >>immediate
> >>+     * effect. brightness_set op cannot be used for this purpose because
> >>+     * the led drivers schedule a work queue task in it to allow for
> >>+     * being called from led-triggers, i.e. from the timer irq context.
> >>+     */
> >
> >Do we need to classify actual devices based on this? I think it's rather
> >a different API behaviour between the LED and the V4L2 APIs.
> >
> >On devices that are slow to control, the behaviour should be asynchronous
> >over the LED API and synchronous when accessed through the V4L2 API. How
> >about implementing the work queue, as I have suggested, in the
> >framework, so
> >that individual drivers don't need to care about this and just implement
> >the
> >synchronous variant of this op? A flag could be added to distinguish
> >devices
> >that are fast so that the work queue isn't needed.
> >
> >It'd be nice to avoid individual drivers having to implement multiple
> >ops to
> >do the same thing, just for differing user space interfacs.
> >
> 
> It is not only the matter of a device controller speed. If a flash
> device is to be made accessible from the LED subsystem, then it
> should be also compatible with led-triggers. Some of led-triggers
> call brightness_set op from the timer irq context and thus no
> locking in the callback can occur. This requirement cannot be
> met i.e. if i2c bus is to be used. This is probably the primary
> reason for scheduling work queue tasks in brightness_set op.
> 
> Having the above in mind, setting a brightness in a work queue
> task must be possible for all LED Class Flash drivers, regardless
> whether related devices have fast or slow controller.
> 
> Let's recap the cost of possible solutions then:
> 
> 1) Moving the work queues to the LED framework
> 
>   - it would probably require extending led_set_brightness and
>     __led_set_brightness functions by a parameter indicating whether it
>     should call brightness_set op in the work queue task or directly;
>   - all existing triggers would have to be updated accordingly;
>   - work queues would have to be removed from all the LED drivers;
> 
> 2) adding led_set_torch_brightness API
> 
>   - no modifications in existing drivers and triggers would be required
>   - instead, only the modifications from the discussed patch would
>     be required
> 
> Solution 1 looks cleaner but requires much more modifications.

How about a combination of the two, i.e. option 1 with the old op remaining
there for compatibility with the old drivers (with a comment telling it's
deprecated)?

This way new drivers will benefit from having to implement this just once,
and modifications to the existing drivers could be left for later. The
downside is that any old drivers wouldn't get V4L2 flash API but that's
entirely acceptable in my opinion since these would hardly be needed in use
cases that would benefit from V4L2 flash API.

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [PATCH/RFC v4 15/21] media: Add registration helpers for V4L2 flash
  2014-07-21 11:12   ` Sakari Ailus
@ 2014-08-04 14:43     ` Jacek Anaszewski
  2014-08-11 12:26       ` Sakari Ailus
  0 siblings, 1 reply; 55+ messages in thread
From: Jacek Anaszewski @ 2014-08-04 14:43 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie, Hans Verkuil

Hi Sakari,

Thanks for the review.

On 07/21/2014 01:12 PM, Sakari Ailus wrote:
> Hi Jacek,
>
> Jacek Anaszewski wrote:
>> 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
>> support for V4L2 Flash sub-devices 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>
>> Cc: Sakari Ailus <sakari.ailus@iki.fi>
>> Cc: Hans Verkuil <hans.verkuil@cisco.com>
>> ---
>>   drivers/media/v4l2-core/Kconfig      |   11 +
>>   drivers/media/v4l2-core/Makefile     |    2 +
>>   drivers/media/v4l2-core/v4l2-flash.c |  580 ++++++++++++++++++++++++++++++++++
>>   include/media/v4l2-flash.h           |  137 ++++++++
>>   4 files changed, 730 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 9ca0f8d..3ae3f0f 100644
>> --- a/drivers/media/v4l2-core/Kconfig
>> +++ b/drivers/media/v4l2-core/Kconfig
>> @@ -35,6 +35,17 @@ config V4L2_MEM2MEM_DEV
>>           tristate
>>           depends on VIDEOBUF2_CORE
>>
>> +# Used by LED subsystem flash drivers
>> +config V4L2_FLASH_LED_CLASS
>> +	bool "Enable support for Flash sub-devices"
>> +	depends on VIDEO_V4L2_SUBDEV_API
>> +	depends on LEDS_CLASS_FLASH
>> +	---help---
>> +	  Say Y here to enable support for Flash sub-devices, which allow
>> +	  to control LED class devices with use of V4L2 Flash controls.
>> +
>> +	  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 63d29f2..44e858c 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_LED_CLASS) += 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..21080c6
>> --- /dev/null
>> +++ b/drivers/media/v4l2-core/v4l2-flash.c
>> @@ -0,0 +1,580 @@
>> +/*
>> + * V4L2 Flash LED sub-device 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 <linux/led-class-flash.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of_led_flash_manager.h>
>> +#include <linux/slab.h>
>> +#include <media/v4l2-flash.h>
>> +
>> +#define call_flash_op(v4l2_flash, op, args...)			\
>> +		(v4l2_flash->ops->op  ?				\
>> +			v4l2_flash->ops->op(args) :		\
>> +			-EINVAL)
>> +
>> +static struct v4l2_device *v4l2_dev;
>> +static int registered_flashes;
>> +
>> +static inline enum led_brightness v4l2_flash_intensity_to_led_brightness(
>> +					struct v4l2_ctrl_config *config,
>> +					s32 intensity)
>> +{
>> +	return ((intensity - config->min) / config->step) + 1;
>> +}
>> +
>> +static inline s32 v4l2_flash_led_brightness_to_intensity(
>> +					struct v4l2_ctrl_config *config,
>> +					enum led_brightness brightness)
>> +{
>> +	return ((brightness - 1) * config->step) + config->min;
>> +}
>> +
>> +static int v4l2_flash_g_volatile_ctrl(struct v4l2_ctrl *c)
>> +
>> +{
>> +	struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c);
>> +	struct led_classdev_flash *flash = v4l2_flash->flash;
>> +	struct led_classdev *led_cdev = &flash->led_cdev;
>> +	struct v4l2_flash_ctrl_config *config = &v4l2_flash->config;
>> +	struct v4l2_flash_ctrl *ctrl = &v4l2_flash->ctrl;
>> +	bool is_strobing;
>> +	int ret;
>> +
>> +	switch (c->id) {
>> +	case V4L2_CID_FLASH_TORCH_INTENSITY:
>> +		/*
>> +		 * Update torch brightness only if in TORCH_MODE,
>> +		 * as otherwise brightness_update op returns 0,
>> +		 * which would spuriously inform user space that
>> +		 * V4L2_CID_FLASH_TORCH_INTENSITY control value
>> +		 * has changed.
>> +		 */
>> +		if (ctrl->led_mode->val == V4L2_FLASH_LED_MODE_TORCH) {
>> +			ret = call_flash_op(v4l2_flash, torch_brightness_update,
>> +							led_cdev);
>> +			if (ret < 0)
>> +				return ret;
>> +			ctrl->torch_intensity->val =
>> +				v4l2_flash_led_brightness_to_intensity(
>> +						&config->torch_intensity,
>> +						led_cdev->brightness);
>> +		}
>> +		return 0;
>> +	case V4L2_CID_FLASH_INTENSITY:
>> +		ret = call_flash_op(v4l2_flash, flash_brightness_update,
>> +					flash);
>> +		if (ret < 0)
>> +			return ret;
>> +		/* no conversion is needed */
>> +		c->val = flash->brightness.val;
>> +		return 0;
>> +	case V4L2_CID_FLASH_INDICATOR_INTENSITY:
>> +		ret = call_flash_op(v4l2_flash, indicator_brightness_update,
>> +						flash);
>> +		if (ret < 0)
>> +			return ret;
>> +		/* no conversion is needed */
>> +		c->val = flash->indicator_brightness->val;
>> +		return 0;
>> +	case V4L2_CID_FLASH_STROBE_STATUS:
>> +		ret = call_flash_op(v4l2_flash, strobe_get, flash,
>> +							&is_strobing);
>> +		if (ret < 0)
>> +			return ret;
>> +		c->val = is_strobing;
>> +		return 0;
>> +	case V4L2_CID_FLASH_FAULT:
>> +		/* led faults map directly to V4L2 flash faults */
>> +		ret = call_flash_op(v4l2_flash, fault_get, flash, &c->val);
>> +		return ret;
>> +	case V4L2_CID_FLASH_STROBE_SOURCE:
>> +		c->val = flash->external_strobe;
>> +		return 0;
>> +	case V4L2_CID_FLASH_STROBE_PROVIDER:
>> +		c->val = flash->strobe_provider_id;
>> +		return 0;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static int v4l2_flash_s_ctrl(struct v4l2_ctrl *c)
>> +{
>> +	struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c);
>> +	struct led_classdev_flash *flash = v4l2_flash->flash;
>> +	struct v4l2_flash_ctrl *ctrl = &v4l2_flash->ctrl;
>> +	struct v4l2_flash_ctrl_config *config = &v4l2_flash->config;
>> +	enum led_brightness torch_brightness;
>> +	bool external_strobe;
>> +	int ret;
>> +
>> +	switch (c->id) {
>> +	case V4L2_CID_FLASH_LED_MODE:
>> +		switch (c->val) {
>> +		case V4L2_FLASH_LED_MODE_NONE:
>> +			call_flash_op(v4l2_flash, torch_brightness_set,
>> +							&flash->led_cdev, 0);
>> +			return call_flash_op(v4l2_flash, strobe_set, flash,
>> +							false);
>> +		case V4L2_FLASH_LED_MODE_FLASH:
>> +			/* Turn off torch LED */
>> +			call_flash_op(v4l2_flash, torch_brightness_set,
>> +							&flash->led_cdev, 0);
>> +			external_strobe = (ctrl->source->val ==
>> +					V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
>> +			return call_flash_op(v4l2_flash, external_strobe_set,
>> +						flash, external_strobe);
>> +		case V4L2_FLASH_LED_MODE_TORCH:
>> +			/* Stop flash strobing */
>> +			ret = call_flash_op(v4l2_flash, strobe_set, flash,
>> +							false);
>> +			if (ret)
>> +				return ret;
>> +
>> +			torch_brightness =
>> +				v4l2_flash_intensity_to_led_brightness(
>> +						&config->torch_intensity,
>> +						ctrl->torch_intensity->val);
>> +			call_flash_op(v4l2_flash, torch_brightness_set,
>> +					&flash->led_cdev, torch_brightness);
>> +			return ret;
>> +		}
>> +		break;
>> +	case V4L2_CID_FLASH_STROBE_SOURCE:
>> +		external_strobe = (c->val == V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
>
> Is the external_strobe argument match exactly to the strobe source
> control? You seem to assume that in g_volatile_ctrl() above. I think
> having it the either way is fine but not both. :-)

The STROBE_SOURCE_EXTERNAL control state is volatile if a flash device
depends on muxes that route strobe signals to more then one flash
device. In such a case it behaves similarly to FLASH_STROBE control,
i.e. it activates external strobe only for the flash timeout period.
I touched this issue in the cover letter of this patch series,
paragraph 2.

>
>> +		return call_flash_op(v4l2_flash, external_strobe_set, flash,
>> +							external_strobe);
>> +	case V4L2_CID_FLASH_STROBE:
>> +		if (ctrl->led_mode->val != V4L2_FLASH_LED_MODE_FLASH ||
>> +		    ctrl->source->val != V4L2_FLASH_STROBE_SOURCE_SOFTWARE)
>> +			return -EINVAL;
>> +		return call_flash_op(v4l2_flash, strobe_set, flash, true);
>> +	case V4L2_CID_FLASH_STROBE_STOP:
>
> Should we check the flash mode here? I guess so. How about strobe source
> as well?

Good point.

>> +		return call_flash_op(v4l2_flash, strobe_set, flash, false);
>> +	case V4L2_CID_FLASH_TIMEOUT:
>> +		return call_flash_op(v4l2_flash, timeout_set, flash, c->val);
>> +	case V4L2_CID_FLASH_INTENSITY:
>> +		/* no conversion is needed */
>> +		return call_flash_op(v4l2_flash, flash_brightness_set, flash,
>> +								c->val);
>> +	case V4L2_CID_FLASH_INDICATOR_INTENSITY:
>> +		/* no conversion is needed */
>> +		return call_flash_op(v4l2_flash, indicator_brightness_set,
>> +						flash, c->val);
>> +	case V4L2_CID_FLASH_TORCH_INTENSITY:
>> +		/*
>> +		 * If not in MODE_TORCH don't call led-class brightness_set
>> +		 * op, as it would result in turning the torch led on.
>> +		 * Instead the value is cached only and will be written
>> +		 * to the device upon transition to MODE_TORCH.
>> +		 */
>> +		if (ctrl->led_mode->val == V4L2_FLASH_LED_MODE_TORCH) {
>> +			torch_brightness =
>> +				v4l2_flash_intensity_to_led_brightness(
>> +						&config->torch_intensity,
>> +						ctrl->torch_intensity->val);
>> +			call_flash_op(v4l2_flash, torch_brightness_set,
>> +					&flash->led_cdev, torch_brightness);
>> +		}
>> +		return 0;
>> +	case V4L2_CID_FLASH_STROBE_PROVIDER:
>> +		flash->strobe_provider_id = c->val;
>> +		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_strobe_providers_menu(struct v4l2_flash *v4l2_flash)
>> +{
>> +	struct led_classdev_flash *flash = v4l2_flash->flash;
>> +	struct led_flash_strobe_provider *provider;
>> +	struct v4l2_ctrl *ctrl;
>> +	int i = 0;
>> +
>> +	v4l2_flash->strobe_providers_menu =
>> +			kzalloc(sizeof(char *) * (flash->num_strobe_providers),
>> +					GFP_KERNEL);
>> +	if (!v4l2_flash->strobe_providers_menu)
>> +		return -ENOMEM;
>> +
>> +	list_for_each_entry(provider, &flash->strobe_providers, list)
>> +		v4l2_flash->strobe_providers_menu[i++] =
>> +						(char *) provider->name;
>> +
>> +	ctrl = v4l2_ctrl_new_std_menu_items(
>> +		&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
>> +		V4L2_CID_FLASH_STROBE_PROVIDER,
>> +		flash->num_strobe_providers - 1,
>> +		0, 0,
>> +		(const char * const *) v4l2_flash->strobe_providers_menu);
>> +
>> +	if (ctrl)
>> +		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
>> +
>> +	return 0;
>> +}
>> +
>> +static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash)
>> +
>> +{
>> +	struct led_classdev_flash *flash = v4l2_flash->flash;
>> +	const struct led_flash_ops *flash_ops = flash->ops;
>> +	struct led_classdev *led_cdev = &flash->led_cdev;
>> +	struct v4l2_flash_ctrl_config *config = &v4l2_flash->config;
>> +	struct v4l2_ctrl *ctrl;
>> +	struct v4l2_ctrl_config *ctrl_cfg;
>> +	bool has_flash = led_cdev->flags & LED_DEV_CAP_FLASH;
>> +	bool has_indicator = led_cdev->flags & LED_DEV_CAP_INDICATOR;
>> +	bool has_strobe_providers = (flash->num_strobe_providers > 1);
>> +	unsigned int mask;
>> +	int ret, max, num_ctrls;
>> +
>> +	num_ctrls = has_flash ? 5 : 2;
>> +	if (has_flash) {
>> +		if (flash_ops->flash_brightness_set)
>> +			++num_ctrls;
>> +		if (flash_ops->timeout_set)
>> +			++num_ctrls;
>> +		if (flash_ops->strobe_get)
>> +			++num_ctrls;
>> +		if (has_indicator)
>> +			++num_ctrls;
>> +		if (config->flash_faults)
>> +			++num_ctrls;
>> +		if (has_strobe_providers)
>> +			++num_ctrls;
>> +	}
>> +
>> +	v4l2_ctrl_handler_init(&v4l2_flash->hdl, num_ctrls);
>> +
>> +	mask = 1 << V4L2_FLASH_LED_MODE_NONE |
>> +	       1 << V4L2_FLASH_LED_MODE_TORCH;
>> +	if (flash)
>> +		mask |= 1 << V4L2_FLASH_LED_MODE_FLASH;
>> +
>> +	/* Configure FLASH_LED_MODE ctrl */
>> +	v4l2_flash->ctrl.led_mode = v4l2_ctrl_new_std_menu(
>> +			&v4l2_flash->hdl,
>> +			&v4l2_flash_ctrl_ops, V4L2_CID_FLASH_LED_MODE,
>> +			V4L2_FLASH_LED_MODE_TORCH, ~mask,
>> +			V4L2_FLASH_LED_MODE_NONE);
>> +
>> +	/* Configure TORCH_INTENSITY ctrl */
>> +	ctrl_cfg = &config->torch_intensity;
>> +	ctrl = v4l2_ctrl_new_std(&v4l2_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;
>> +	v4l2_flash->ctrl.torch_intensity = ctrl;
>> +
>> +	if (has_flash) {
>> +		/* Configure FLASH_STROBE_SOURCE ctrl */
>> +		mask = 1 << V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
>> +
>> +		if (flash->has_external_strobe) {
>> +			mask |= 1 << V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
>> +			max = V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
>> +		} else {
>> +			max = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
>> +		}
>> +
>> +		v4l2_flash->ctrl.source = v4l2_ctrl_new_std_menu(
>> +					&v4l2_flash->hdl,
>> +					&v4l2_flash_ctrl_ops,
>> +					V4L2_CID_FLASH_STROBE_SOURCE,
>> +					max,
>> +					~mask,
>> +					V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
>> +		if (v4l2_flash->ctrl.source)
>> +			v4l2_flash->ctrl.source->flags |=
>> +						V4L2_CTRL_FLAG_VOLATILE;
>> +
>> +		/* Configure FLASH_STROBE ctrl */
>> +		ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
>> +					  V4L2_CID_FLASH_STROBE, 0, 1, 1, 0);
>> +
>> +		/* Configure FLASH_STROBE_STOP ctrl */
>> +		ctrl = v4l2_ctrl_new_std(&v4l2_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(&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
>> +					 V4L2_CID_FLASH_STROBE_STATUS,
>> +					 0, 1, 1, 1);
>
> I think you only should implement the strobe status control if you
> really can know it, i.e. have strobe_get().

Of course, thanks.

>> +		if (flash_ops->strobe_get)
>> +			if (ctrl)
>> +				ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
>> +					       V4L2_CTRL_FLAG_READ_ONLY;
>> +
>> +		if (flash_ops->timeout_set) {
>> +			/* Configure FLASH_TIMEOUT ctrl */
>> +			ctrl_cfg = &config->flash_timeout;
>> +			ctrl = v4l2_ctrl_new_std(
>> +					&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
>> +					V4L2_CID_FLASH_TIMEOUT, ctrl_cfg->min,
>> +					ctrl_cfg->max, ctrl_cfg->step,
>> +					ctrl_cfg->def);
>> +			if (ctrl)
>> +				ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
>> +		}
>> +
>> +		if (flash_ops->flash_brightness_set) {
>> +			/* Configure FLASH_INTENSITY ctrl */
>> +			ctrl_cfg = &config->flash_intensity;
>> +			ctrl = v4l2_ctrl_new_std(
>> +					&v4l2_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->flash_faults) {
>> +			/* Configure FLASH_FAULT ctrl */
>> +			ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl,
>> +						 &v4l2_flash_ctrl_ops,
>> +						 V4L2_CID_FLASH_FAULT, 0,
>> +						 config->flash_faults,
>> +						 0, 0);
>> +			if (ctrl)
>> +				ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
>> +					       V4L2_CTRL_FLAG_READ_ONLY;
>> +		}
>> +		if (has_indicator) {
>> +			/* Configure FLASH_INDICATOR_INTENSITY ctrl */
>> +			ctrl_cfg = &config->indicator_intensity;
>> +			ctrl = v4l2_ctrl_new_std(
>> +					&v4l2_flash->hdl, &v4l2_flash_ctrl_ops,
>> +					V4L2_CID_FLASH_INDICATOR_INTENSITY,
>> +					ctrl_cfg->min, ctrl_cfg->max,
>> +					ctrl_cfg->step, ctrl_cfg->def);
>> +			if (ctrl)
>> +				ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
>> +		}
>
> Could you implement the above in a loop? You're essentially repeating
> the same but with different parameters.

Sure.

>> +		if (has_strobe_providers) {
>> +			/* Configure V4L2_CID_FLASH_STROBE_PROVIDERS ctrl */
>> +			ret = v4l2_flash_init_strobe_providers_menu(v4l2_flash);
>> +			if (ret < 0)
>> +				goto error_free_handler;
>> +		}
>> +	}
>> +
>> +	if (v4l2_flash->hdl.error) {
>> +		ret = v4l2_flash->hdl.error;
>> +		goto error_free_handler;
>> +	}
>> +
>> +	ret = v4l2_ctrl_handler_setup(&v4l2_flash->hdl);
>> +	if (ret < 0)
>> +		goto error_free_handler;
>> +
>> +	v4l2_flash->sd.ctrl_handler = &v4l2_flash->hdl;
>> +
>> +	return 0;
>> +
>> +error_free_handler:
>> +	v4l2_ctrl_handler_free(&v4l2_flash->hdl);
>> +	return ret;
>> +}
>> +
>> +/*
>> + * V4L2 subdev internal operations
>> + */
>> +
>> +static int v4l2_flash_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
>> +{
>> +	struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd);
>> +	struct led_classdev_flash *flash = v4l2_flash->flash;
>> +	struct led_classdev *led_cdev = &flash->led_cdev;
>> +
>> +	mutex_lock(&led_cdev->led_lock);
>> +	call_flash_op(v4l2_flash, sysfs_lock, led_cdev);
>
> What if you have the device open through multiple file handles? I
> believe v4l2_subdev_fh_is_singular(&fh->vfh) would prove helpful here.

I assume you propose to implement such a function? I don't see it in
the mainline kernel.

>> +	mutex_unlock(&led_cdev->led_lock);
>> +
>> +	return 0;
>> +}
>> +
>> +static int v4l2_flash_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
>> +{
>> +	struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd);
>> +	struct led_classdev_flash *flash = v4l2_flash->flash;
>> +	struct led_classdev *led_cdev = &flash->led_cdev;
>> +
>> +	mutex_lock(&led_cdev->led_lock);
>> +	call_flash_op(v4l2_flash, sysfs_unlock, led_cdev);
>> +	mutex_unlock(&led_cdev->led_lock);
>> +
>> +	return 0;
>> +}
>> +
>> +int v4l2_flash_register(struct v4l2_flash *v4l2_flash)
>
> Do you expect to call this from elsewhere than this file?

No, I just forgot about 'static' keyword here :)

>> +{
>> +	struct led_classdev_flash *flash = v4l2_flash->flash;
>> +	struct led_classdev *led_cdev = &flash->led_cdev;
>> +	int ret;
>> +
>> +	if (!v4l2_dev) {
>> +		v4l2_dev = kzalloc(sizeof(*v4l2_dev), GFP_KERNEL);
>> +		if (!v4l2_dev)
>> +			return -ENOMEM;
>> +
>> +		strlcpy(v4l2_dev->name, "v4l2-flash-manager",
>> +						sizeof(v4l2_dev->name));
>> +		ret = v4l2_device_register(NULL, v4l2_dev);
>> +		if (ret < 0) {
>> +			dev_err(led_cdev->dev->parent,
>> +				 "Failed to register v4l2_device: %d\n", ret);
>> +			goto err_v4l2_device_register;
>> +		}
>> +	}
>> +
>> +	ret = v4l2_device_register_subdev(v4l2_dev, &v4l2_flash->sd);
>> +	if (ret < 0) {
>> +		dev_err(led_cdev->dev->parent,
>> +			 "Failed to register v4l2_subdev: %d\n", ret);
>> +		goto err_v4l2_device_register;
>> +	}
>> +
>> +	ret = v4l2_device_register_subdev_node(&v4l2_flash->sd, v4l2_dev);
>> +	if (ret < 0) {
>> +		dev_err(led_cdev->dev->parent,
>> +			 "Failed to register v4l2_subdev node: %d\n", ret);
>> +		goto err_register_subdev_node;
>> +	}
>
> This way you can create a V4L2 sub-device node. However, flash devices
> are seldom alone in the system. They are physically close to a sensor,
> and this connection is shown in the Media controller interface. This
> means that the flash sub-device (entity) should be part of the Media
> device created by the driver in control of it. This can be achieved by
> the master driver creating the sub-device. You should register an async
> sub-device here.
>
> This results in the sub-device not being registered if there's no such
> master driver, but I wouldn't expect that to be an issue since the V4L2
> flash API is mostly relevant in such cases.

I addressed this issue in the cover letter of this patch series,
paragraph 1. If strobe signals are routed to a flash device through
a multiplexer then assignment of the related V4L2 Flash sub-device
to a particular media controller may dynamically change in time.
Actually, if a flash device can be shared between media systems
(i.e. it can be configured to react on strobe signals from different
camera sensors, basing on muxes configuration), than it becomes
bound to a particular media system only for the time of strobing.
Please refer to [1].

>> +	++registered_flashes;
>> +
>> +	return 0;
>> +
>> +err_register_subdev_node:
>> +	v4l2_device_unregister_subdev(&v4l2_flash->sd);
>> +err_v4l2_device_register:
>> +	kfree(v4l2_flash->strobe_providers_menu);
>> +	if (v4l2_dev && registered_flashes == 0) {
>> +		v4l2_device_unregister(v4l2_dev);
>> +		kfree(v4l2_dev);
>> +		v4l2_dev = NULL;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static void v4l2_flash_unregister(struct v4l2_flash *v4l2_flash)
>> +{
>> +	if (registered_flashes == 0)
>> +		return;
>> +
>> +	v4l2_device_unregister_subdev(&v4l2_flash->sd);
>> +
>> +	--registered_flashes;
>> +
>> +	if (registered_flashes == 0) {
>> +		v4l2_device_unregister(v4l2_dev);
>> +		kfree(v4l2_dev);
>> +		v4l2_dev = NULL;
>> +	}
>> +}
>> +
>> +static const struct v4l2_subdev_internal_ops v4l2_flash_subdev_internal_ops = {
>> +	.open = v4l2_flash_open,
>> +	.close = v4l2_flash_close,
>> +};
>> +
>> +static const struct v4l2_subdev_core_ops v4l2_flash_core_ops = {
>> +	.queryctrl = v4l2_subdev_queryctrl,
>> +	.querymenu = v4l2_subdev_querymenu,
>> +};
>> +
>> +static const struct v4l2_subdev_ops v4l2_flash_subdev_ops = {
>> +	.core = &v4l2_flash_core_ops,
>> +};
>> +
>> +int v4l2_flash_init(struct led_classdev_flash *flash,
>> +		    struct v4l2_flash_ctrl_config *config,
>> +		    const struct v4l2_flash_ops *flash_ops,
>> +		    struct v4l2_flash **out_flash)
>> +{
>> +	struct v4l2_flash *v4l2_flash;
>> +	struct led_classdev *led_cdev = &flash->led_cdev;
>> +	struct v4l2_subdev *sd;
>> +	int ret;
>> +
>> +	if (!flash || !config || !out_flash)
>> +		return -EINVAL;
>> +
>> +	v4l2_flash = kzalloc(sizeof(*v4l2_flash), GFP_KERNEL);
>> +	if (!v4l2_flash)
>> +		return -ENOMEM;
>> +
>> +	sd = &v4l2_flash->sd;
>> +	v4l2_flash->flash = flash;
>> +	v4l2_flash->ops = flash_ops;
>> +	sd->dev = led_cdev->dev->parent;
>> +	v4l2_subdev_init(sd, &v4l2_flash_subdev_ops);
>> +	sd->internal_ops = &v4l2_flash_subdev_internal_ops;
>> +	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>> +	snprintf(sd->name, sizeof(sd->name), led_cdev->name);
>> +
>> +	v4l2_flash->config = *config;
>> +	ret = v4l2_flash_init_controls(v4l2_flash);
>> +	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_flash_register(v4l2_flash);
>> +	if (ret < 0)
>> +		goto err_init_entity;
>> +
>> +	*out_flash = v4l2_flash;
>
> How about returning the pointer instead?

OK.

>> +	return 0;
>> +
>> +err_init_entity:
>> +	media_entity_cleanup(&sd->entity);
>> +err_init_controls:
>> +	v4l2_ctrl_handler_free(sd->ctrl_handler);
>> +	kfree(v4l2_flash);
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(v4l2_flash_init);
>> +
>> +void v4l2_flash_release(struct v4l2_flash *v4l2_flash)
>> +{
>> +	if (!v4l2_flash)
>> +		return;
>> +
>> +	v4l2_flash_unregister(v4l2_flash);
>> +	v4l2_ctrl_handler_free(v4l2_flash->sd.ctrl_handler);
>> +	media_entity_cleanup(&v4l2_flash->sd.entity);
>> +	kfree(v4l2_flash->strobe_providers_menu);
>> +	kfree(v4l2_flash);
>> +}
>> +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..effa46b
>> --- /dev/null
>> +++ b/include/media/v4l2-flash.h
>> @@ -0,0 +1,137 @@
>> +/*
>> + * V4L2 Flash LED sub-device 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 <media/v4l2-ioctl.h>
>> +
>> +struct led_classdev_flash;
>> +struct led_classdev;
>> +enum led_brightness;
>> +
>> +struct v4l2_flash_ops {
>> +	int (*torch_brightness_set)(struct led_classdev *led_cdev,
>> +					enum led_brightness brightness);
>> +	int (*torch_brightness_update)(struct led_classdev *led_cdev);
>> +	int (*flash_brightness_set)(struct led_classdev_flash *flash,
>> +					u32 brightness);
>> +	int (*flash_brightness_update)(struct led_classdev_flash *flash);
>> +	int (*strobe_set)(struct led_classdev_flash *flash, bool state);
>> +	int (*strobe_get)(struct led_classdev_flash *flash, bool *state);
>> +	int (*timeout_set)(struct led_classdev_flash *flash, u32 timeout);
>> +	int (*indicator_brightness_set)(struct led_classdev_flash *flash,
>> +					u32 brightness);
>> +	int (*indicator_brightness_update)(struct led_classdev_flash *flash);
>> +	int (*external_strobe_set)(struct led_classdev_flash *flash,
>> +					bool enable);
>> +	int (*fault_get)(struct led_classdev_flash *flash, u32 *fault);
>> +	void (*sysfs_lock)(struct led_classdev *led_cdev);
>> +	void (*sysfs_unlock)(struct led_classdev *led_cdev);
>
> These functions are not driver specific and there's going to be just one
> implementation (I suppose). Could you refresh my memory regarding why
> the LED framework functions aren't called directly?

These ops are required to make possible building led-class-flash as a 
kernel module.

>> +};
>> +
>> +/**
>> + * struct v4l2_flash_ctrl - controls that define the sub-dev's state
>> + * @source:		V4L2_CID_FLASH_STROBE_SOURCE control
>> + * @led_mode:		V4L2_CID_FLASH_LED_MODE control
>> + * @torch_intensity:	V4L2_CID_FLASH_TORCH_INTENSITY control
>> + */
>> +struct v4l2_flash_ctrl {
>> +	struct v4l2_ctrl *source;
>> +	struct v4l2_ctrl *led_mode;
>> +	struct v4l2_ctrl *torch_intensity;
>> +};
>> +
>> +/**
>> + * struct v4l2_flash_ctrl_config - V4L2 Flash controls initialization data
>> + * @torch_intensity:		V4L2_CID_FLASH_TORCH_INTENSITY constraints
>> + * @flash_intensity:		V4L2_CID_FLASH_INTENSITY constraints
>> + * @indicator_intensity:	V4L2_CID_FLASH_INDICATOR_INTENSITY constraints
>> + * @flash_timeout:		V4L2_CID_FLASH_TIMEOUT constraints
>> + * @flash_fault:		possible flash faults
>> + */
>> +struct v4l2_flash_ctrl_config {
>> +	struct v4l2_ctrl_config torch_intensity;
>> +	struct v4l2_ctrl_config flash_intensity;
>> +	struct v4l2_ctrl_config indicator_intensity;
>> +	struct v4l2_ctrl_config flash_timeout;
>> +	u32 flash_faults;
>> +};
>> +
>> +/**
>> + * struct v4l2_flash - Flash sub-device context
>> + * @flash:		LED Flash Class device controlled by this sub-device
>> + * @ops:		LED Flash Class device ops
>> + * @sd:			V4L2 sub-device
>> + * @hdl:		flash controls handler
>> + * @ctrl:		state defining controls
>> + * @config:		V4L2 Flash controlsrconfiguration data
>> + * @software_strobe_gates: route to the software strobe signal
>> + * @external_strobe_gates: route to the external strobe signal
>> + * @sensors:		available external strobe sources
>> + */
>> +struct v4l2_flash {
>> +	struct led_classdev_flash *flash;
>> +	const struct v4l2_flash_ops *ops;
>> +
>> +	struct v4l2_subdev sd;
>> +	struct v4l2_ctrl_handler hdl;
>> +	struct v4l2_flash_ctrl ctrl;
>> +	struct v4l2_flash_ctrl_config config;
>> +	char **strobe_providers_menu;
>> +};
>> +
>> +static inline struct v4l2_flash *v4l2_subdev_to_v4l2_flash(
>> +							struct v4l2_subdev *sd)
>> +{
>> +	return container_of(sd, struct v4l2_flash, sd);
>> +}
>> +
>> +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_LED_CLASS
>> +/**
>> + * v4l2_flash_init - initialize V4L2 flash led sub-device
>> + * @led_fdev:	the LED Flash Class device to wrap
>> + * @config:	initialization data for V4L2 Flash controls
>> + * @flash_ops:	V4L2 Flash device ops
>> + * @out_flash:	handler to the new V4L2 Flash device
>> + *
>> + * Create V4L2 subdev wrapping given LED subsystem device.
>> +
>> + * Returns: 0 on success or negative error value on failure
>> + */
>> +int v4l2_flash_init(struct led_classdev_flash *led_fdev,
>> +		    struct v4l2_flash_ctrl_config *config,
>> +		    const struct v4l2_flash_ops *flash_ops,
>> +		    struct v4l2_flash **out_flash);
>> +
>> +/**
>> + * v4l2_flash_release - release V4L2 Flash sub-device
>> + * @flash: the V4L2 Flash device to release
>> + *
>> + * Release V4L2 flash led subdev.
>> + */
>> +void v4l2_flash_release(struct v4l2_flash *v4l2_flash);
>> +
>> +#else
>> +#define v4l2_flash_init(led_cdev, config, flash_ops, out_flash) (0)
>> +#define v4l2_flash_release(v4l2_flash)
>> +#endif /* CONFIG_V4L2_FLASH_LED_CLASS */
>> +
>> +#endif /* _V4L2_FLASH_H */
>>
>

Best Regards,
Jacek Anaszewski

[1] - https://lkml.org/lkml/2014/7/11/942

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

* Re: [PATCH/RFC v4 00/21] LED / flash API integration
  2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
                   ` (21 preceding siblings ...)
  2014-07-16 17:19 ` [PATCH/RFC v4 00/21] LED / flash API integration Bryan Wu
@ 2014-08-06  6:53 ` Sakari Ailus
  2014-08-07  8:21   ` Jacek Anaszewski
  22 siblings, 1 reply; 55+ messages in thread
From: Sakari Ailus @ 2014-08-06  6:53 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie

Hi Jacek,

On Fri, Jul 11, 2014 at 04:04:03PM +0200, Jacek Anaszewski wrote:
...
> 1) Who should register V4L2 Flash sub-device?
> 
> LED Flash Class devices, after introduction of the Flash Manager,
> are not tightly coupled with any media controller. They are maintained
> by the Flash Manager and made available for dynamic assignment to
> any media system they are connected to through multiplexing devices.
> 
> In the proposed rough solution, when support for V4L2 Flash sub-devices
> is enabled, there is a v4l2_device created for them to register in.
> This however implies that V4L2 Flash device will not be available
> in any media controller, which calls its existence into question.
> 
> Therefore I'd like to consult possible ways of solving this issue.
> The option I see is implementing a mechanism for moving V4L2 Flash
> sub-devices between media controllers. A V4L2 Flash sub-device
> would initially be assigned to one media system in the relevant
> device tree binding, but it could be dynamically reassigned to
> the other one. However I'm not sure if media controller design
> is prepared for dynamic modifications of its graph and how many
> modifications in the existing drivers this solution would require.

Do you have a use case where you would need to strobe a flash from multiple
media devices at different times, or is this entirely theoretical? Typically
flash controllers are connected to a single source of hardware strobe (if
there's one) since the flash LEDs are in fact mounted next to a specific
camera sensor.

If this is a real issue the way to solve it would be to have a single media
device instead of many.

> 2) Consequences of locking the Flash Manager during flash strobe
> 
> In case a LED Flash Class device depends on muxes involved in
> routing the other LED Flash Class device's strobe signals,
> the Flash Manager must be locked for the time of strobing
> to prevent reconfiguration of the strobe signal routing
> by the other device.

I wouldn't be concerned of this in particular. It's more important we do
actully have V4L2 flash API supported by LED flash drivers and that they do
implement the API correctly.

It's at least debatable whether you should try to prevent user space from
doing silly things or not. With complex devices it may relatively easily
lead to wrecking havoc with actual use cases which we certainly do not want.

In this case, if you just prevent changing the routing (do you have a use
case for it?) while strobing, someone else could still change the routing
just before you strobe.

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [PATCH/RFC v4 00/21] LED / flash API integration
  2014-08-06  6:53 ` Sakari Ailus
@ 2014-08-07  8:21   ` Jacek Anaszewski
  2014-08-07  8:31     ` Jacek Anaszewski
  2014-08-14  5:03     ` Sakari Ailus
  0 siblings, 2 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-08-07  8:21 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie

Hi Sakari,

On 08/06/2014 08:53 AM, Sakari Ailus wrote:
> Hi Jacek,
>
> On Fri, Jul 11, 2014 at 04:04:03PM +0200, Jacek Anaszewski wrote:
> ...
>> 1) Who should register V4L2 Flash sub-device?
>>
>> LED Flash Class devices, after introduction of the Flash Manager,
>> are not tightly coupled with any media controller. They are maintained
>> by the Flash Manager and made available for dynamic assignment to
>> any media system they are connected to through multiplexing devices.
>>
>> In the proposed rough solution, when support for V4L2 Flash sub-devices
>> is enabled, there is a v4l2_device created for them to register in.
>> This however implies that V4L2 Flash device will not be available
>> in any media controller, which calls its existence into question.
>>
>> Therefore I'd like to consult possible ways of solving this issue.
>> The option I see is implementing a mechanism for moving V4L2 Flash
>> sub-devices between media controllers. A V4L2 Flash sub-device
>> would initially be assigned to one media system in the relevant
>> device tree binding, but it could be dynamically reassigned to
>> the other one. However I'm not sure if media controller design
>> is prepared for dynamic modifications of its graph and how many
>> modifications in the existing drivers this solution would require.
>
> Do you have a use case where you would need to strobe a flash from multiple
> media devices at different times, or is this entirely theoretical? Typically
> flash controllers are connected to a single source of hardware strobe (if
> there's one) since the flash LEDs are in fact mounted next to a specific
> camera sensor.

I took into account such arrangements in response to your message [1], 
where you were considering configurations like "one flash but two
cameras", "one camera and two flashes". And you also called for 
proposing generic solution.

One flash and two (or more) cameras case is easily conceivable -
You even mentioned stereo cameras. One camera and many flashes
arrangement might be useful in case of some professional devices which
might be designed so that they would be able to apply different scene
lighting. I haven't heard about such devices, but as you said
such a configuration isn't unthinkable.

> If this is a real issue the way to solve it would be to have a single media
> device instead of many.

I was considering adding media device, that would be a representation
of a flash manager, gathering all the registered flashes. Nonetheless,
finally I came to conclusion that a v4l2-device alone should suffice,
just to provide a Flash Manager representation allowing for v4l2-flash 
sub-devices to register in.
All the features provided by the media device are useless in case
of a set of V4L2 Flash sub-devices. They couldn't have any linkage
in such a device. The only benefit from having media device gathering
V4L2 Flash devices would be possibility of listing them.

>> 2) Consequences of locking the Flash Manager during flash strobe
>>
>> In case a LED Flash Class device depends on muxes involved in
>> routing the other LED Flash Class device's strobe signals,
>> the Flash Manager must be locked for the time of strobing
>> to prevent reconfiguration of the strobe signal routing
>> by the other device.
>
> I wouldn't be concerned of this in particular. It's more important we do
> actully have V4L2 flash API supported by LED flash drivers and that they do
> implement the API correctly.
>
> It's at least debatable whether you should try to prevent user space from
> doing silly things or not. With complex devices it may relatively easily
> lead to wrecking havoc with actual use cases which we certainly do not want.
>
> In this case, if you just prevent changing the routing (do you have a use
> case for it?) while strobing, someone else could still change the routing
> just before you strobe.

Originally I started to implementing this so that strobe signal routing
was altered upon setting strobe source. With such an implementation the 
use case would be as follows:

1. Process 1 sets strobe source to external
2. Process 2 sets strobe source to software
3. Process 1 strobes the flash, unaware that strobe source setting has
    been changed

To avoid such problems I changed the implementation so that the
routing is set in the led_flash_manager_setup_strobe function called
from led_set_flash_strobe and led_set_external_strobe functions.
led_flash_manager_setup_strobe sets strobe signal routing
and strobes the flash under lock and holds it for the flash timeout
period, which prevents spurious reconfiguration.

Nonetheless, I agree that trying to handle this problem is troublesome,
and would affect current V4L2 Flash SPI semantics. If you don't share
my concerns I am happy to leave this locking solution out :)

Best Regards,
Jacek Anaszewski

[1] http://www.spinics.net/lists/linux-leds/msg01617.html

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

* Re: [PATCH/RFC v4 00/21] LED / flash API integration
  2014-08-07  8:21   ` Jacek Anaszewski
@ 2014-08-07  8:31     ` Jacek Anaszewski
  2014-08-14  5:03     ` Sakari Ailus
  1 sibling, 0 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-08-07  8:31 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie

On 08/07/2014 10:21 AM, Jacek Anaszewski wrote:
> Hi Sakari,
>
> On 08/06/2014 08:53 AM, Sakari Ailus wrote:
>> Hi Jacek,
>>
>> On Fri, Jul 11, 2014 at 04:04:03PM +0200, Jacek Anaszewski wrote:
>> ...
>>> 1) Who should register V4L2 Flash sub-device?
>>>
>>> LED Flash Class devices, after introduction of the Flash Manager,
>>> are not tightly coupled with any media controller. They are maintained
>>> by the Flash Manager and made available for dynamic assignment to
>>> any media system they are connected to through multiplexing devices.
>>>
>>> In the proposed rough solution, when support for V4L2 Flash sub-devices
>>> is enabled, there is a v4l2_device created for them to register in.
>>> This however implies that V4L2 Flash device will not be available
>>> in any media controller, which calls its existence into question.
>>>
>>> Therefore I'd like to consult possible ways of solving this issue.
>>> The option I see is implementing a mechanism for moving V4L2 Flash
>>> sub-devices between media controllers. A V4L2 Flash sub-device
>>> would initially be assigned to one media system in the relevant
>>> device tree binding, but it could be dynamically reassigned to
>>> the other one. However I'm not sure if media controller design
>>> is prepared for dynamic modifications of its graph and how many
>>> modifications in the existing drivers this solution would require.
>>
>> Do you have a use case where you would need to strobe a flash from
>> multiple
>> media devices at different times, or is this entirely theoretical?
>> Typically
>> flash controllers are connected to a single source of hardware strobe (if
>> there's one) since the flash LEDs are in fact mounted next to a specific
>> camera sensor.
>
> I took into account such arrangements in response to your message [1],
> where you were considering configurations like "one flash but two
> cameras", "one camera and two flashes". And you also called for
> proposing generic solution.
>
> One flash and two (or more) cameras case is easily conceivable -
> You even mentioned stereo cameras. One camera and many flashes
> arrangement might be useful in case of some professional devices which
> might be designed so that they would be able to apply different scene
> lighting. I haven't heard about such devices, but as you said
> such a configuration isn't unthinkable.
>
>> If this is a real issue the way to solve it would be to have a single
>> media
>> device instead of many.
>
> I was considering adding media device, that would be a representation
> of a flash manager, gathering all the registered flashes. Nonetheless,
> finally I came to conclusion that a v4l2-device alone should suffice,
> just to provide a Flash Manager representation allowing for v4l2-flash
> sub-devices to register in.
> All the features provided by the media device are useless in case
> of a set of V4L2 Flash sub-devices. They couldn't have any linkage
> in such a device. The only benefit from having media device gathering
> V4L2 Flash devices would be possibility of listing them.
>
>>> 2) Consequences of locking the Flash Manager during flash strobe
>>>
>>> In case a LED Flash Class device depends on muxes involved in
>>> routing the other LED Flash Class device's strobe signals,
>>> the Flash Manager must be locked for the time of strobing
>>> to prevent reconfiguration of the strobe signal routing
>>> by the other device.
>>
>> I wouldn't be concerned of this in particular. It's more important we do
>> actully have V4L2 flash API supported by LED flash drivers and that
>> they do
>> implement the API correctly.
>>
>> It's at least debatable whether you should try to prevent user space from
>> doing silly things or not. With complex devices it may relatively easily
>> lead to wrecking havoc with actual use cases which we certainly do not
>> want.
>>
>> In this case, if you just prevent changing the routing (do you have a use
>> case for it?) while strobing, someone else could still change the routing
>> just before you strobe.
>
> Originally I started to implementing this so that strobe signal routing
> was altered upon setting strobe source. With such an implementation the
> use case would be as follows:
>
> 1. Process 1 sets strobe source to external
> 2. Process 2 sets strobe source to software
> 3. Process 1 strobes the flash, unaware that strobe source setting has
>     been changed

It is of course too generic use case. The more specific one could
be the situation when there are many users in the system that
want to take the picture in the same time. It is not valid in
case of mobile devices though.

> To avoid such problems I changed the implementation so that the
> routing is set in the led_flash_manager_setup_strobe function called
> from led_set_flash_strobe and led_set_external_strobe functions.
> led_flash_manager_setup_strobe sets strobe signal routing
> and strobes the flash under lock and holds it for the flash timeout
> period, which prevents spurious reconfiguration.
>
> Nonetheless, I agree that trying to handle this problem is troublesome,
> and would affect current V4L2 Flash SPI semantics. If you don't share
> my concerns I am happy to leave this locking solution out :)
>
> Best Regards,
> Jacek Anaszewski
>
> [1] http://www.spinics.net/lists/linux-leds/msg01617.html


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

* Re: [PATCH/RFC v4 06/21] leds: add API for setting torch brightness
  2014-08-04 12:50       ` Sakari Ailus
@ 2014-08-07 13:12         ` Jacek Anaszewski
  2014-08-14  4:39           ` Sakari Ailus
  0 siblings, 1 reply; 55+ messages in thread
From: Jacek Anaszewski @ 2014-08-07 13:12 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie, Bryan Wu, Richard Purdie

Hi Sakari,

On 08/04/2014 02:50 PM, Sakari Ailus wrote:
> Hi Jacek,
>
> Thank you for your continued efforts on this!
>
> On Mon, Aug 04, 2014 at 02:35:26PM +0200, Jacek Anaszewski wrote:
>> On 07/16/2014 11:54 PM, Sakari Ailus wrote:
>>> Hi Jacek,
>>>
>>> Jacek Anaszewski wrote:
>>> ...
>>>> diff --git a/include/linux/leds.h b/include/linux/leds.h
>>>> index 1a130cc..9bea9e6 100644
>>>> --- a/include/linux/leds.h
>>>> +++ b/include/linux/leds.h
>>>> @@ -44,11 +44,21 @@ struct led_classdev {
>>>>   #define LED_BLINK_ONESHOT_STOP    (1 << 18)
>>>>   #define LED_BLINK_INVERT    (1 << 19)
>>>>   #define LED_SYSFS_LOCK        (1 << 20)
>>>> +#define LED_DEV_CAP_TORCH    (1 << 21)
>>>>
>>>>       /* Set LED brightness level */
>>>>       /* Must not sleep, use a workqueue if needed */
>>>>       void        (*brightness_set)(struct led_classdev *led_cdev,
>>>>                         enum led_brightness brightness);
>>>> +    /*
>>>> +     * Set LED brightness immediately - it is required for flash led
>>>> +     * devices as they require setting torch brightness to have
>>>> immediate
>>>> +     * effect. brightness_set op cannot be used for this purpose because
>>>> +     * the led drivers schedule a work queue task in it to allow for
>>>> +     * being called from led-triggers, i.e. from the timer irq context.
>>>> +     */
>>>
>>> Do we need to classify actual devices based on this? I think it's rather
>>> a different API behaviour between the LED and the V4L2 APIs.
>>>
>>> On devices that are slow to control, the behaviour should be asynchronous
>>> over the LED API and synchronous when accessed through the V4L2 API. How
>>> about implementing the work queue, as I have suggested, in the
>>> framework, so
>>> that individual drivers don't need to care about this and just implement
>>> the
>>> synchronous variant of this op? A flag could be added to distinguish
>>> devices
>>> that are fast so that the work queue isn't needed.
>>>
>>> It'd be nice to avoid individual drivers having to implement multiple
>>> ops to
>>> do the same thing, just for differing user space interfacs.
>>>
>>
>> It is not only the matter of a device controller speed. If a flash
>> device is to be made accessible from the LED subsystem, then it
>> should be also compatible with led-triggers. Some of led-triggers
>> call brightness_set op from the timer irq context and thus no
>> locking in the callback can occur. This requirement cannot be
>> met i.e. if i2c bus is to be used. This is probably the primary
>> reason for scheduling work queue tasks in brightness_set op.
>>
>> Having the above in mind, setting a brightness in a work queue
>> task must be possible for all LED Class Flash drivers, regardless
>> whether related devices have fast or slow controller.
>>
>> Let's recap the cost of possible solutions then:
>>
>> 1) Moving the work queues to the LED framework
>>
>>    - it would probably require extending led_set_brightness and
>>      __led_set_brightness functions by a parameter indicating whether it
>>      should call brightness_set op in the work queue task or directly;
>>    - all existing triggers would have to be updated accordingly;
>>    - work queues would have to be removed from all the LED drivers;
>>
>> 2) adding led_set_torch_brightness API
>>
>>    - no modifications in existing drivers and triggers would be required
>>    - instead, only the modifications from the discussed patch would
>>      be required
>>
>> Solution 1 looks cleaner but requires much more modifications.
>
> How about a combination of the two, i.e. option 1 with the old op remaining
> there for compatibility with the old drivers (with a comment telling it's
> deprecated)?
>
> This way new drivers will benefit from having to implement this just once,
> and modifications to the existing drivers could be left for later.

It's OK for me, but the opinion from the LED side guys is needed here
as well.

> The downside is that any old drivers wouldn't get V4L2 flash API but that's
> entirely acceptable in my opinion since these would hardly be needed in use
> cases that would benefit from V4L2 flash API.

In the version 4 of the patch set I changed the implementation, so that
a flash led driver must call led_classdev_flash_register to get
registered as a LED Flash Class device and v4l2_flash_init to get
V4L2 Flash API. In effect old drivers will have no chance to get V4L2
Flash API either way.

Best Regards,
Jacek Anaszewski

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

* Re: [PATCH/RFC v4 00/21] LED / flash API integration
  2014-07-16 17:21   ` Bryan Wu
@ 2014-08-08  6:43     ` Jacek Anaszewski
  2014-08-08 16:59       ` Bryan Wu
  0 siblings, 1 reply; 55+ messages in thread
From: Jacek Anaszewski @ 2014-08-08  6:43 UTC (permalink / raw)
  To: Bryan Wu
  Cc: Linux LED Subsystem, devicetree, linux-media, lkml,
	Kyungmin Park, b.zolnierkie

Hi Bryan,

On 07/16/2014 07:21 PM, Bryan Wu wrote:
> On Wed, Jul 16, 2014 at 10:19 AM, Bryan Wu <cooloney@gmail.com> wrote:
>> On Fri, Jul 11, 2014 at 7:04 AM, Jacek Anaszewski
>> <j.anaszewski@samsung.com> wrote:
>>> This is is the fourth 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-20140707
>>>
>>
>> I really apologize that I missed your discussion email in my Gmail
>> inbox, it was archived some where. Even in this series some of these
>> patches are archived in different tab.
>>
>> I will start to review and help to push this.
>>
>
> In the mean time, could you please provide an git tree for me to pull?
> It's much easier for me to review in my git.

Few days ago I sent to your private email the path to the git
repository to pull, but I am resending it through the lists to
make sure that it will not get filtered somehow again.

git://linuxtv.org/snawrocki/samsung.git
branch: led_flash_integration_v4

gitweb:
http://git.linuxtv.org/cgit.cgi/snawrocki/samsung.git/log/?h=led_flash_integration_v4

Best Regards,
Jacek Anaszewski

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

* Re: [PATCH/RFC v4 00/21] LED / flash API integration
  2014-08-08  6:43     ` Jacek Anaszewski
@ 2014-08-08 16:59       ` Bryan Wu
  2014-08-11 14:24         ` Jacek Anaszewski
  0 siblings, 1 reply; 55+ messages in thread
From: Bryan Wu @ 2014-08-08 16:59 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: Linux LED Subsystem, devicetree, linux-media, lkml,
	Kyungmin Park, b.zolnierkie

On Thu, Aug 7, 2014 at 11:43 PM, Jacek Anaszewski
<j.anaszewski@samsung.com> wrote:
> Hi Bryan,
>
>
> On 07/16/2014 07:21 PM, Bryan Wu wrote:
>>
>> On Wed, Jul 16, 2014 at 10:19 AM, Bryan Wu <cooloney@gmail.com> wrote:
>>>
>>> On Fri, Jul 11, 2014 at 7:04 AM, Jacek Anaszewski
>>> <j.anaszewski@samsung.com> wrote:
>>>>
>>>> This is is the fourth 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-20140707
>>>>
>>>
>>> I really apologize that I missed your discussion email in my Gmail
>>> inbox, it was archived some where. Even in this series some of these
>>> patches are archived in different tab.
>>>
>>> I will start to review and help to push this.
>>>
>>
>> In the mean time, could you please provide an git tree for me to pull?
>> It's much easier for me to review in my git.
>
>
> Few days ago I sent to your private email the path to the git
> repository to pull, but I am resending it through the lists to
> make sure that it will not get filtered somehow again.
>
>
> git://linuxtv.org/snawrocki/samsung.git
> branch: led_flash_integration_v4
>
> gitweb:
> http://git.linuxtv.org/cgit.cgi/snawrocki/samsung.git/log/?h=led_flash_integration_v4
>

Yes, I got it. I merged some leds core fixing patches from you
recently. So could you please split these big patchset into several
small parts which should be easier for reviewing and discussion.
 - led core fixing
 - led flash core patches
 - led flash manager patches
 - v4l2 led patches
 - Documentations

My plan is to review these and merge them to my -devel branch. Then
invite people for testing.

Thanks,
-Bryan

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

* Re: [PATCH/RFC v4 15/21] media: Add registration helpers for V4L2 flash
  2014-08-04 14:43     ` Jacek Anaszewski
@ 2014-08-11 12:26       ` Sakari Ailus
  2014-08-11 13:27         ` Jacek Anaszewski
  0 siblings, 1 reply; 55+ messages in thread
From: Sakari Ailus @ 2014-08-11 12:26 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie, Hans Verkuil


Hi Jacek,

On Mon, Aug 04, 2014 at 04:43:54PM +0200, Jacek Anaszewski wrote:
> Hi Sakari,
> 
> Thanks for the review.

You're welcome! :)

> On 07/21/2014 01:12 PM, Sakari Ailus wrote:
> >Hi Jacek,
> >
> >Jacek Anaszewski wrote:
> >>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
> >>support for V4L2 Flash sub-devices 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>
> >>Cc: Sakari Ailus <sakari.ailus@iki.fi>
> >>Cc: Hans Verkuil <hans.verkuil@cisco.com>
> >>---
> >>  drivers/media/v4l2-core/Kconfig      |   11 +
> >>  drivers/media/v4l2-core/Makefile     |    2 +
> >>  drivers/media/v4l2-core/v4l2-flash.c |  580 ++++++++++++++++++++++++++++++++++
> >>  include/media/v4l2-flash.h           |  137 ++++++++
> >>  4 files changed, 730 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 9ca0f8d..3ae3f0f 100644
> >>--- a/drivers/media/v4l2-core/Kconfig
> >>+++ b/drivers/media/v4l2-core/Kconfig
> >>@@ -35,6 +35,17 @@ config V4L2_MEM2MEM_DEV
> >>          tristate
> >>          depends on VIDEOBUF2_CORE
> >>
> >>+# Used by LED subsystem flash drivers
> >>+config V4L2_FLASH_LED_CLASS
> >>+	bool "Enable support for Flash sub-devices"
> >>+	depends on VIDEO_V4L2_SUBDEV_API
> >>+	depends on LEDS_CLASS_FLASH
> >>+	---help---
> >>+	  Say Y here to enable support for Flash sub-devices, which allow
> >>+	  to control LED class devices with use of V4L2 Flash controls.
> >>+
> >>+	  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 63d29f2..44e858c 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_LED_CLASS) += 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..21080c6
> >>--- /dev/null
> >>+++ b/drivers/media/v4l2-core/v4l2-flash.c
> >>@@ -0,0 +1,580 @@
> >>+/*
> >>+ * V4L2 Flash LED sub-device 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 <linux/led-class-flash.h>
> >>+#include <linux/mutex.h>
> >>+#include <linux/of_led_flash_manager.h>
> >>+#include <linux/slab.h>
> >>+#include <media/v4l2-flash.h>
> >>+
> >>+#define call_flash_op(v4l2_flash, op, args...)			\
> >>+		(v4l2_flash->ops->op  ?				\
> >>+			v4l2_flash->ops->op(args) :		\
> >>+			-EINVAL)
> >>+
> >>+static struct v4l2_device *v4l2_dev;
> >>+static int registered_flashes;
> >>+
> >>+static inline enum led_brightness v4l2_flash_intensity_to_led_brightness(
> >>+					struct v4l2_ctrl_config *config,
> >>+					s32 intensity)
> >>+{
> >>+	return ((intensity - config->min) / config->step) + 1;
> >>+}
> >>+
> >>+static inline s32 v4l2_flash_led_brightness_to_intensity(
> >>+					struct v4l2_ctrl_config *config,
> >>+					enum led_brightness brightness)
> >>+{
> >>+	return ((brightness - 1) * config->step) + config->min;
> >>+}
> >>+
> >>+static int v4l2_flash_g_volatile_ctrl(struct v4l2_ctrl *c)
> >>+
> >>+{
> >>+	struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c);
> >>+	struct led_classdev_flash *flash = v4l2_flash->flash;
> >>+	struct led_classdev *led_cdev = &flash->led_cdev;
> >>+	struct v4l2_flash_ctrl_config *config = &v4l2_flash->config;
> >>+	struct v4l2_flash_ctrl *ctrl = &v4l2_flash->ctrl;
> >>+	bool is_strobing;
> >>+	int ret;
> >>+
> >>+	switch (c->id) {
> >>+	case V4L2_CID_FLASH_TORCH_INTENSITY:
> >>+		/*
> >>+		 * Update torch brightness only if in TORCH_MODE,
> >>+		 * as otherwise brightness_update op returns 0,
> >>+		 * which would spuriously inform user space that
> >>+		 * V4L2_CID_FLASH_TORCH_INTENSITY control value
> >>+		 * has changed.
> >>+		 */
> >>+		if (ctrl->led_mode->val == V4L2_FLASH_LED_MODE_TORCH) {
> >>+			ret = call_flash_op(v4l2_flash, torch_brightness_update,
> >>+							led_cdev);
> >>+			if (ret < 0)
> >>+				return ret;
> >>+			ctrl->torch_intensity->val =
> >>+				v4l2_flash_led_brightness_to_intensity(
> >>+						&config->torch_intensity,
> >>+						led_cdev->brightness);
> >>+		}
> >>+		return 0;
> >>+	case V4L2_CID_FLASH_INTENSITY:
> >>+		ret = call_flash_op(v4l2_flash, flash_brightness_update,
> >>+					flash);
> >>+		if (ret < 0)
> >>+			return ret;
> >>+		/* no conversion is needed */
> >>+		c->val = flash->brightness.val;
> >>+		return 0;
> >>+	case V4L2_CID_FLASH_INDICATOR_INTENSITY:
> >>+		ret = call_flash_op(v4l2_flash, indicator_brightness_update,
> >>+						flash);
> >>+		if (ret < 0)
> >>+			return ret;
> >>+		/* no conversion is needed */
> >>+		c->val = flash->indicator_brightness->val;
> >>+		return 0;
> >>+	case V4L2_CID_FLASH_STROBE_STATUS:
> >>+		ret = call_flash_op(v4l2_flash, strobe_get, flash,
> >>+							&is_strobing);
> >>+		if (ret < 0)
> >>+			return ret;
> >>+		c->val = is_strobing;
> >>+		return 0;
> >>+	case V4L2_CID_FLASH_FAULT:
> >>+		/* led faults map directly to V4L2 flash faults */
> >>+		ret = call_flash_op(v4l2_flash, fault_get, flash, &c->val);
> >>+		return ret;
> >>+	case V4L2_CID_FLASH_STROBE_SOURCE:
> >>+		c->val = flash->external_strobe;
> >>+		return 0;
> >>+	case V4L2_CID_FLASH_STROBE_PROVIDER:
> >>+		c->val = flash->strobe_provider_id;
> >>+		return 0;
> >>+	default:
> >>+		return -EINVAL;
> >>+	}
> >>+}
> >>+
> >>+static int v4l2_flash_s_ctrl(struct v4l2_ctrl *c)
> >>+{
> >>+	struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c);
> >>+	struct led_classdev_flash *flash = v4l2_flash->flash;
> >>+	struct v4l2_flash_ctrl *ctrl = &v4l2_flash->ctrl;
> >>+	struct v4l2_flash_ctrl_config *config = &v4l2_flash->config;
> >>+	enum led_brightness torch_brightness;
> >>+	bool external_strobe;
> >>+	int ret;
> >>+
> >>+	switch (c->id) {
> >>+	case V4L2_CID_FLASH_LED_MODE:
> >>+		switch (c->val) {
> >>+		case V4L2_FLASH_LED_MODE_NONE:
> >>+			call_flash_op(v4l2_flash, torch_brightness_set,
> >>+							&flash->led_cdev, 0);
> >>+			return call_flash_op(v4l2_flash, strobe_set, flash,
> >>+							false);
> >>+		case V4L2_FLASH_LED_MODE_FLASH:
> >>+			/* Turn off torch LED */
> >>+			call_flash_op(v4l2_flash, torch_brightness_set,
> >>+							&flash->led_cdev, 0);
> >>+			external_strobe = (ctrl->source->val ==
> >>+					V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
> >>+			return call_flash_op(v4l2_flash, external_strobe_set,
> >>+						flash, external_strobe);
> >>+		case V4L2_FLASH_LED_MODE_TORCH:
> >>+			/* Stop flash strobing */
> >>+			ret = call_flash_op(v4l2_flash, strobe_set, flash,
> >>+							false);
> >>+			if (ret)
> >>+				return ret;
> >>+
> >>+			torch_brightness =
> >>+				v4l2_flash_intensity_to_led_brightness(
> >>+						&config->torch_intensity,
> >>+						ctrl->torch_intensity->val);
> >>+			call_flash_op(v4l2_flash, torch_brightness_set,
> >>+					&flash->led_cdev, torch_brightness);
> >>+			return ret;
> >>+		}
> >>+		break;
> >>+	case V4L2_CID_FLASH_STROBE_SOURCE:
> >>+		external_strobe = (c->val == V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
> >
> >Is the external_strobe argument match exactly to the strobe source
> >control? You seem to assume that in g_volatile_ctrl() above. I think
> >having it the either way is fine but not both. :-)
> 
> The STROBE_SOURCE_EXTERNAL control state is volatile if a flash device
> depends on muxes that route strobe signals to more then one flash
> device. In such a case it behaves similarly to FLASH_STROBE control,
> i.e. it activates external strobe only for the flash timeout period.
> I touched this issue in the cover letter of this patch series,
> paragraph 2.

I meant that flash->external_strobe is directly used as
V4L2_CID_FLASH_STROBE_SOURCE. Are the two guaranteed to be the same?

...

> >>+/*
> >>+ * V4L2 subdev internal operations
> >>+ */
> >>+
> >>+static int v4l2_flash_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> >>+{
> >>+	struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd);
> >>+	struct led_classdev_flash *flash = v4l2_flash->flash;
> >>+	struct led_classdev *led_cdev = &flash->led_cdev;
> >>+
> >>+	mutex_lock(&led_cdev->led_lock);
> >>+	call_flash_op(v4l2_flash, sysfs_lock, led_cdev);
> >
> >What if you have the device open through multiple file handles? I
> >believe v4l2_subdev_fh_is_singular(&fh->vfh) would prove helpful here.
> 
> I assume you propose to implement such a function? I don't see it in
> the mainline kernel.

Uh, I thought of v4l2_subdev_fh_is_singular() but what I really meant was
v4l2_fh_is_singular(). There's no sub-device variant of it, as the
sub-device file handle struct embeds struct v4l2_fh.

...

> >>+{
> >>+	struct led_classdev_flash *flash = v4l2_flash->flash;
> >>+	struct led_classdev *led_cdev = &flash->led_cdev;
> >>+	int ret;
> >>+
> >>+	if (!v4l2_dev) {
> >>+		v4l2_dev = kzalloc(sizeof(*v4l2_dev), GFP_KERNEL);
> >>+		if (!v4l2_dev)
> >>+			return -ENOMEM;
> >>+
> >>+		strlcpy(v4l2_dev->name, "v4l2-flash-manager",
> >>+						sizeof(v4l2_dev->name));
> >>+		ret = v4l2_device_register(NULL, v4l2_dev);
> >>+		if (ret < 0) {
> >>+			dev_err(led_cdev->dev->parent,
> >>+				 "Failed to register v4l2_device: %d\n", ret);
> >>+			goto err_v4l2_device_register;
> >>+		}
> >>+	}
> >>+
> >>+	ret = v4l2_device_register_subdev(v4l2_dev, &v4l2_flash->sd);
> >>+	if (ret < 0) {
> >>+		dev_err(led_cdev->dev->parent,
> >>+			 "Failed to register v4l2_subdev: %d\n", ret);
> >>+		goto err_v4l2_device_register;
> >>+	}
> >>+
> >>+	ret = v4l2_device_register_subdev_node(&v4l2_flash->sd, v4l2_dev);
> >>+	if (ret < 0) {
> >>+		dev_err(led_cdev->dev->parent,
> >>+			 "Failed to register v4l2_subdev node: %d\n", ret);
> >>+		goto err_register_subdev_node;
> >>+	}
> >
> >This way you can create a V4L2 sub-device node. However, flash devices
> >are seldom alone in the system. They are physically close to a sensor,
> >and this connection is shown in the Media controller interface. This
> >means that the flash sub-device (entity) should be part of the Media
> >device created by the driver in control of it. This can be achieved by
> >the master driver creating the sub-device. You should register an async
> >sub-device here.
> >
> >This results in the sub-device not being registered if there's no such
> >master driver, but I wouldn't expect that to be an issue since the V4L2
> >flash API is mostly relevant in such cases.
> 
> I addressed this issue in the cover letter of this patch series,
> paragraph 1. If strobe signals are routed to a flash device through
> a multiplexer then assignment of the related V4L2 Flash sub-device
> to a particular media controller may dynamically change in time.
> Actually, if a flash device can be shared between media systems
> (i.e. it can be configured to react on strobe signals from different
> camera sensors, basing on muxes configuration), than it becomes
> bound to a particular media system only for the time of strobing.
> Please refer to [1].

Replied to the cover letter.

...

> >>diff --git a/include/media/v4l2-flash.h b/include/media/v4l2-flash.h
> >>new file mode 100644
> >>index 0000000..effa46b
> >>--- /dev/null
> >>+++ b/include/media/v4l2-flash.h
> >>@@ -0,0 +1,137 @@
> >>+/*
> >>+ * V4L2 Flash LED sub-device 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 <media/v4l2-ioctl.h>
> >>+
> >>+struct led_classdev_flash;
> >>+struct led_classdev;
> >>+enum led_brightness;
> >>+
> >>+struct v4l2_flash_ops {
> >>+	int (*torch_brightness_set)(struct led_classdev *led_cdev,
> >>+					enum led_brightness brightness);
> >>+	int (*torch_brightness_update)(struct led_classdev *led_cdev);
> >>+	int (*flash_brightness_set)(struct led_classdev_flash *flash,
> >>+					u32 brightness);
> >>+	int (*flash_brightness_update)(struct led_classdev_flash *flash);
> >>+	int (*strobe_set)(struct led_classdev_flash *flash, bool state);
> >>+	int (*strobe_get)(struct led_classdev_flash *flash, bool *state);
> >>+	int (*timeout_set)(struct led_classdev_flash *flash, u32 timeout);
> >>+	int (*indicator_brightness_set)(struct led_classdev_flash *flash,
> >>+					u32 brightness);
> >>+	int (*indicator_brightness_update)(struct led_classdev_flash *flash);
> >>+	int (*external_strobe_set)(struct led_classdev_flash *flash,
> >>+					bool enable);
> >>+	int (*fault_get)(struct led_classdev_flash *flash, u32 *fault);
> >>+	void (*sysfs_lock)(struct led_classdev *led_cdev);
> >>+	void (*sysfs_unlock)(struct led_classdev *led_cdev);
> >
> >These functions are not driver specific and there's going to be just one
> >implementation (I suppose). Could you refresh my memory regarding why
> >the LED framework functions aren't called directly?
> 
> These ops are required to make possible building led-class-flash as
> a kernel module.

Assuming you'd use the actual implementation directly, what would be the
dependencies? I don't think the LED flash framework has any callbacks
towards the V4L2 (LED) flash framework, does it? Please correct my
understanding if I'm missing something. In Makefile format, assume all
targets are .PHONY:

led-flash-api: led-api

v4l2-flash: led-flash-api

driver: led-flash-api v4l2-flash

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [PATCH/RFC v4 15/21] media: Add registration helpers for V4L2 flash
  2014-08-11 12:26       ` Sakari Ailus
@ 2014-08-11 13:27         ` Jacek Anaszewski
  2014-08-11 13:45           ` Jacek Anaszewski
  2014-08-14  4:34           ` Sakari Ailus
  0 siblings, 2 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-08-11 13:27 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie, Hans Verkuil

Hi Sakari

On 08/11/2014 02:26 PM, Sakari Ailus wrote:
>
> Hi Jacek,
>
> On Mon, Aug 04, 2014 at 04:43:54PM +0200, Jacek Anaszewski wrote:
>> Hi Sakari,
>>
>> Thanks for the review.
>
> You're welcome! :)
>
>> On 07/21/2014 01:12 PM, Sakari Ailus wrote:
>>> Hi Jacek,
>>>
>>> Jacek Anaszewski wrote:
>>>> 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
>>>> support for V4L2 Flash sub-devices 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>
>>>> Cc: Sakari Ailus <sakari.ailus@iki.fi>
>>>> Cc: Hans Verkuil <hans.verkuil@cisco.com>
>>>> ---
>>>>   drivers/media/v4l2-core/Kconfig      |   11 +
>>>>   drivers/media/v4l2-core/Makefile     |    2 +
>>>>   drivers/media/v4l2-core/v4l2-flash.c |  580 ++++++++++++++++++++++++++++++++++
>>>>   include/media/v4l2-flash.h           |  137 ++++++++
>>>>   4 files changed, 730 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 9ca0f8d..3ae3f0f 100644
>>>> --- a/drivers/media/v4l2-core/Kconfig
>>>> +++ b/drivers/media/v4l2-core/Kconfig
>>>> @@ -35,6 +35,17 @@ config V4L2_MEM2MEM_DEV
>>>>           tristate
>>>>           depends on VIDEOBUF2_CORE
>>>>
>>>> +# Used by LED subsystem flash drivers
>>>> +config V4L2_FLASH_LED_CLASS
>>>> +	bool "Enable support for Flash sub-devices"
>>>> +	depends on VIDEO_V4L2_SUBDEV_API
>>>> +	depends on LEDS_CLASS_FLASH
>>>> +	---help---
>>>> +	  Say Y here to enable support for Flash sub-devices, which allow
>>>> +	  to control LED class devices with use of V4L2 Flash controls.
>>>> +
>>>> +	  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 63d29f2..44e858c 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_LED_CLASS) += 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..21080c6
>>>> --- /dev/null
>>>> +++ b/drivers/media/v4l2-core/v4l2-flash.c
>>>> @@ -0,0 +1,580 @@
>>>> +/*
>>>> + * V4L2 Flash LED sub-device 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 <linux/led-class-flash.h>
>>>> +#include <linux/mutex.h>
>>>> +#include <linux/of_led_flash_manager.h>
>>>> +#include <linux/slab.h>
>>>> +#include <media/v4l2-flash.h>
>>>> +
>>>> +#define call_flash_op(v4l2_flash, op, args...)			\
>>>> +		(v4l2_flash->ops->op  ?				\
>>>> +			v4l2_flash->ops->op(args) :		\
>>>> +			-EINVAL)
>>>> +
>>>> +static struct v4l2_device *v4l2_dev;
>>>> +static int registered_flashes;
>>>> +
>>>> +static inline enum led_brightness v4l2_flash_intensity_to_led_brightness(
>>>> +					struct v4l2_ctrl_config *config,
>>>> +					s32 intensity)
>>>> +{
>>>> +	return ((intensity - config->min) / config->step) + 1;
>>>> +}
>>>> +
>>>> +static inline s32 v4l2_flash_led_brightness_to_intensity(
>>>> +					struct v4l2_ctrl_config *config,
>>>> +					enum led_brightness brightness)
>>>> +{
>>>> +	return ((brightness - 1) * config->step) + config->min;
>>>> +}
>>>> +
>>>> +static int v4l2_flash_g_volatile_ctrl(struct v4l2_ctrl *c)
>>>> +
>>>> +{
>>>> +	struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c);
>>>> +	struct led_classdev_flash *flash = v4l2_flash->flash;
>>>> +	struct led_classdev *led_cdev = &flash->led_cdev;
>>>> +	struct v4l2_flash_ctrl_config *config = &v4l2_flash->config;
>>>> +	struct v4l2_flash_ctrl *ctrl = &v4l2_flash->ctrl;
>>>> +	bool is_strobing;
>>>> +	int ret;
>>>> +
>>>> +	switch (c->id) {
>>>> +	case V4L2_CID_FLASH_TORCH_INTENSITY:
>>>> +		/*
>>>> +		 * Update torch brightness only if in TORCH_MODE,
>>>> +		 * as otherwise brightness_update op returns 0,
>>>> +		 * which would spuriously inform user space that
>>>> +		 * V4L2_CID_FLASH_TORCH_INTENSITY control value
>>>> +		 * has changed.
>>>> +		 */
>>>> +		if (ctrl->led_mode->val == V4L2_FLASH_LED_MODE_TORCH) {
>>>> +			ret = call_flash_op(v4l2_flash, torch_brightness_update,
>>>> +							led_cdev);
>>>> +			if (ret < 0)
>>>> +				return ret;
>>>> +			ctrl->torch_intensity->val =
>>>> +				v4l2_flash_led_brightness_to_intensity(
>>>> +						&config->torch_intensity,
>>>> +						led_cdev->brightness);
>>>> +		}
>>>> +		return 0;
>>>> +	case V4L2_CID_FLASH_INTENSITY:
>>>> +		ret = call_flash_op(v4l2_flash, flash_brightness_update,
>>>> +					flash);
>>>> +		if (ret < 0)
>>>> +			return ret;
>>>> +		/* no conversion is needed */
>>>> +		c->val = flash->brightness.val;
>>>> +		return 0;
>>>> +	case V4L2_CID_FLASH_INDICATOR_INTENSITY:
>>>> +		ret = call_flash_op(v4l2_flash, indicator_brightness_update,
>>>> +						flash);
>>>> +		if (ret < 0)
>>>> +			return ret;
>>>> +		/* no conversion is needed */
>>>> +		c->val = flash->indicator_brightness->val;
>>>> +		return 0;
>>>> +	case V4L2_CID_FLASH_STROBE_STATUS:
>>>> +		ret = call_flash_op(v4l2_flash, strobe_get, flash,
>>>> +							&is_strobing);
>>>> +		if (ret < 0)
>>>> +			return ret;
>>>> +		c->val = is_strobing;
>>>> +		return 0;
>>>> +	case V4L2_CID_FLASH_FAULT:
>>>> +		/* led faults map directly to V4L2 flash faults */
>>>> +		ret = call_flash_op(v4l2_flash, fault_get, flash, &c->val);
>>>> +		return ret;
>>>> +	case V4L2_CID_FLASH_STROBE_SOURCE:
>>>> +		c->val = flash->external_strobe;
>>>> +		return 0;
>>>> +	case V4L2_CID_FLASH_STROBE_PROVIDER:
>>>> +		c->val = flash->strobe_provider_id;
>>>> +		return 0;
>>>> +	default:
>>>> +		return -EINVAL;
>>>> +	}
>>>> +}
>>>> +
>>>> +static int v4l2_flash_s_ctrl(struct v4l2_ctrl *c)
>>>> +{
>>>> +	struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c);
>>>> +	struct led_classdev_flash *flash = v4l2_flash->flash;
>>>> +	struct v4l2_flash_ctrl *ctrl = &v4l2_flash->ctrl;
>>>> +	struct v4l2_flash_ctrl_config *config = &v4l2_flash->config;
>>>> +	enum led_brightness torch_brightness;
>>>> +	bool external_strobe;
>>>> +	int ret;
>>>> +
>>>> +	switch (c->id) {
>>>> +	case V4L2_CID_FLASH_LED_MODE:
>>>> +		switch (c->val) {
>>>> +		case V4L2_FLASH_LED_MODE_NONE:
>>>> +			call_flash_op(v4l2_flash, torch_brightness_set,
>>>> +							&flash->led_cdev, 0);
>>>> +			return call_flash_op(v4l2_flash, strobe_set, flash,
>>>> +							false);
>>>> +		case V4L2_FLASH_LED_MODE_FLASH:
>>>> +			/* Turn off torch LED */
>>>> +			call_flash_op(v4l2_flash, torch_brightness_set,
>>>> +							&flash->led_cdev, 0);
>>>> +			external_strobe = (ctrl->source->val ==
>>>> +					V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
>>>> +			return call_flash_op(v4l2_flash, external_strobe_set,
>>>> +						flash, external_strobe);
>>>> +		case V4L2_FLASH_LED_MODE_TORCH:
>>>> +			/* Stop flash strobing */
>>>> +			ret = call_flash_op(v4l2_flash, strobe_set, flash,
>>>> +							false);
>>>> +			if (ret)
>>>> +				return ret;
>>>> +
>>>> +			torch_brightness =
>>>> +				v4l2_flash_intensity_to_led_brightness(
>>>> +						&config->torch_intensity,
>>>> +						ctrl->torch_intensity->val);
>>>> +			call_flash_op(v4l2_flash, torch_brightness_set,
>>>> +					&flash->led_cdev, torch_brightness);
>>>> +			return ret;
>>>> +		}
>>>> +		break;
>>>> +	case V4L2_CID_FLASH_STROBE_SOURCE:
>>>> +		external_strobe = (c->val == V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
>>>
>>> Is the external_strobe argument match exactly to the strobe source
>>> control? You seem to assume that in g_volatile_ctrl() above. I think
>>> having it the either way is fine but not both. :-)
>>
>> The STROBE_SOURCE_EXTERNAL control state is volatile if a flash device
>> depends on muxes that route strobe signals to more then one flash
>> device. In such a case it behaves similarly to FLASH_STROBE control,
>> i.e. it activates external strobe only for the flash timeout period.
>> I touched this issue in the cover letter of this patch series,
>> paragraph 2.
>
> I meant that flash->external_strobe is directly used as
> V4L2_CID_FLASH_STROBE_SOURCE. Are the two guaranteed to be the same?
>
> ...
>
>>>> +/*
>>>> + * V4L2 subdev internal operations
>>>> + */
>>>> +
>>>> +static int v4l2_flash_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
>>>> +{
>>>> +	struct v4l2_flash *v4l2_flash = v4l2_subdev_to_v4l2_flash(sd);
>>>> +	struct led_classdev_flash *flash = v4l2_flash->flash;
>>>> +	struct led_classdev *led_cdev = &flash->led_cdev;
>>>> +
>>>> +	mutex_lock(&led_cdev->led_lock);
>>>> +	call_flash_op(v4l2_flash, sysfs_lock, led_cdev);
>>>
>>> What if you have the device open through multiple file handles? I
>>> believe v4l2_subdev_fh_is_singular(&fh->vfh) would prove helpful here.
>>
>> I assume you propose to implement such a function? I don't see it in
>> the mainline kernel.
>
> Uh, I thought of v4l2_subdev_fh_is_singular() but what I really meant was
> v4l2_fh_is_singular(). There's no sub-device variant of it, as the
> sub-device file handle struct embeds struct v4l2_fh.

OK, I will use it here.

> ...
>
>>>> +{
>>>> +	struct led_classdev_flash *flash = v4l2_flash->flash;
>>>> +	struct led_classdev *led_cdev = &flash->led_cdev;
>>>> +	int ret;
>>>> +
>>>> +	if (!v4l2_dev) {
>>>> +		v4l2_dev = kzalloc(sizeof(*v4l2_dev), GFP_KERNEL);
>>>> +		if (!v4l2_dev)
>>>> +			return -ENOMEM;
>>>> +
>>>> +		strlcpy(v4l2_dev->name, "v4l2-flash-manager",
>>>> +						sizeof(v4l2_dev->name));
>>>> +		ret = v4l2_device_register(NULL, v4l2_dev);
>>>> +		if (ret < 0) {
>>>> +			dev_err(led_cdev->dev->parent,
>>>> +				 "Failed to register v4l2_device: %d\n", ret);
>>>> +			goto err_v4l2_device_register;
>>>> +		}
>>>> +	}
>>>> +
>>>> +	ret = v4l2_device_register_subdev(v4l2_dev, &v4l2_flash->sd);
>>>> +	if (ret < 0) {
>>>> +		dev_err(led_cdev->dev->parent,
>>>> +			 "Failed to register v4l2_subdev: %d\n", ret);
>>>> +		goto err_v4l2_device_register;
>>>> +	}
>>>> +
>>>> +	ret = v4l2_device_register_subdev_node(&v4l2_flash->sd, v4l2_dev);
>>>> +	if (ret < 0) {
>>>> +		dev_err(led_cdev->dev->parent,
>>>> +			 "Failed to register v4l2_subdev node: %d\n", ret);
>>>> +		goto err_register_subdev_node;
>>>> +	}
>>>
>>> This way you can create a V4L2 sub-device node. However, flash devices
>>> are seldom alone in the system. They are physically close to a sensor,
>>> and this connection is shown in the Media controller interface. This
>>> means that the flash sub-device (entity) should be part of the Media
>>> device created by the driver in control of it. This can be achieved by
>>> the master driver creating the sub-device. You should register an async
>>> sub-device here.
>>>
>>> This results in the sub-device not being registered if there's no such
>>> master driver, but I wouldn't expect that to be an issue since the V4L2
>>> flash API is mostly relevant in such cases.
>>
>> I addressed this issue in the cover letter of this patch series,
>> paragraph 1. If strobe signals are routed to a flash device through
>> a multiplexer then assignment of the related V4L2 Flash sub-device
>> to a particular media controller may dynamically change in time.
>> Actually, if a flash device can be shared between media systems
>> (i.e. it can be configured to react on strobe signals from different
>> camera sensors, basing on muxes configuration), than it becomes
>> bound to a particular media system only for the time of strobing.
>> Please refer to [1].
>
> Replied to the cover letter.

Replied to your reply.

> ...
>
>>>> diff --git a/include/media/v4l2-flash.h b/include/media/v4l2-flash.h
>>>> new file mode 100644
>>>> index 0000000..effa46b
>>>> --- /dev/null
>>>> +++ b/include/media/v4l2-flash.h
>>>> @@ -0,0 +1,137 @@
>>>> +/*
>>>> + * V4L2 Flash LED sub-device 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>le
>>>> +#include <media/v4l2-event.h>
>>>> +#include <media/v4l2-ioctl.h>
>>>> +
>>>> +struct led_classdev_flash;
>>>> +struct led_classdev;
>>>> +enum led_brightness;
>>>> +
>>>> +struct v4l2_flash_ops {
>>>> +	int (*torch_brightness_set)(struct led_classdev *led_cdev,
>>>> +					enum led_brightness brightness);
>>>> +	int (*torch_brightness_update)(struct led_classdev *led_cdev);
>>>> +	int (*flash_brightness_set)(struct led_classdev_flash *flash,
>>>> +					u32 brightness);
>>>> +	int (*flash_brightness_update)(struct led_classdev_flash *flash);
>>>> +	int (*strobe_set)(struct led_classdev_flash *flash, bool state);
>>>> +	int (*strobe_get)(struct led_classdev_flash *flash, bool *state);
>>>> +	int (*timeout_set)(struct led_classdev_flash *flash, u32 timeout);
>>>> +	int (*indicator_brightness_set)(struct led_classdev_flash *flash,
>>>> +					u32 brightness);
>>>> +	int (*indicator_brightness_update)(struct led_classdev_flash *flash);
>>>> +	int (*external_strobe_set)(struct led_classdev_flash *flash,
>>>> +					bool enable);
>>>> +	int (*fault_get)(struct led_classdev_flash *flash, u32 *fault);
>>>> +	void (*sysfs_lock)(struct led_classdev *led_cdev);
>>>> +	void (*sysfs_unlock)(struct led_classdev *led_cdev);
>>>
>>> These functions are not driver specific and there's going to be just one
>>> implementation (I suppose). Could you refresh my memory regarding why
>>> the LED framework functions aren't called directly?
>>
>> These ops are required to make possible building led-class-flash as
>> a kernel module.
>
> Assuming you'd use the actual implementation directly, what would be the
> dependencies? I don't think the LED flash framework has any callbacks
> towards the V4L2 (LED) flash framework, does it? Please correct my
> understanding if I'm missing something. In Makefile format, assume all
> targets are .PHONY:
>
> led-flash-api: led-api
>
> v4l2-flash: led-flash-api
>
> driver: led-flash-api v4l2-flash

LED Class Flash driver gains V4L2 Flash API when
CONFIG_V4L2_FLASH_LED_CLASS is defined. This is accomplished in
the probe function by either calling v4l2_flash_init function
or the macro of this name, when the CONFIG_V4L2_FLASH_LED_CLASS
macro isn't defined.

If the v4l2-flash.c was to call the LED API directly, then the
led-class-flash module symbols would have to be available at
v4l2-flash.o linking time.

This requirement cannot be met if the led-class-flash is built
as a module.

Use of function pointers in the v4l2-flash.c allows to compile it
into the kernel and enables the possibility of adding the V4L2 Flash
support conditionally, during driver probing.

Best Regards,
Jacek Anaszewski

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

* Re: [PATCH/RFC v4 15/21] media: Add registration helpers for V4L2 flash
  2014-08-11 13:27         ` Jacek Anaszewski
@ 2014-08-11 13:45           ` Jacek Anaszewski
  2014-08-14  4:34           ` Sakari Ailus
  1 sibling, 0 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-08-11 13:45 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie, Hans Verkuil

> On 08/11/2014 02:26 PM, Sakari Ailus wrote:
>>
>> Hi Jacek,
>>

...

>>>>> +static int v4l2_flash_s_ctrl(struct v4l2_ctrl *c)
>>>>> +{
>>>>> +    struct v4l2_flash *v4l2_flash = v4l2_ctrl_to_v4l2_flash(c);
>>>>> +    struct led_classdev_flash *flash = v4l2_flash->flash;
>>>>> +    struct v4l2_flash_ctrl *ctrl = &v4l2_flash->ctrl;
>>>>> +    struct v4l2_flash_ctrl_config *config = &v4l2_flash->config;
>>>>> +    enum led_brightness torch_brightness;
>>>>> +    bool external_strobe;
>>>>> +    int ret;
>>>>> +
>>>>> +    switch (c->id) {
>>>>> +    case V4L2_CID_FLASH_LED_MODE:
>>>>> +        switch (c->val) {
>>>>> +        case V4L2_FLASH_LED_MODE_NONE:
>>>>> +            call_flash_op(v4l2_flash, torch_brightness_set,
>>>>> +                            &flash->led_cdev, 0);
>>>>> +            return call_flash_op(v4l2_flash, strobe_set, flash,
>>>>> +                            false);
>>>>> +        case V4L2_FLASH_LED_MODE_FLASH:
>>>>> +            /* Turn off torch LED */
>>>>> +            call_flash_op(v4l2_flash, torch_brightness_set,
>>>>> +                            &flash->led_cdev, 0);
>>>>> +            external_strobe = (ctrl->source->val ==
>>>>> +                    V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
>>>>> +            return call_flash_op(v4l2_flash, external_strobe_set,
>>>>> +                        flash, external_strobe);
>>>>> +        case V4L2_FLASH_LED_MODE_TORCH:
>>>>> +            /* Stop flash strobing */
>>>>> +            ret = call_flash_op(v4l2_flash, strobe_set, flash,
>>>>> +                            false);
>>>>> +            if (ret)
>>>>> +                return ret;
>>>>> +
>>>>> +            torch_brightness =
>>>>> +                v4l2_flash_intensity_to_led_brightness(
>>>>> +                        &config->torch_intensity,
>>>>> +                        ctrl->torch_intensity->val);
>>>>> +            call_flash_op(v4l2_flash, torch_brightness_set,
>>>>> +                    &flash->led_cdev, torch_brightness);
>>>>> +            return ret;
>>>>> +        }
>>>>> +        break;
>>>>> +    case V4L2_CID_FLASH_STROBE_SOURCE:
>>>>> +        external_strobe = (c->val ==
>>>>> V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
>>>>
>>>> Is the external_strobe argument match exactly to the strobe source
>>>> control? You seem to assume that in g_volatile_ctrl() above. I think
>>>> having it the either way is fine but not both. :-)
>>>
>>> The STROBE_SOURCE_EXTERNAL control state is volatile if a flash device
>>> depends on muxes that route strobe signals to more then one flash
>>> device. In such a case it behaves similarly to FLASH_STROBE control,
>>> i.e. it activates external strobe only for the flash timeout period.
>>> I touched this issue in the cover letter of this patch series,
>>> paragraph 2.
>>
>> I meant that flash->external_strobe is directly used as
>> V4L2_CID_FLASH_STROBE_SOURCE. Are the two guaranteed to be the same?

Yes, the external_strobe sysfs attribute is mapped to it.

Best Regards,
Jacek Anaszewski

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

* Re: [PATCH/RFC v4 00/21] LED / flash API integration
  2014-08-08 16:59       ` Bryan Wu
@ 2014-08-11 14:24         ` Jacek Anaszewski
  0 siblings, 0 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-08-11 14:24 UTC (permalink / raw)
  To: Bryan Wu
  Cc: Linux LED Subsystem, devicetree, linux-media, lkml,
	Kyungmin Park, b.zolnierkie

Hi Bryan,

On 08/08/2014 06:59 PM, Bryan Wu wrote:
> On Thu, Aug 7, 2014 at 11:43 PM, Jacek Anaszewski
> <j.anaszewski@samsung.com> wrote:
>> Hi Bryan,
>>
>>
>> On 07/16/2014 07:21 PM, Bryan Wu wrote:
>>>
>>> On Wed, Jul 16, 2014 at 10:19 AM, Bryan Wu <cooloney@gmail.com> wrote:
>>>>
>>>> On Fri, Jul 11, 2014 at 7:04 AM, Jacek Anaszewski
>>>> <j.anaszewski@samsung.com> wrote:
>>>>>
>>>>> This is is the fourth 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-20140707
>>>>>
>>>>
>>>> I really apologize that I missed your discussion email in my Gmail
>>>> inbox, it was archived some where. Even in this series some of these
>>>> patches are archived in different tab.
>>>>
>>>> I will start to review and help to push this.
>>>>
>>>
>>> In the mean time, could you please provide an git tree for me to pull?
>>> It's much easier for me to review in my git.
>>
>>
>> Few days ago I sent to your private email the path to the git
>> repository to pull, but I am resending it through the lists to
>> make sure that it will not get filtered somehow again.
>>
>>
>> git://linuxtv.org/snawrocki/samsung.git
>> branch: led_flash_integration_v4
>>
>> gitweb:
>> http://git.linuxtv.org/cgit.cgi/snawrocki/samsung.git/log/?h=led_flash_integration_v4
>>
>
> Yes, I got it. I merged some leds core fixing patches from you
> recently. So could you please split these big patchset into several
> small parts which should be easier for reviewing and discussion.
>   - led core fixing
>   - led flash core patches
>   - led flash manager patches
>   - v4l2 led patches
>   - Documentations
>
> My plan is to review these and merge them to my -devel branch. Then
> invite people for testing.

Thanks for merging the fixes. I will come up with a new patch sets,
in the form as you requested, but I would like to apply as many
improvements, in comparison to the version 4, as possible. Therefore
I'd be grateful if you could express your opinion in the discussion [1].

Best Regards,
Jacek Anaszewski

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

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

* Re: [PATCH/RFC v4 15/21] media: Add registration helpers for V4L2 flash
  2014-08-11 13:27         ` Jacek Anaszewski
  2014-08-11 13:45           ` Jacek Anaszewski
@ 2014-08-14  4:34           ` Sakari Ailus
  2014-08-14  8:25             ` Jacek Anaszewski
  1 sibling, 1 reply; 55+ messages in thread
From: Sakari Ailus @ 2014-08-14  4:34 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie, Hans Verkuil

Hi Jacek,

On Mon, Aug 11, 2014 at 03:27:22PM +0200, Jacek Anaszewski wrote:

...

> >>>>diff --git a/include/media/v4l2-flash.h b/include/media/v4l2-flash.h
> >>>>new file mode 100644
> >>>>index 0000000..effa46b
> >>>>--- /dev/null
> >>>>+++ b/include/media/v4l2-flash.h
> >>>>@@ -0,0 +1,137 @@
> >>>>+/*
> >>>>+ * V4L2 Flash LED sub-device 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>le
> >>>>+#include <media/v4l2-event.h>
> >>>>+#include <media/v4l2-ioctl.h>
> >>>>+
> >>>>+struct led_classdev_flash;
> >>>>+struct led_classdev;
> >>>>+enum led_brightness;
> >>>>+
> >>>>+struct v4l2_flash_ops {
> >>>>+	int (*torch_brightness_set)(struct led_classdev *led_cdev,
> >>>>+					enum led_brightness brightness);
> >>>>+	int (*torch_brightness_update)(struct led_classdev *led_cdev);
> >>>>+	int (*flash_brightness_set)(struct led_classdev_flash *flash,
> >>>>+					u32 brightness);
> >>>>+	int (*flash_brightness_update)(struct led_classdev_flash *flash);
> >>>>+	int (*strobe_set)(struct led_classdev_flash *flash, bool state);
> >>>>+	int (*strobe_get)(struct led_classdev_flash *flash, bool *state);
> >>>>+	int (*timeout_set)(struct led_classdev_flash *flash, u32 timeout);
> >>>>+	int (*indicator_brightness_set)(struct led_classdev_flash *flash,
> >>>>+					u32 brightness);
> >>>>+	int (*indicator_brightness_update)(struct led_classdev_flash *flash);
> >>>>+	int (*external_strobe_set)(struct led_classdev_flash *flash,
> >>>>+					bool enable);
> >>>>+	int (*fault_get)(struct led_classdev_flash *flash, u32 *fault);
> >>>>+	void (*sysfs_lock)(struct led_classdev *led_cdev);
> >>>>+	void (*sysfs_unlock)(struct led_classdev *led_cdev);
> >>>
> >>>These functions are not driver specific and there's going to be just one
> >>>implementation (I suppose). Could you refresh my memory regarding why
> >>>the LED framework functions aren't called directly?
> >>
> >>These ops are required to make possible building led-class-flash as
> >>a kernel module.
> >
> >Assuming you'd use the actual implementation directly, what would be the
> >dependencies? I don't think the LED flash framework has any callbacks
> >towards the V4L2 (LED) flash framework, does it? Please correct my
> >understanding if I'm missing something. In Makefile format, assume all
> >targets are .PHONY:
> >
> >led-flash-api: led-api
> >
> >v4l2-flash: led-flash-api
> >
> >driver: led-flash-api v4l2-flash
> 
> LED Class Flash driver gains V4L2 Flash API when
> CONFIG_V4L2_FLASH_LED_CLASS is defined. This is accomplished in
> the probe function by either calling v4l2_flash_init function
> or the macro of this name, when the CONFIG_V4L2_FLASH_LED_CLASS
> macro isn't defined.
> 
> If the v4l2-flash.c was to call the LED API directly, then the
> led-class-flash module symbols would have to be available at
> v4l2-flash.o linking time.

Is this an issue? EXPORT_SYMBOL_GPL() for the relevant symbols should be
enough.

> This requirement cannot be met if the led-class-flash is built
> as a module.
> 
> Use of function pointers in the v4l2-flash.c allows to compile it
> into the kernel and enables the possibility of adding the V4L2 Flash
> support conditionally, during driver probing.

I'd simply decide this during kernel compilation time. If you want
something, just enable it. v4l2_flash_init() is called directly by the
driver in any case, so unless that is also called through a wrapper the
driver is still directly dependent on it.

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [PATCH/RFC v4 06/21] leds: add API for setting torch brightness
  2014-08-07 13:12         ` Jacek Anaszewski
@ 2014-08-14  4:39           ` Sakari Ailus
  2014-08-18 19:55             ` Richard Purdie
  0 siblings, 1 reply; 55+ messages in thread
From: Sakari Ailus @ 2014-08-14  4:39 UTC (permalink / raw)
  To: Jacek Anaszewski, Bryan Wu, Richard Purdie
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie

Bryan and Richard,

Your opinion would be much appreciated to a question myself and Jacek were
pondering. Please see below.

On Thu, Aug 07, 2014 at 03:12:09PM +0200, Jacek Anaszewski wrote:
> Hi Sakari,
> 
> On 08/04/2014 02:50 PM, Sakari Ailus wrote:
> >Hi Jacek,
> >
> >Thank you for your continued efforts on this!
> >
> >On Mon, Aug 04, 2014 at 02:35:26PM +0200, Jacek Anaszewski wrote:
> >>On 07/16/2014 11:54 PM, Sakari Ailus wrote:
> >>>Hi Jacek,
> >>>
> >>>Jacek Anaszewski wrote:
> >>>...
> >>>>diff --git a/include/linux/leds.h b/include/linux/leds.h
> >>>>index 1a130cc..9bea9e6 100644
> >>>>--- a/include/linux/leds.h
> >>>>+++ b/include/linux/leds.h
> >>>>@@ -44,11 +44,21 @@ struct led_classdev {
> >>>>  #define LED_BLINK_ONESHOT_STOP    (1 << 18)
> >>>>  #define LED_BLINK_INVERT    (1 << 19)
> >>>>  #define LED_SYSFS_LOCK        (1 << 20)
> >>>>+#define LED_DEV_CAP_TORCH    (1 << 21)
> >>>>
> >>>>      /* Set LED brightness level */
> >>>>      /* Must not sleep, use a workqueue if needed */
> >>>>      void        (*brightness_set)(struct led_classdev *led_cdev,
> >>>>                        enum led_brightness brightness);
> >>>>+    /*
> >>>>+     * Set LED brightness immediately - it is required for flash led
> >>>>+     * devices as they require setting torch brightness to have
> >>>>immediate
> >>>>+     * effect. brightness_set op cannot be used for this purpose because
> >>>>+     * the led drivers schedule a work queue task in it to allow for
> >>>>+     * being called from led-triggers, i.e. from the timer irq context.
> >>>>+     */
> >>>
> >>>Do we need to classify actual devices based on this? I think it's rather
> >>>a different API behaviour between the LED and the V4L2 APIs.
> >>>
> >>>On devices that are slow to control, the behaviour should be asynchronous
> >>>over the LED API and synchronous when accessed through the V4L2 API. How
> >>>about implementing the work queue, as I have suggested, in the
> >>>framework, so
> >>>that individual drivers don't need to care about this and just implement
> >>>the
> >>>synchronous variant of this op? A flag could be added to distinguish
> >>>devices
> >>>that are fast so that the work queue isn't needed.
> >>>
> >>>It'd be nice to avoid individual drivers having to implement multiple
> >>>ops to
> >>>do the same thing, just for differing user space interfacs.
> >>>
> >>
> >>It is not only the matter of a device controller speed. If a flash
> >>device is to be made accessible from the LED subsystem, then it
> >>should be also compatible with led-triggers. Some of led-triggers
> >>call brightness_set op from the timer irq context and thus no
> >>locking in the callback can occur. This requirement cannot be
> >>met i.e. if i2c bus is to be used. This is probably the primary
> >>reason for scheduling work queue tasks in brightness_set op.
> >>
> >>Having the above in mind, setting a brightness in a work queue
> >>task must be possible for all LED Class Flash drivers, regardless
> >>whether related devices have fast or slow controller.
> >>
> >>Let's recap the cost of possible solutions then:
> >>
> >>1) Moving the work queues to the LED framework
> >>
> >>   - it would probably require extending led_set_brightness and
> >>     __led_set_brightness functions by a parameter indicating whether it
> >>     should call brightness_set op in the work queue task or directly;
> >>   - all existing triggers would have to be updated accordingly;
> >>   - work queues would have to be removed from all the LED drivers;
> >>
> >>2) adding led_set_torch_brightness API
> >>
> >>   - no modifications in existing drivers and triggers would be required
> >>   - instead, only the modifications from the discussed patch would
> >>     be required
> >>
> >>Solution 1 looks cleaner but requires much more modifications.
> >
> >How about a combination of the two, i.e. option 1 with the old op remaining
> >there for compatibility with the old drivers (with a comment telling it's
> >deprecated)?
> >
> >This way new drivers will benefit from having to implement this just once,
> >and modifications to the existing drivers could be left for later.
> 
> It's OK for me, but the opinion from the LED side guys is needed here
> as well.

Ping.

> >The downside is that any old drivers wouldn't get V4L2 flash API but that's
> >entirely acceptable in my opinion since these would hardly be needed in use
> >cases that would benefit from V4L2 flash API.
> 
> In the version 4 of the patch set I changed the implementation, so that
> a flash led driver must call led_classdev_flash_register to get
> registered as a LED Flash Class device and v4l2_flash_init to get
> V4L2 Flash API. In effect old drivers will have no chance to get V4L2
> Flash API either way.

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [PATCH/RFC v4 00/21] LED / flash API integration
  2014-08-07  8:21   ` Jacek Anaszewski
  2014-08-07  8:31     ` Jacek Anaszewski
@ 2014-08-14  5:03     ` Sakari Ailus
  2014-08-14 10:35       ` Jacek Anaszewski
  1 sibling, 1 reply; 55+ messages in thread
From: Sakari Ailus @ 2014-08-14  5:03 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie

Hi Jacek,

On Thu, Aug 07, 2014 at 10:21:14AM +0200, Jacek Anaszewski wrote:
> On 08/06/2014 08:53 AM, Sakari Ailus wrote:
> >Hi Jacek,
> >
> >On Fri, Jul 11, 2014 at 04:04:03PM +0200, Jacek Anaszewski wrote:
> >...
> >>1) Who should register V4L2 Flash sub-device?
> >>
> >>LED Flash Class devices, after introduction of the Flash Manager,
> >>are not tightly coupled with any media controller. They are maintained
> >>by the Flash Manager and made available for dynamic assignment to
> >>any media system they are connected to through multiplexing devices.
> >>
> >>In the proposed rough solution, when support for V4L2 Flash sub-devices
> >>is enabled, there is a v4l2_device created for them to register in.
> >>This however implies that V4L2 Flash device will not be available
> >>in any media controller, which calls its existence into question.
> >>
> >>Therefore I'd like to consult possible ways of solving this issue.
> >>The option I see is implementing a mechanism for moving V4L2 Flash
> >>sub-devices between media controllers. A V4L2 Flash sub-device
> >>would initially be assigned to one media system in the relevant
> >>device tree binding, but it could be dynamically reassigned to
> >>the other one. However I'm not sure if media controller design
> >>is prepared for dynamic modifications of its graph and how many
> >>modifications in the existing drivers this solution would require.
> >
> >Do you have a use case where you would need to strobe a flash from multiple
> >media devices at different times, or is this entirely theoretical? Typically
> >flash controllers are connected to a single source of hardware strobe (if
> >there's one) since the flash LEDs are in fact mounted next to a specific
> >camera sensor.
> 
> I took into account such arrangements in response to your message
> [1], where you were considering configurations like "one flash but
> two
> cameras", "one camera and two flashes". And you also called for
> proposing generic solution.
> 
> One flash and two (or more) cameras case is easily conceivable -
> You even mentioned stereo cameras. One camera and many flashes
> arrangement might be useful in case of some professional devices which
> might be designed so that they would be able to apply different scene
> lighting. I haven't heard about such devices, but as you said
> such a configuration isn't unthinkable.
> 
> >If this is a real issue the way to solve it would be to have a single media
> >device instead of many.
> 
> I was considering adding media device, that would be a representation
> of a flash manager, gathering all the registered flashes. Nonetheless,
> finally I came to conclusion that a v4l2-device alone should suffice,
> just to provide a Flash Manager representation allowing for
> v4l2-flash sub-devices to register in.
> All the features provided by the media device are useless in case
> of a set of V4L2 Flash sub-devices. They couldn't have any linkage
> in such a device. The only benefit from having media device gathering
> V4L2 Flash devices would be possibility of listing them.

Not quite so. The flash is associated to the sensor (and lens) using the
group ID in the Media controller. The user space doesn't need to "know" this
association.

More complex use cases such as the above may need extensions to the Media
controller API.

> >>2) Consequences of locking the Flash Manager during flash strobe
> >>
> >>In case a LED Flash Class device depends on muxes involved in
> >>routing the other LED Flash Class device's strobe signals,
> >>the Flash Manager must be locked for the time of strobing
> >>to prevent reconfiguration of the strobe signal routing
> >>by the other device.
> >
> >I wouldn't be concerned of this in particular. It's more important we do
> >actully have V4L2 flash API supported by LED flash drivers and that they do
> >implement the API correctly.
> >
> >It's at least debatable whether you should try to prevent user space from
> >doing silly things or not. With complex devices it may relatively easily
> >lead to wrecking havoc with actual use cases which we certainly do not want.
> >
> >In this case, if you just prevent changing the routing (do you have a use
> >case for it?) while strobing, someone else could still change the routing
> >just before you strobe.
> 
> Originally I started to implementing this so that strobe signal routing
> was altered upon setting strobe source. With such an implementation
> the use case would be as follows:
> 
> 1. Process 1 sets strobe source to external
> 2. Process 2 sets strobe source to software
> 3. Process 1 strobes the flash, unaware that strobe source setting has
>    been changed
> 
> To avoid such problems I changed the implementation so that the
> routing is set in the led_flash_manager_setup_strobe function called
> from led_set_flash_strobe and led_set_external_strobe functions.
> led_flash_manager_setup_strobe sets strobe signal routing
> and strobes the flash under lock and holds it for the flash timeout
> period, which prevents spurious reconfiguration.
> 
> Nonetheless, I agree that trying to handle this problem is troublesome,
> and would affect current V4L2 Flash SPI semantics. If you don't share
> my concerns I am happy to leave this locking solution out :)

I do propose leaving it out. If we should have such a thing we should think
it in a larger scope than just flash strobing.

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [PATCH/RFC v4 15/21] media: Add registration helpers for V4L2 flash
  2014-08-14  4:34           ` Sakari Ailus
@ 2014-08-14  8:25             ` Jacek Anaszewski
  2014-08-20 14:41               ` Sakari Ailus
  0 siblings, 1 reply; 55+ messages in thread
From: Jacek Anaszewski @ 2014-08-14  8:25 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie, Hans Verkuil

On 08/14/2014 06:34 AM, Sakari Ailus wrote:
> Hi Jacek,
>
> On Mon, Aug 11, 2014 at 03:27:22PM +0200, Jacek Anaszewski wrote:
>
> ...
>
>>>>>> diff --git a/include/media/v4l2-flash.h b/include/media/v4l2-flash.h
>>>>>> new file mode 100644
>>>>>> index 0000000..effa46b
>>>>>> --- /dev/null
>>>>>> +++ b/include/media/v4l2-flash.h
>>>>>> @@ -0,0 +1,137 @@
>>>>>> +/*
>>>>>> + * V4L2 Flash LED sub-device 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>le
>>>>>> +#include <media/v4l2-event.h>
>>>>>> +#include <media/v4l2-ioctl.h>
>>>>>> +
>>>>>> +struct led_classdev_flash;
>>>>>> +struct led_classdev;
>>>>>> +enum led_brightness;
>>>>>> +
>>>>>> +struct v4l2_flash_ops {
>>>>>> +	int (*torch_brightness_set)(struct led_classdev *led_cdev,
>>>>>> +					enum led_brightness brightness);
>>>>>> +	int (*torch_brightness_update)(struct led_classdev *led_cdev);
>>>>>> +	int (*flash_brightness_set)(struct led_classdev_flash *flash,
>>>>>> +					u32 brightness);
>>>>>> +	int (*flash_brightness_update)(struct led_classdev_flash *flash);
>>>>>> +	int (*strobe_set)(struct led_classdev_flash *flash, bool state);
>>>>>> +	int (*strobe_get)(struct led_classdev_flash *flash, bool *state);
>>>>>> +	int (*timeout_set)(struct led_classdev_flash *flash, u32 timeout);
>>>>>> +	int (*indicator_brightness_set)(struct led_classdev_flash *flash,
>>>>>> +					u32 brightness);
>>>>>> +	int (*indicator_brightness_update)(struct led_classdev_flash *flash);
>>>>>> +	int (*external_strobe_set)(struct led_classdev_flash *flash,
>>>>>> +					bool enable);
>>>>>> +	int (*fault_get)(struct led_classdev_flash *flash, u32 *fault);
>>>>>> +	void (*sysfs_lock)(struct led_classdev *led_cdev);
>>>>>> +	void (*sysfs_unlock)(struct led_classdev *led_cdev);
>>>>>
>>>>> These functions are not driver specific and there's going to be just one
>>>>> implementation (I suppose). Could you refresh my memory regarding why
>>>>> the LED framework functions aren't called directly?
>>>>
>>>> These ops are required to make possible building led-class-flash as
>>>> a kernel module.
>>>
>>> Assuming you'd use the actual implementation directly, what would be the
>>> dependencies? I don't think the LED flash framework has any callbacks
>>> towards the V4L2 (LED) flash framework, does it? Please correct my
>>> understanding if I'm missing something. In Makefile format, assume all
>>> targets are .PHONY:
>>>
>>> led-flash-api: led-api
>>>
>>> v4l2-flash: led-flash-api
>>>
>>> driver: led-flash-api v4l2-flash
>>
>> LED Class Flash driver gains V4L2 Flash API when
>> CONFIG_V4L2_FLASH_LED_CLASS is defined. This is accomplished in
>> the probe function by either calling v4l2_flash_init function
>> or the macro of this name, when the CONFIG_V4L2_FLASH_LED_CLASS
>> macro isn't defined.
>>
>> If the v4l2-flash.c was to call the LED API directly, then the
>> led-class-flash module symbols would have to be available at
>> v4l2-flash.o linking time.
>
> Is this an issue? EXPORT_SYMBOL_GPL() for the relevant symbols should be
> enough.

It isn't enough. If I call e.g. led_set_flash_brightness
directly from v4l2-flash.c and configure led-class-flash to be built as
a module then I am getting "undefined reference to
led_set_flash_brightness" error during linking phase.

It happens because the linker doesn't take into account
led-flash-class.ko symbols. It is reasonable because initially
the kernel boots up without led-flash-class.ko module and
the processor wouldn't know the address to jump to in the
result of calling a led API function.
The led-class-flash.ko binary code is loaded into memory not
sooner than after executing "insmod led-class-flash.ko".

After linking dynamically with kernel the LED API function
addresses are relocated, and the LED Flash Class core can
initialize the v4l2_flash_ops structure. Then every LED Flash Class
driver can obtain the address of this structure with
led_get_v4l2_flash_ops and pass it to the v4l2_flash_init.

>> This requirement cannot be met if the led-class-flash is built
>> as a module.
>>
>> Use of function pointers in the v4l2-flash.c allows to compile it
>> into the kernel and enables the possibility of adding the V4L2 Flash
>> support conditionally, during driver probing.
>
> I'd simply decide this during kernel compilation time. If you want
> something, just enable it. v4l2_flash_init() is called directly by the
> driver in any case, so unless that is also called through a wrapper the
> driver is still directly dependent on it.

The problem is that v4l2-flash.o would have to depend on
led-class-flash.o, which when built as a module isn't available
during v4l2-flash.o linking time. In order to avoid v4l2-flash.o linking
problem, it would have to be built as a module.

Nevertheless, in this arrangement, the CONFIG_V4L2_FLASH_LED_CLASS
macro would be defined only in v4l2-flash.ko module, and
a LED Flash Class driver couldn't check whether V4L2 Flash support
is enabled. Its dependence on v4l2-flash.o would have to be fixed,
which is not what we want.

I have tested all these cases.

Best Regards,
Jacek Anaszewski

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

* Re: [PATCH/RFC v4 00/21] LED / flash API integration
  2014-08-14  5:03     ` Sakari Ailus
@ 2014-08-14 10:35       ` Jacek Anaszewski
  2014-08-15  4:48         ` Sakari Ailus
  0 siblings, 1 reply; 55+ messages in thread
From: Jacek Anaszewski @ 2014-08-14 10:35 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie

On 08/14/2014 07:03 AM, Sakari Ailus wrote:
> Hi Jacek,
>
> On Thu, Aug 07, 2014 at 10:21:14AM +0200, Jacek Anaszewski wrote:
>> On 08/06/2014 08:53 AM, Sakari Ailus wrote:
>>> Hi Jacek,
>>>
>>> On Fri, Jul 11, 2014 at 04:04:03PM +0200, Jacek Anaszewski wrote:
>>> ...
>>>> 1) Who should register V4L2 Flash sub-device?
>>>>
>>>> LED Flash Class devices, after introduction of the Flash Manager,
>>>> are not tightly coupled with any media controller. They are maintained
>>>> by the Flash Manager and made available for dynamic assignment to
>>>> any media system they are connected to through multiplexing devices.
>>>>
>>>> In the proposed rough solution, when support for V4L2 Flash sub-devices
>>>> is enabled, there is a v4l2_device created for them to register in.
>>>> This however implies that V4L2 Flash device will not be available
>>>> in any media controller, which calls its existence into question.
>>>>
>>>> Therefore I'd like to consult possible ways of solving this issue.
>>>> The option I see is implementing a mechanism for moving V4L2 Flash
>>>> sub-devices between media controllers. A V4L2 Flash sub-device
>>>> would initially be assigned to one media system in the relevant
>>>> device tree binding, but it could be dynamically reassigned to
>>>> the other one. However I'm not sure if media controller design
>>>> is prepared for dynamic modifications of its graph and how many
>>>> modifications in the existing drivers this solution would require.
>>>
>>> Do you have a use case where you would need to strobe a flash from multiple
>>> media devices at different times, or is this entirely theoretical? Typically
>>> flash controllers are connected to a single source of hardware strobe (if
>>> there's one) since the flash LEDs are in fact mounted next to a specific
>>> camera sensor.
>>
>> I took into account such arrangements in response to your message
>> [1], where you were considering configurations like "one flash but
>> two
>> cameras", "one camera and two flashes". And you also called for
>> proposing generic solution.
>>
>> One flash and two (or more) cameras case is easily conceivable -
>> You even mentioned stereo cameras. One camera and many flashes
>> arrangement might be useful in case of some professional devices which
>> might be designed so that they would be able to apply different scene
>> lighting. I haven't heard about such devices, but as you said
>> such a configuration isn't unthinkable.
>>
>>> If this is a real issue the way to solve it would be to have a single media
>>> device instead of many.
>>
>> I was considering adding media device, that would be a representation
>> of a flash manager, gathering all the registered flashes. Nonetheless,
>> finally I came to conclusion that a v4l2-device alone should suffice,
>> just to provide a Flash Manager representation allowing for
>> v4l2-flash sub-devices to register in.
>> All the features provided by the media device are useless in case
>> of a set of V4L2 Flash sub-devices. They couldn't have any linkage
>> in such a device. The only benefit from having media device gathering
>> V4L2 Flash devices would be possibility of listing them.
>
> Not quite so. The flash is associated to the sensor (and lens) using the
> group ID in the Media controller. The user space doesn't need to "know" this
> association.
>
> More complex use cases such as the above may need extensions to the Media
> controller API.

I think that I have unnecessarily complicated the issue. Generally
there will be always one media controller created for all camera
sensors available in the system. If there is a single media controller
then we can easily use async subdev registration API. A media-dev
driver would have to parse list of flash device phandles from
the ISP device's DT node and register them as async sub-devices.

Best Regards,
Jacek Anaszewski


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

* Re: [PATCH/RFC v4 00/21] LED / flash API integration
  2014-08-14 10:35       ` Jacek Anaszewski
@ 2014-08-15  4:48         ` Sakari Ailus
  0 siblings, 0 replies; 55+ messages in thread
From: Sakari Ailus @ 2014-08-15  4:48 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie, laurent.pinchart

On Thu, Aug 14, 2014 at 12:35:05PM +0200, Jacek Anaszewski wrote:
> On 08/14/2014 07:03 AM, Sakari Ailus wrote:
> >Hi Jacek,
> >
> >On Thu, Aug 07, 2014 at 10:21:14AM +0200, Jacek Anaszewski wrote:
> >>On 08/06/2014 08:53 AM, Sakari Ailus wrote:
> >>>Hi Jacek,
> >>>
> >>>On Fri, Jul 11, 2014 at 04:04:03PM +0200, Jacek Anaszewski wrote:
> >>>...
> >>>>1) Who should register V4L2 Flash sub-device?
> >>>>
> >>>>LED Flash Class devices, after introduction of the Flash Manager,
> >>>>are not tightly coupled with any media controller. They are maintained
> >>>>by the Flash Manager and made available for dynamic assignment to
> >>>>any media system they are connected to through multiplexing devices.
> >>>>
> >>>>In the proposed rough solution, when support for V4L2 Flash sub-devices
> >>>>is enabled, there is a v4l2_device created for them to register in.
> >>>>This however implies that V4L2 Flash device will not be available
> >>>>in any media controller, which calls its existence into question.
> >>>>
> >>>>Therefore I'd like to consult possible ways of solving this issue.
> >>>>The option I see is implementing a mechanism for moving V4L2 Flash
> >>>>sub-devices between media controllers. A V4L2 Flash sub-device
> >>>>would initially be assigned to one media system in the relevant
> >>>>device tree binding, but it could be dynamically reassigned to
> >>>>the other one. However I'm not sure if media controller design
> >>>>is prepared for dynamic modifications of its graph and how many
> >>>>modifications in the existing drivers this solution would require.
> >>>
> >>>Do you have a use case where you would need to strobe a flash from multiple
> >>>media devices at different times, or is this entirely theoretical? Typically
> >>>flash controllers are connected to a single source of hardware strobe (if
> >>>there's one) since the flash LEDs are in fact mounted next to a specific
> >>>camera sensor.
> >>
> >>I took into account such arrangements in response to your message
> >>[1], where you were considering configurations like "one flash but
> >>two
> >>cameras", "one camera and two flashes". And you also called for
> >>proposing generic solution.
> >>
> >>One flash and two (or more) cameras case is easily conceivable -
> >>You even mentioned stereo cameras. One camera and many flashes
> >>arrangement might be useful in case of some professional devices which
> >>might be designed so that they would be able to apply different scene
> >>lighting. I haven't heard about such devices, but as you said
> >>such a configuration isn't unthinkable.
> >>
> >>>If this is a real issue the way to solve it would be to have a single media
> >>>device instead of many.
> >>
> >>I was considering adding media device, that would be a representation
> >>of a flash manager, gathering all the registered flashes. Nonetheless,
> >>finally I came to conclusion that a v4l2-device alone should suffice,
> >>just to provide a Flash Manager representation allowing for
> >>v4l2-flash sub-devices to register in.
> >>All the features provided by the media device are useless in case
> >>of a set of V4L2 Flash sub-devices. They couldn't have any linkage
> >>in such a device. The only benefit from having media device gathering
> >>V4L2 Flash devices would be possibility of listing them.
> >
> >Not quite so. The flash is associated to the sensor (and lens) using the
> >group ID in the Media controller. The user space doesn't need to "know" this
> >association.
> >
> >More complex use cases such as the above may need extensions to the Media
> >controller API.
> 
> I think that I have unnecessarily complicated the issue. Generally
> there will be always one media controller created for all camera
> sensors available in the system. If there is a single media controller
> then we can easily use async subdev registration API. A media-dev
> driver would have to parse list of flash device phandles from
> the ISP device's DT node and register them as async sub-devices.

Currently the media device is created by a driver which is most of the time
the ISP driver on embedded systems. This driver is also responsible for
registering the flash device (nodes) to the media device. So "will" is the
word, I think.

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [PATCH/RFC v4 06/21] leds: add API for setting torch brightness
  2014-08-14  4:39           ` Sakari Ailus
@ 2014-08-18 19:55             ` Richard Purdie
  2014-08-18 20:06               ` Sakari Ailus
  0 siblings, 1 reply; 55+ messages in thread
From: Richard Purdie @ 2014-08-18 19:55 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Jacek Anaszewski, Bryan Wu, linux-leds, devicetree, linux-media,
	linux-kernel, kyungmin.park, b.zolnierkie

On Thu, 2014-08-14 at 07:39 +0300, Sakari Ailus wrote:
> Bryan and Richard,
> 
> Your opinion would be much appreciated to a question myself and Jacek were
> pondering. Please see below.
> 
> On Thu, Aug 07, 2014 at 03:12:09PM +0200, Jacek Anaszewski wrote:
> > Hi Sakari,
> > 
> > On 08/04/2014 02:50 PM, Sakari Ailus wrote:
> > >Hi Jacek,
> > >
> > >Thank you for your continued efforts on this!
> > >
> > >On Mon, Aug 04, 2014 at 02:35:26PM +0200, Jacek Anaszewski wrote:
> > >>On 07/16/2014 11:54 PM, Sakari Ailus wrote:
> > >>>Hi Jacek,
> > >>>
> > >>>Jacek Anaszewski wrote:
> > >>>...
> > >>>>diff --git a/include/linux/leds.h b/include/linux/leds.h
> > >>>>index 1a130cc..9bea9e6 100644
> > >>>>--- a/include/linux/leds.h
> > >>>>+++ b/include/linux/leds.h
> > >>>>@@ -44,11 +44,21 @@ struct led_classdev {
> > >>>>  #define LED_BLINK_ONESHOT_STOP    (1 << 18)
> > >>>>  #define LED_BLINK_INVERT    (1 << 19)
> > >>>>  #define LED_SYSFS_LOCK        (1 << 20)
> > >>>>+#define LED_DEV_CAP_TORCH    (1 << 21)
> > >>>>
> > >>>>      /* Set LED brightness level */
> > >>>>      /* Must not sleep, use a workqueue if needed */
> > >>>>      void        (*brightness_set)(struct led_classdev *led_cdev,
> > >>>>                        enum led_brightness brightness);
> > >>>>+    /*
> > >>>>+     * Set LED brightness immediately - it is required for flash led
> > >>>>+     * devices as they require setting torch brightness to have
> > >>>>immediate
> > >>>>+     * effect. brightness_set op cannot be used for this purpose because
> > >>>>+     * the led drivers schedule a work queue task in it to allow for
> > >>>>+     * being called from led-triggers, i.e. from the timer irq context.
> > >>>>+     */
> > >>>
> > >>>Do we need to classify actual devices based on this? I think it's rather
> > >>>a different API behaviour between the LED and the V4L2 APIs.
> > >>>
> > >>>On devices that are slow to control, the behaviour should be asynchronous
> > >>>over the LED API and synchronous when accessed through the V4L2 API. How
> > >>>about implementing the work queue, as I have suggested, in the
> > >>>framework, so
> > >>>that individual drivers don't need to care about this and just implement
> > >>>the
> > >>>synchronous variant of this op? A flag could be added to distinguish
> > >>>devices
> > >>>that are fast so that the work queue isn't needed.
> > >>>
> > >>>It'd be nice to avoid individual drivers having to implement multiple
> > >>>ops to
> > >>>do the same thing, just for differing user space interfacs.
> > >>>
> > >>
> > >>It is not only the matter of a device controller speed. If a flash
> > >>device is to be made accessible from the LED subsystem, then it
> > >>should be also compatible with led-triggers. Some of led-triggers
> > >>call brightness_set op from the timer irq context and thus no
> > >>locking in the callback can occur. This requirement cannot be
> > >>met i.e. if i2c bus is to be used. This is probably the primary
> > >>reason for scheduling work queue tasks in brightness_set op.
> > >>
> > >>Having the above in mind, setting a brightness in a work queue
> > >>task must be possible for all LED Class Flash drivers, regardless
> > >>whether related devices have fast or slow controller.
> > >>
> > >>Let's recap the cost of possible solutions then:
> > >>
> > >>1) Moving the work queues to the LED framework
> > >>
> > >>   - it would probably require extending led_set_brightness and
> > >>     __led_set_brightness functions by a parameter indicating whether it
> > >>     should call brightness_set op in the work queue task or directly;
> > >>   - all existing triggers would have to be updated accordingly;
> > >>   - work queues would have to be removed from all the LED drivers;
> > >>
> > >>2) adding led_set_torch_brightness API
> > >>
> > >>   - no modifications in existing drivers and triggers would be required
> > >>   - instead, only the modifications from the discussed patch would
> > >>     be required
> > >>
> > >>Solution 1 looks cleaner but requires much more modifications.
> > >
> > >How about a combination of the two, i.e. option 1 with the old op remaining
> > >there for compatibility with the old drivers (with a comment telling it's
> > >deprecated)?
> > >
> > >This way new drivers will benefit from having to implement this just once,
> > >and modifications to the existing drivers could be left for later.
> > 
> > It's OK for me, but the opinion from the LED side guys is needed here
> > as well.
> 
> Ping.

I'm not a fan of forcing everything to the lowest common denominator. At
a basic level an LED is a binary on/off so the shear levels of
complexity we end up going through is kind of scary. Forcing everything
through a workqueue due to their being some hardware out there can can't
do it in interrupt context is kind of sad and wasn't where the API set
out from.

So personally I'd prefer not to see the API changed like that however I
appreciate I've been more distant from the code of late.

Cheers,

Richard


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

* Re: [PATCH/RFC v4 06/21] leds: add API for setting torch brightness
  2014-08-18 19:55             ` Richard Purdie
@ 2014-08-18 20:06               ` Sakari Ailus
  0 siblings, 0 replies; 55+ messages in thread
From: Sakari Ailus @ 2014-08-18 20:06 UTC (permalink / raw)
  To: Richard Purdie
  Cc: Jacek Anaszewski, Bryan Wu, linux-leds, devicetree, linux-media,
	linux-kernel, kyungmin.park, b.zolnierkie

Hi Richard,

Richard Purdie wrote:
> On Thu, 2014-08-14 at 07:39 +0300, Sakari Ailus wrote:
>> Bryan and Richard,
>>
>> Your opinion would be much appreciated to a question myself and Jacek were
>> pondering. Please see below.
>>
>> On Thu, Aug 07, 2014 at 03:12:09PM +0200, Jacek Anaszewski wrote:
>>> Hi Sakari,
>>>
>>> On 08/04/2014 02:50 PM, Sakari Ailus wrote:
>>>> Hi Jacek,
>>>>
>>>> Thank you for your continued efforts on this!
>>>>
>>>> On Mon, Aug 04, 2014 at 02:35:26PM +0200, Jacek Anaszewski wrote:
>>>>> On 07/16/2014 11:54 PM, Sakari Ailus wrote:
>>>>>> Hi Jacek,
>>>>>>
>>>>>> Jacek Anaszewski wrote:
>>>>>> ...
>>>>>>> diff --git a/include/linux/leds.h b/include/linux/leds.h
>>>>>>> index 1a130cc..9bea9e6 100644
>>>>>>> --- a/include/linux/leds.h
>>>>>>> +++ b/include/linux/leds.h
>>>>>>> @@ -44,11 +44,21 @@ struct led_classdev {
>>>>>>>  #define LED_BLINK_ONESHOT_STOP    (1 << 18)
>>>>>>>  #define LED_BLINK_INVERT    (1 << 19)
>>>>>>>  #define LED_SYSFS_LOCK        (1 << 20)
>>>>>>> +#define LED_DEV_CAP_TORCH    (1 << 21)
>>>>>>>
>>>>>>>      /* Set LED brightness level */
>>>>>>>      /* Must not sleep, use a workqueue if needed */
>>>>>>>      void        (*brightness_set)(struct led_classdev *led_cdev,
>>>>>>>                        enum led_brightness brightness);
>>>>>>> +    /*
>>>>>>> +     * Set LED brightness immediately - it is required for flash led
>>>>>>> +     * devices as they require setting torch brightness to have
>>>>>>> immediate
>>>>>>> +     * effect. brightness_set op cannot be used for this purpose because
>>>>>>> +     * the led drivers schedule a work queue task in it to allow for
>>>>>>> +     * being called from led-triggers, i.e. from the timer irq context.
>>>>>>> +     */
>>>>>>
>>>>>> Do we need to classify actual devices based on this? I think it's rather
>>>>>> a different API behaviour between the LED and the V4L2 APIs.
>>>>>>
>>>>>> On devices that are slow to control, the behaviour should be asynchronous
>>>>>> over the LED API and synchronous when accessed through the V4L2 API. How
>>>>>> about implementing the work queue, as I have suggested, in the
>>>>>> framework, so
>>>>>> that individual drivers don't need to care about this and just implement
>>>>>> the
>>>>>> synchronous variant of this op? A flag could be added to distinguish
>>>>>> devices
>>>>>> that are fast so that the work queue isn't needed.
>>>>>>
>>>>>> It'd be nice to avoid individual drivers having to implement multiple
>>>>>> ops to
>>>>>> do the same thing, just for differing user space interfacs.
>>>>>>
>>>>>
>>>>> It is not only the matter of a device controller speed. If a flash
>>>>> device is to be made accessible from the LED subsystem, then it
>>>>> should be also compatible with led-triggers. Some of led-triggers
>>>>> call brightness_set op from the timer irq context and thus no
>>>>> locking in the callback can occur. This requirement cannot be
>>>>> met i.e. if i2c bus is to be used. This is probably the primary
>>>>> reason for scheduling work queue tasks in brightness_set op.
>>>>>
>>>>> Having the above in mind, setting a brightness in a work queue
>>>>> task must be possible for all LED Class Flash drivers, regardless
>>>>> whether related devices have fast or slow controller.
>>>>>
>>>>> Let's recap the cost of possible solutions then:
>>>>>
>>>>> 1) Moving the work queues to the LED framework
>>>>>
>>>>>   - it would probably require extending led_set_brightness and
>>>>>     __led_set_brightness functions by a parameter indicating whether it
>>>>>     should call brightness_set op in the work queue task or directly;
>>>>>   - all existing triggers would have to be updated accordingly;
>>>>>   - work queues would have to be removed from all the LED drivers;
>>>>>
>>>>> 2) adding led_set_torch_brightness API
>>>>>
>>>>>   - no modifications in existing drivers and triggers would be required
>>>>>   - instead, only the modifications from the discussed patch would
>>>>>     be required
>>>>>
>>>>> Solution 1 looks cleaner but requires much more modifications.
>>>>
>>>> How about a combination of the two, i.e. option 1 with the old op remaining
>>>> there for compatibility with the old drivers (with a comment telling it's
>>>> deprecated)?
>>>>
>>>> This way new drivers will benefit from having to implement this just once,
>>>> and modifications to the existing drivers could be left for later.
>>>
>>> It's OK for me, but the opinion from the LED side guys is needed here
>>> as well.
>>
>> Ping.
> 
> I'm not a fan of forcing everything to the lowest common denominator. At
> a basic level an LED is a binary on/off so the shear levels of
> complexity we end up going through is kind of scary. Forcing everything
> through a workqueue due to their being some hardware out there can can't
> do it in interrupt context is kind of sad and wasn't where the API set
> out from.

The discussion has been centered around devices that do actually need
that work queue for sleepless operation. Naturally, if the work queue
isn't required by a particular device, it should not be used.

What we'd prefer to avoid here is drivers having to implement two ways
(synchronoys and potentially asynchronous) to perform the same actions
(for V4L2 and LED API, respectively).

-- 
Kind regards,

Sakari Ailus
sakari.ailus@iki.fi

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

* Re: [PATCH/RFC v4 15/21] media: Add registration helpers for V4L2 flash
  2014-08-14  8:25             ` Jacek Anaszewski
@ 2014-08-20 14:41               ` Sakari Ailus
  2014-08-21  8:38                 ` Jacek Anaszewski
  0 siblings, 1 reply; 55+ messages in thread
From: Sakari Ailus @ 2014-08-20 14:41 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie, Hans Verkuil

Hi Jacek,

On Thu, Aug 14, 2014 at 10:25:28AM +0200, Jacek Anaszewski wrote:
> On 08/14/2014 06:34 AM, Sakari Ailus wrote:
> >Hi Jacek,
> >
> >On Mon, Aug 11, 2014 at 03:27:22PM +0200, Jacek Anaszewski wrote:
> >
> >...
> >
> >>>>>>diff --git a/include/media/v4l2-flash.h b/include/media/v4l2-flash.h
> >>>>>>new file mode 100644
> >>>>>>index 0000000..effa46b
> >>>>>>--- /dev/null
> >>>>>>+++ b/include/media/v4l2-flash.h
> >>>>>>@@ -0,0 +1,137 @@
> >>>>>>+/*
> >>>>>>+ * V4L2 Flash LED sub-device 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>le
> >>>>>>+#include <media/v4l2-event.h>
> >>>>>>+#include <media/v4l2-ioctl.h>
> >>>>>>+
> >>>>>>+struct led_classdev_flash;
> >>>>>>+struct led_classdev;
> >>>>>>+enum led_brightness;
> >>>>>>+
> >>>>>>+struct v4l2_flash_ops {
> >>>>>>+	int (*torch_brightness_set)(struct led_classdev *led_cdev,
> >>>>>>+					enum led_brightness brightness);
> >>>>>>+	int (*torch_brightness_update)(struct led_classdev *led_cdev);
> >>>>>>+	int (*flash_brightness_set)(struct led_classdev_flash *flash,
> >>>>>>+					u32 brightness);
> >>>>>>+	int (*flash_brightness_update)(struct led_classdev_flash *flash);
> >>>>>>+	int (*strobe_set)(struct led_classdev_flash *flash, bool state);
> >>>>>>+	int (*strobe_get)(struct led_classdev_flash *flash, bool *state);
> >>>>>>+	int (*timeout_set)(struct led_classdev_flash *flash, u32 timeout);
> >>>>>>+	int (*indicator_brightness_set)(struct led_classdev_flash *flash,
> >>>>>>+					u32 brightness);
> >>>>>>+	int (*indicator_brightness_update)(struct led_classdev_flash *flash);
> >>>>>>+	int (*external_strobe_set)(struct led_classdev_flash *flash,
> >>>>>>+					bool enable);
> >>>>>>+	int (*fault_get)(struct led_classdev_flash *flash, u32 *fault);
> >>>>>>+	void (*sysfs_lock)(struct led_classdev *led_cdev);
> >>>>>>+	void (*sysfs_unlock)(struct led_classdev *led_cdev);
> >>>>>
> >>>>>These functions are not driver specific and there's going to be just one
> >>>>>implementation (I suppose). Could you refresh my memory regarding why
> >>>>>the LED framework functions aren't called directly?
> >>>>
> >>>>These ops are required to make possible building led-class-flash as
> >>>>a kernel module.
> >>>
> >>>Assuming you'd use the actual implementation directly, what would be the
> >>>dependencies? I don't think the LED flash framework has any callbacks
> >>>towards the V4L2 (LED) flash framework, does it? Please correct my
> >>>understanding if I'm missing something. In Makefile format, assume all
> >>>targets are .PHONY:
> >>>
> >>>led-flash-api: led-api
> >>>
> >>>v4l2-flash: led-flash-api
> >>>
> >>>driver: led-flash-api v4l2-flash
> >>
> >>LED Class Flash driver gains V4L2 Flash API when
> >>CONFIG_V4L2_FLASH_LED_CLASS is defined. This is accomplished in
> >>the probe function by either calling v4l2_flash_init function
> >>or the macro of this name, when the CONFIG_V4L2_FLASH_LED_CLASS
> >>macro isn't defined.
> >>
> >>If the v4l2-flash.c was to call the LED API directly, then the
> >>led-class-flash module symbols would have to be available at
> >>v4l2-flash.o linking time.
> >
> >Is this an issue? EXPORT_SYMBOL_GPL() for the relevant symbols should be
> >enough.
> 
> It isn't enough. If I call e.g. led_set_flash_brightness
> directly from v4l2-flash.c and configure led-class-flash to be built as
> a module then I am getting "undefined reference to
> led_set_flash_brightness" error during linking phase.

You should not. You also should change the check as (unless you've changed
it already):

#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)

This will evaluate to non-zero if the macro arguent or the argument
postfixed with "_MODULE" is defined.

> It happens because the linker doesn't take into account
> led-flash-class.ko symbols. It is reasonable because initially
> the kernel boots up without led-flash-class.ko module and
> the processor wouldn't know the address to jump to in the
> result of calling a led API function.
> The led-class-flash.ko binary code is loaded into memory not
> sooner than after executing "insmod led-class-flash.ko".
> 
> After linking dynamically with kernel the LED API function
> addresses are relocated, and the LED Flash Class core can
> initialize the v4l2_flash_ops structure. Then every LED Flash Class
> driver can obtain the address of this structure with
> led_get_v4l2_flash_ops and pass it to the v4l2_flash_init.
> 
> >>This requirement cannot be met if the led-class-flash is built
> >>as a module.
> >>
> >>Use of function pointers in the v4l2-flash.c allows to compile it
> >>into the kernel and enables the possibility of adding the V4L2 Flash
> >>support conditionally, during driver probing.
> >
> >I'd simply decide this during kernel compilation time. If you want
> >something, just enable it. v4l2_flash_init() is called directly by the
> >driver in any case, so unless that is also called through a wrapper the
> >driver is still directly dependent on it.
> 
> The problem is that v4l2-flash.o would have to depend on
> led-class-flash.o, which when built as a module isn't available
> during v4l2-flash.o linking time. In order to avoid v4l2-flash.o linking
> problem, it would have to be built as a module.

Modules can depend on other modules, that's not an issue. All dependencies
will themselves be modules as well, i.e. if led-class-flash.o is a module,
so will be v4l2-flash.o as well --- as the former depends on the latter.

> Nevertheless, in this arrangement, the CONFIG_V4L2_FLASH_LED_CLASS
> macro would be defined only in v4l2-flash.ko module, and
> a LED Flash Class driver couldn't check whether V4L2 Flash support
> is enabled. Its dependence on v4l2-flash.o would have to be fixed,
> which is not what we want.

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi	XMPP: sailus@retiisi.org.uk

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

* Re: [PATCH/RFC v4 15/21] media: Add registration helpers for V4L2 flash
  2014-08-20 14:41               ` Sakari Ailus
@ 2014-08-21  8:38                 ` Jacek Anaszewski
  0 siblings, 0 replies; 55+ messages in thread
From: Jacek Anaszewski @ 2014-08-21  8:38 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie, Hans Verkuil

Hi Sakari,

On 08/20/2014 04:41 PM, Sakari Ailus wrote:
> Hi Jacek,

[...]
>>>>
>>>> LED Class Flash driver gains V4L2 Flash API when
>>>> CONFIG_V4L2_FLASH_LED_CLASS is defined. This is accomplished in
>>>> the probe function by either calling v4l2_flash_init function
>>>> or the macro of this name, when the CONFIG_V4L2_FLASH_LED_CLASS
>>>> macro isn't defined.
>>>>
>>>> If the v4l2-flash.c was to call the LED API directly, then the
>>>> led-class-flash module symbols would have to be available at
>>>> v4l2-flash.o linking time.
>>>
>>> Is this an issue? EXPORT_SYMBOL_GPL() for the relevant symbols should be
>>> enough.
>>
>> It isn't enough. If I call e.g. led_set_flash_brightness
>> directly from v4l2-flash.c and configure led-class-flash to be built as
>> a module then I am getting "undefined reference to
>> led_set_flash_brightness" error during linking phase.
>
> You should not. You also should change the check as (unless you've changed
> it already):
>
> #if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
>
> This will evaluate to non-zero if the macro arguent or the argument
> postfixed with "_MODULE" is defined.

I've missed this macro. Indeed, it is possible to avoid the need
for ops with it. I will fix it in the next version of the patch set.
Thanks for the hint.

Best Regards,
Jacek Anaszewski


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

end of thread, other threads:[~2014-08-21  8:38 UTC | newest]

Thread overview: 55+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-07-11 14:04 [PATCH/RFC v4 00/21] LED / flash API integration Jacek Anaszewski
2014-07-11 14:04 ` [PATCH/RFC v4 01/21] leds: make brightness type consistent across whole subsystem Jacek Anaszewski
2014-07-11 14:04 ` [PATCH/RFC v4 02/21] leds: implement sysfs interface locking mechanism Jacek Anaszewski
2014-07-16 15:35   ` Sakari Ailus
2014-07-11 14:04 ` [PATCH/RFC v4 03/21] leds: Improve and export led_update_brightness Jacek Anaszewski
2014-07-11 14:04 ` [PATCH/RFC v4 04/21] leds: Reorder include directives Jacek Anaszewski
2014-07-16 15:42   ` Sakari Ailus
2014-07-11 14:04 ` [PATCH/RFC v4 05/21] leds: avoid using deprecated DEVICE_ATTR macro Jacek Anaszewski
2014-07-16 15:46   ` Sakari Ailus
2014-07-11 14:04 ` [PATCH/RFC v4 06/21] leds: add API for setting torch brightness Jacek Anaszewski
2014-07-16 21:54   ` Sakari Ailus
2014-08-04 12:35     ` Jacek Anaszewski
2014-08-04 12:50       ` Sakari Ailus
2014-08-07 13:12         ` Jacek Anaszewski
2014-08-14  4:39           ` Sakari Ailus
2014-08-18 19:55             ` Richard Purdie
2014-08-18 20:06               ` Sakari Ailus
2014-07-11 14:04 ` [PATCH/RFC v4 07/21] of: add of_node_ncmp wrapper Jacek Anaszewski
2014-07-16 22:00   ` Sakari Ailus
2014-07-28 13:41   ` Grant Likely
2014-07-11 14:04 ` [PATCH/RFC v4 08/21] leds: Add sysfs and kernel internal API for flash LEDs Jacek Anaszewski
2014-07-11 14:04 ` [PATCH/RFC v4 09/21] Documentation: leds: Add description of LED Flash Class extension Jacek Anaszewski
2014-07-11 14:04 ` [PATCH/RFC v4 10/21] Documentation: leds: add exemplary asynchronous mux driver Jacek Anaszewski
2014-07-11 14:04 ` [PATCH/RFC v4 11/21] DT: leds: Add flash led devices related properties Jacek Anaszewski
2014-07-11 14:04 ` [PATCH/RFC v4 12/21] DT: Add documentation for LED Class Flash Manger Jacek Anaszewski
2014-07-11 14:04 ` [PATCH/RFC v4 13/21] v4l2-device: add v4l2_device_register_subdev_node API Jacek Anaszewski
2014-07-11 14:04 ` [PATCH/RFC v4 14/21] v4l2-ctrls: add control for flash strobe signal providers Jacek Anaszewski
2014-07-11 14:04 ` [PATCH/RFC v4 15/21] media: Add registration helpers for V4L2 flash Jacek Anaszewski
2014-07-21 11:12   ` Sakari Ailus
2014-08-04 14:43     ` Jacek Anaszewski
2014-08-11 12:26       ` Sakari Ailus
2014-08-11 13:27         ` Jacek Anaszewski
2014-08-11 13:45           ` Jacek Anaszewski
2014-08-14  4:34           ` Sakari Ailus
2014-08-14  8:25             ` Jacek Anaszewski
2014-08-20 14:41               ` Sakari Ailus
2014-08-21  8:38                 ` Jacek Anaszewski
2014-07-11 14:04 ` [PATCH/RFC v4 16/21] leds: Add support for max77693 mfd flash cell Jacek Anaszewski
2014-07-21 14:12   ` Sakari Ailus
2014-07-11 14:04 ` [PATCH/RFC v4 17/21] DT: Add documentation for the mfd Maxim max77693 Jacek Anaszewski
2014-07-11 14:04 ` [PATCH/RFC v4 18/21] leds: Add driver for AAT1290 current regulator Jacek Anaszewski
2014-07-11 14:04 ` [PATCH/RFC v4 19/21] of: Add Skyworks Solutions, Inc. vendor prefix Jacek Anaszewski
2014-07-11 14:04 ` [PATCH/RFC v4 20/21] DT: Add documentation for the Skyworks AAT1290 Jacek Anaszewski
2014-07-11 14:04 ` [PATCH/RFC v4 21/21] ARM: dts: add aat1290 current regulator device node Jacek Anaszewski
2014-07-16 17:19 ` [PATCH/RFC v4 00/21] LED / flash API integration Bryan Wu
2014-07-16 17:21   ` Bryan Wu
2014-08-08  6:43     ` Jacek Anaszewski
2014-08-08 16:59       ` Bryan Wu
2014-08-11 14:24         ` Jacek Anaszewski
2014-08-06  6:53 ` Sakari Ailus
2014-08-07  8:21   ` Jacek Anaszewski
2014-08-07  8:31     ` Jacek Anaszewski
2014-08-14  5:03     ` Sakari Ailus
2014-08-14 10:35       ` Jacek Anaszewski
2014-08-15  4:48         ` Sakari Ailus

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).