linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v10 0/8] LED / flash API integration
@ 2015-06-08  9:02 Jacek Anaszewski
  2015-06-08  9:02 ` [PATCH v10 1/8] Documentation: leds: Add description of v4l2-flash sub-device Jacek Anaszewski
                   ` (8 more replies)
  0 siblings, 9 replies; 24+ messages in thread
From: Jacek Anaszewski @ 2015-06-08  9:02 UTC (permalink / raw)
  To: linux-leds, linux-media
  Cc: kyungmin.park, pavel, cooloney, rpurdie, sakari.ailus,
	s.nawrocki, Jacek Anaszewski

This is tenth non-RFC version of LED / flash API integration
series [1]. It is based on linux_next-20150605 with patch [2].

================
Changes since v9
================
- improved v4l2_flash_open and v4l2_flash_close functions of
  v4l2-flash-led-class wrapper, to lock/unlock also the sysfs interface
  of indicator LED flash class device
- improved slightly LED subsystem documentation for V4L2 flash wrapper
- used proper function for getting the size of array of phandles in
  the exynos4-is media device driver and switched over to only raising
  a warning when a sensor phandle related to a flash phandle is not known
  to the media device controller

================
Changes since v8
================
- switched from samsung,flash-leds to samsun,camera-flashes DT property
- improved async sub-devices matching for exynos4-is media device
- modified v4l2-flash-led-class wrapper to incorporate indicator
  LED class deviecs

================
Changes since v7
================

- Merged patches from Sakari for v4l2-flash-led-class and V4L2 related
  patches for leds-max77693 and leds-aat1290 drivers
- applied minor modifications to the both led drivers related patches
- modified exynos4-is media device to parse new samsung,flash-led
  property, instead of 'flashes' array
- added DT documentation for samsung,flash-led property

================
Changes since v5
================
- renamed v4l2-flash module to v4l2-flash-led-class and applied
  other related modifications spotted by Sakari
- fixed not released of_node reference in max77693-led driver

================
Changes since v4
================
- adapted leds-max77693 and leds-aat1290 drivers to the recent
  modifications in leds/common.txt bindings documentation and
  changed the behaviour when properties are missing
- modified DT bindings documenation for the aforementioned
  drivers
- removed unjustified use of goto in the leds-aat1290 driver
- fixed lack of of_node_put in leds-aat1290 driver, after parsing
  DT child node 
- removed patch adding 'skyworks' vendor prefix, as the entry
  has been recently added

================
Changes since v2
================
- improved leds/common DT bindings documentation
- improved max77693-led DT documentation
- fixed validation of DT confguration for leds-max77693 by
  minimal values in joint iouts case
- removed trigger-type property from leds-max77693 bindings
  and adjusted the driver accordingly
- improved LED Flash class documentation related to v4l2-flash sub-device
  initialization
- excluded from leds-aat1290 DT bindings documentation the part
  related to handling external strobe sources

================
Changes since v1
================

- excluded exynos4-is media device related patches, as there is
  consenus required related to flash devices handling in media device
  DT bindings
- modifications around LED Flash class settings and v4l2 flash config
  initialization in LED Flash class drivers and v4l2-flash wrapper
- switched to using DT node name as a device name for leds-max77693
  and leds-aat1290 drivers, in case DT 'label' property is absent
- dropped OF dependecy for v4l2-flash wrapper
- moved LED_FAULTS definitions from led-class-flash.h to uapi/linux/leds.h
- allowed for multiple clients of v4l2-flash sub-device

======================
Changes since RFC v13:
======================

- reduced number of patches - some of them have been merged
- slightly modified max77693-led device naming
- fixed issues in v4l2-flash helpers detected with yavta
- cleaned up AAT1290 device tree documentation
- added GPIOLIB dependecy to AAT1290 related entry in Kconfig

Thanks,
Jacek Anaszewski

[1] http://www.spinics.net/lists/kernel/msg1944538.html
[2] http://www.spinics.net/lists/linux-media/msg89839.html

Jacek Anaszewski (8):
  Documentation: leds: Add description of v4l2-flash sub-device
  media: Add registration helpers for V4L2 flash sub-devices
  leds: max77693: add support for V4L2 Flash sub-device
  DT: aat1290: Document handling external strobe sources
  leds: aat1290: add support for V4L2 Flash sub-device
  exynos4-is: Improve the mechanism of async subdevs verification
  DT: Add documentation for exynos4-is 'flashes' property
  exynos4-is: Add support for v4l2-flash subdevs

 .../devicetree/bindings/leds/leds-aat1290.txt      |   36 +-
 .../devicetree/bindings/media/samsung-fimc.txt     |   10 +
 Documentation/leds/leds-class-flash.txt            |   51 ++
 drivers/leds/Kconfig                               |    1 +
 drivers/leds/leds-aat1290.c                        |  137 +++-
 drivers/leds/leds-max77693.c                       |  129 +++-
 drivers/media/platform/exynos4-is/media-dev.c      |  105 ++-
 drivers/media/platform/exynos4-is/media-dev.h      |   12 +-
 drivers/media/v4l2-core/Kconfig                    |   11 +
 drivers/media/v4l2-core/Makefile                   |    2 +
 drivers/media/v4l2-core/v4l2-flash-led-class.c     |  708 ++++++++++++++++++++
 include/media/v4l2-flash-led-class.h               |  148 ++++
 12 files changed, 1326 insertions(+), 24 deletions(-)
 create mode 100644 drivers/media/v4l2-core/v4l2-flash-led-class.c
 create mode 100644 include/media/v4l2-flash-led-class.h

-- 
1.7.9.5


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

* [PATCH v10 1/8] Documentation: leds: Add description of v4l2-flash sub-device
  2015-06-08  9:02 [PATCH v10 0/8] LED / flash API integration Jacek Anaszewski
@ 2015-06-08  9:02 ` Jacek Anaszewski
  2015-06-10 18:21   ` Bryan Wu
  2015-06-08  9:02 ` [PATCH v10 2/8] media: Add registration helpers for V4L2 flash sub-devices Jacek Anaszewski
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 24+ messages in thread
From: Jacek Anaszewski @ 2015-06-08  9:02 UTC (permalink / raw)
  To: linux-leds, linux-media
  Cc: kyungmin.park, pavel, cooloney, rpurdie, sakari.ailus,
	s.nawrocki, Jacek Anaszewski

This patch extends LED Flash class documention by
the description of interactions with v4l2-flash sub-device.

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

diff --git a/Documentation/leds/leds-class-flash.txt b/Documentation/leds/leds-class-flash.txt
index 19bb673..8da3c6f 100644
--- a/Documentation/leds/leds-class-flash.txt
+++ b/Documentation/leds/leds-class-flash.txt
@@ -20,3 +20,54 @@ Following sysfs attributes are exposed for controlling flash LED devices:
 	- max_flash_timeout
 	- flash_strobe
 	- flash_fault
+
+
+V4L2 flash wrapper for flash LEDs
+=================================
+
+A LED subsystem driver can be controlled also from the level of VideoForLinux2
+subsystem. In order to enable this CONFIG_V4L2_FLASH_LED_CLASS symbol has to
+be defined in the kernel config.
+
+The driver must call the v4l2_flash_init function to get registered in the
+V4L2 subsystem. The function takes six arguments:
+- dev       : flash device, e.g. an I2C device
+- of_node   : of_node of the LED, may be NULL if the same as device's
+- fled_cdev : LED flash class device to wrap
+- iled_cdev : LED flash class device representing indicator LED associated with
+	      fled_cdev, may be NULL
+- ops : V4L2 specific ops
+	* external_strobe_set - defines the source of the flash LED strobe -
+		V4L2_CID_FLASH_STROBE control or external source, typically
+		a sensor, which makes it possible to synchronise the flash
+		strobe start with exposure start,
+	* intensity_to_led_brightness and led_brightness_to_intensity - perform
+		enum led_brightness <-> V4L2 intensity conversion in a device
+		specific manner - they can be used for devices with non-linear
+		LED current scale.
+- config : configuration for V4L2 Flash sub-device
+	* dev_name - the name of the media entity, unique in the system,
+	* flash_faults - bitmask of flash faults that the LED flash class
+		device can report; corresponding LED_FAULT* bit definitions are
+		available in <linux/led-class-flash.h>,
+	* torch_intensity - constraints for the LED in TORCH mode
+		in microamperes,
+	* indicator_intensity - constraints for the indicator LED
+		in microamperes,
+	* has_external_strobe - determines whether the flash strobe source
+		can be switched to external,
+
+On remove the v4l2_flash_release function has to be called, which takes one
+argument - struct v4l2_flash pointer returned previously by v4l2_flash_init.
+This function can be safely called with NULL or error pointer argument.
+
+Please refer to drivers/leds/leds-max77693.c for an exemplary usage of the
+v4l2 flash wrapper.
+
+Once the V4L2 sub-device is registered by the driver which created the Media
+controller device, the sub-device node acts just as a node of a native V4L2
+flash API device would. The calls are simply routed to the LED flash API.
+
+Opening the V4L2 flash sub-device makes the LED subsystem sysfs interface
+unavailable. The interface is re-enabled after the V4L2 flash sub-device
+is closed.
-- 
1.7.9.5


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

* [PATCH v10 2/8] media: Add registration helpers for V4L2 flash sub-devices
  2015-06-08  9:02 [PATCH v10 0/8] LED / flash API integration Jacek Anaszewski
  2015-06-08  9:02 ` [PATCH v10 1/8] Documentation: leds: Add description of v4l2-flash sub-device Jacek Anaszewski
@ 2015-06-08  9:02 ` Jacek Anaszewski
  2015-06-10 17:57   ` Bryan Wu
  2015-06-08  9:02 ` [PATCH v10 3/8] leds: max77693: add support for V4L2 Flash sub-device Jacek Anaszewski
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 24+ messages in thread
From: Jacek Anaszewski @ 2015-06-08  9:02 UTC (permalink / raw)
  To: linux-leds, linux-media
  Cc: kyungmin.park, pavel, cooloney, rpurdie, sakari.ailus,
	s.nawrocki, Jacek Anaszewski, Hans Verkuil

This patch adds helper functions for registering/unregistering
LED Flash class devices as V4L2 sub-devices. 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-led-class.c |  708 ++++++++++++++++++++++++
 include/media/v4l2-flash-led-class.h           |  148 +++++
 4 files changed, 869 insertions(+)
 create mode 100644 drivers/media/v4l2-core/v4l2-flash-led-class.c
 create mode 100644 include/media/v4l2-flash-led-class.h

diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
index f7a01a7..b4b0229 100644
--- a/drivers/media/v4l2-core/Kconfig
+++ b/drivers/media/v4l2-core/Kconfig
@@ -44,6 +44,17 @@ config V4L2_MEM2MEM_DEV
         tristate
         depends on VIDEOBUF2_CORE
 
+# Used by LED subsystem flash drivers
+config V4L2_FLASH_LED_CLASS
+	tristate "V4L2 flash API for LED flash class devices"
+	depends on VIDEO_V4L2_SUBDEV_API
+	depends on LEDS_CLASS_FLASH
+	---help---
+	  Say Y here to enable V4L2 flash API support for LED flash
+	  class drivers.
+
+	  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..dc3de00 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-led-class.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-led-class.c b/drivers/media/v4l2-core/v4l2-flash-led-class.c
new file mode 100644
index 0000000..4e19dac
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-flash-led-class.c
@@ -0,0 +1,708 @@
+/*
+ * V4L2 flash LED sub-device registration helpers.
+ *
+ *	Copyright (C) 2015 Samsung Electronics Co., Ltd
+ *	Author: Jacek Anaszewski <j.anaszewski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/led-class-flash.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <media/v4l2-flash-led-class.h>
+
+#define has_flash_op(v4l2_flash, op)				\
+	(v4l2_flash && v4l2_flash->ops->op)
+
+#define call_flash_op(v4l2_flash, op, arg)			\
+		(has_flash_op(v4l2_flash, op) ?			\
+			v4l2_flash->ops->op(v4l2_flash, arg) :	\
+			-EINVAL)
+
+enum ctrl_init_data_id {
+	LED_MODE,
+	TORCH_INTENSITY,
+	FLASH_INTENSITY,
+	INDICATOR_INTENSITY,
+	FLASH_TIMEOUT,
+	STROBE_SOURCE,
+	/*
+	 * Only above values are applicable to
+	 * the 'ctrls' array in the struct v4l2_flash.
+	 */
+	FLASH_STROBE,
+	STROBE_STOP,
+	STROBE_STATUS,
+	FLASH_FAULT,
+	NUM_FLASH_CTRLS,
+};
+
+static enum led_brightness __intensity_to_led_brightness(
+					struct v4l2_ctrl *ctrl, s32 intensity)
+{
+	intensity -= ctrl->minimum;
+	intensity /= (u32) ctrl->step;
+
+	/*
+	 * Indicator LEDs, unlike torch LEDs, are turned on/off basing on
+	 * the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only.
+	 * Therefore it must be possible to set it to 0 level which in
+	 * the LED subsystem reflects LED_OFF state.
+	 */
+	if (ctrl->minimum)
+		++intensity;
+
+	return intensity;
+}
+
+static s32 __led_brightness_to_intensity(struct v4l2_ctrl *ctrl,
+					 enum led_brightness brightness)
+{
+	/*
+	 * Indicator LEDs, unlike torch LEDs, are turned on/off basing on
+	 * the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only.
+	 * Do not decrement brightness read from the LED subsystem for
+	 * indicator LED as it may equal 0. For torch LEDs this function
+	 * is called only when V4L2_FLASH_LED_MODE_TORCH is set and the
+	 * brightness read is guaranteed to be greater than 0. In the mode
+	 * V4L2_FLASH_LED_MODE_NONE the cached torch intensity value is used.
+	 */
+	if (ctrl->id != V4L2_CID_FLASH_INDICATOR_INTENSITY)
+		--brightness;
+
+	return (brightness * ctrl->step) + ctrl->minimum;
+}
+
+static void v4l2_flash_set_led_brightness(struct v4l2_flash *v4l2_flash,
+					struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
+	enum led_brightness brightness;
+
+	if (has_flash_op(v4l2_flash, intensity_to_led_brightness))
+		brightness = call_flash_op(v4l2_flash,
+					intensity_to_led_brightness,
+					ctrl->val);
+	else
+		brightness = __intensity_to_led_brightness(ctrl, ctrl->val);
+	/*
+	 * In case a LED Flash class driver provides ops for custom
+	 * brightness <-> intensity conversion, it also must have defined
+	 * related v4l2 control step == 1. In such a case a backward conversion
+	 * from led brightness to v4l2 intensity is required to find out the
+	 * the aligned intensity value.
+	 */
+	if (has_flash_op(v4l2_flash, led_brightness_to_intensity))
+		ctrl->val = call_flash_op(v4l2_flash,
+					led_brightness_to_intensity,
+					brightness);
+
+	if (ctrl == ctrls[TORCH_INTENSITY]) {
+		if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
+			return;
+
+		led_set_brightness(&v4l2_flash->fled_cdev->led_cdev,
+					brightness);
+	} else {
+		led_set_brightness(&v4l2_flash->iled_cdev->led_cdev,
+					brightness);
+	}
+}
+
+static int v4l2_flash_update_led_brightness(struct v4l2_flash *v4l2_flash,
+					struct v4l2_ctrl *ctrl)
+{
+	struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
+	struct led_classdev *led_cdev;
+	int ret;
+
+	if (ctrl == ctrls[TORCH_INTENSITY]) {
+		/*
+		 * Update torch brightness only if in TORCH_MODE. In other modes
+		 * torch led is turned off, which would spuriously inform the
+		 * user space that V4L2_CID_FLASH_TORCH_INTENSITY control value
+		 * has changed to 0.
+		 */
+		if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
+			return 0;
+		led_cdev = &v4l2_flash->fled_cdev->led_cdev;
+	} else {
+		led_cdev = &v4l2_flash->iled_cdev->led_cdev;
+	}
+
+	ret = led_update_brightness(led_cdev);
+	if (ret < 0)
+		return ret;
+
+	if (has_flash_op(v4l2_flash, led_brightness_to_intensity))
+		ctrl->val = call_flash_op(v4l2_flash,
+						led_brightness_to_intensity,
+						led_cdev->brightness);
+	else
+		ctrl->val = __led_brightness_to_intensity(ctrl,
+						led_cdev->brightness);
+
+	return 0;
+}
+
+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 *fled_cdev = v4l2_flash->fled_cdev;
+	bool is_strobing;
+	int ret;
+
+	switch (c->id) {
+	case V4L2_CID_FLASH_TORCH_INTENSITY:
+	case V4L2_CID_FLASH_INDICATOR_INTENSITY:
+		return v4l2_flash_update_led_brightness(v4l2_flash, c);
+	case V4L2_CID_FLASH_INTENSITY:
+		ret = led_update_flash_brightness(fled_cdev);
+		if (ret < 0)
+			return ret;
+		/*
+		 * No conversion is needed as LED Flash class also uses
+		 * microamperes for flash intensity units.
+		 */
+		c->val = fled_cdev->brightness.val;
+		return 0;
+	case V4L2_CID_FLASH_STROBE_STATUS:
+		ret = led_get_flash_strobe(fled_cdev, &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 */
+		return led_get_flash_fault(fled_cdev, &c->val);
+	default:
+		return -EINVAL;
+	}
+}
+
+static bool __software_strobe_mode_inactive(struct v4l2_ctrl **ctrls)
+{
+	return ((ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH) ||
+		(ctrls[STROBE_SOURCE] && (ctrls[STROBE_SOURCE]->val !=
+				V4L2_FLASH_STROBE_SOURCE_SOFTWARE)));
+}
+
+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 *fled_cdev = v4l2_flash->fled_cdev;
+	struct led_classdev *led_cdev = &fled_cdev->led_cdev;
+	struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
+	bool external_strobe;
+	int ret = 0;
+
+	switch (c->id) {
+	case V4L2_CID_FLASH_LED_MODE:
+		switch (c->val) {
+		case V4L2_FLASH_LED_MODE_NONE:
+			led_set_brightness(led_cdev, LED_OFF);
+			return led_set_flash_strobe(fled_cdev, false);
+		case V4L2_FLASH_LED_MODE_FLASH:
+			/* Turn the torch LED off */
+			led_set_brightness(led_cdev, LED_OFF);
+			if (ctrls[STROBE_SOURCE]) {
+				external_strobe = (ctrls[STROBE_SOURCE]->val ==
+					V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
+
+				ret = call_flash_op(v4l2_flash,
+						external_strobe_set,
+						external_strobe);
+			}
+			return ret;
+		case V4L2_FLASH_LED_MODE_TORCH:
+			if (ctrls[STROBE_SOURCE]) {
+				ret = call_flash_op(v4l2_flash,
+						external_strobe_set,
+						false);
+				if (ret < 0)
+					return ret;
+			}
+			/* Stop flash strobing */
+			ret = led_set_flash_strobe(fled_cdev, false);
+			if (ret < 0)
+				return ret;
+
+			v4l2_flash_set_led_brightness(v4l2_flash,
+							ctrls[TORCH_INTENSITY]);
+			return 0;
+		}
+		break;
+	case V4L2_CID_FLASH_STROBE_SOURCE:
+		external_strobe = (c->val == V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
+		/*
+		 * For some hardware arrangements setting strobe source may
+		 * affect torch mode. Therefore, if not in the flash mode,
+		 * cache only this setting. It will be applied upon switching
+		 * to flash mode.
+		 */
+		if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH)
+			return 0;
+
+		return call_flash_op(v4l2_flash, external_strobe_set,
+					external_strobe);
+	case V4L2_CID_FLASH_STROBE:
+		if (__software_strobe_mode_inactive(ctrls))
+			return -EBUSY;
+		return led_set_flash_strobe(fled_cdev, true);
+	case V4L2_CID_FLASH_STROBE_STOP:
+		if (__software_strobe_mode_inactive(ctrls))
+			return -EBUSY;
+		return led_set_flash_strobe(fled_cdev, false);
+	case V4L2_CID_FLASH_TIMEOUT:
+		/*
+		 * No conversion is needed as LED Flash class also uses
+		 * microseconds for flash timeout units.
+		 */
+		return led_set_flash_timeout(fled_cdev, c->val);
+	case V4L2_CID_FLASH_INTENSITY:
+		/*
+		 * No conversion is needed as LED Flash class also uses
+		 * microamperes for flash intensity units.
+		 */
+		return led_set_flash_brightness(fled_cdev, c->val);
+	case V4L2_CID_FLASH_TORCH_INTENSITY:
+	case V4L2_CID_FLASH_INDICATOR_INTENSITY:
+		v4l2_flash_set_led_brightness(v4l2_flash, c);
+		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 void __lfs_to_v4l2_ctrl_config(struct led_flash_setting *s,
+				struct v4l2_ctrl_config *c)
+{
+	c->min = s->min;
+	c->max = s->max;
+	c->step = s->step;
+	c->def = s->val;
+}
+
+static void __fill_ctrl_init_data(struct v4l2_flash *v4l2_flash,
+			  struct v4l2_flash_config *flash_cfg,
+			  struct v4l2_flash_ctrl_data *ctrl_init_data)
+{
+	struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
+	const struct led_flash_ops *fled_cdev_ops = fled_cdev->ops;
+	struct led_classdev *led_cdev = &fled_cdev->led_cdev;
+	struct v4l2_ctrl_config *ctrl_cfg;
+	u32 mask;
+
+	/* Init FLASH_FAULT ctrl data */
+	if (flash_cfg->flash_faults) {
+		ctrl_init_data[FLASH_FAULT].cid = V4L2_CID_FLASH_FAULT;
+		ctrl_cfg = &ctrl_init_data[FLASH_FAULT].config;
+		ctrl_cfg->id = V4L2_CID_FLASH_FAULT;
+		ctrl_cfg->max = flash_cfg->flash_faults;
+		ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
+				  V4L2_CTRL_FLAG_READ_ONLY;
+	}
+
+	/* Init FLASH_LED_MODE ctrl data */
+	mask = 1 << V4L2_FLASH_LED_MODE_NONE |
+	       1 << V4L2_FLASH_LED_MODE_TORCH;
+	if (led_cdev->flags & LED_DEV_CAP_FLASH)
+		mask |= 1 << V4L2_FLASH_LED_MODE_FLASH;
+
+	ctrl_init_data[LED_MODE].cid = V4L2_CID_FLASH_LED_MODE;
+	ctrl_cfg = &ctrl_init_data[LED_MODE].config;
+	ctrl_cfg->id = V4L2_CID_FLASH_LED_MODE;
+	ctrl_cfg->max = V4L2_FLASH_LED_MODE_TORCH;
+	ctrl_cfg->menu_skip_mask = ~mask;
+	ctrl_cfg->def = V4L2_FLASH_LED_MODE_NONE;
+	ctrl_cfg->flags = 0;
+
+	/* Init TORCH_INTENSITY ctrl data */
+	ctrl_init_data[TORCH_INTENSITY].cid = V4L2_CID_FLASH_TORCH_INTENSITY;
+	ctrl_cfg = &ctrl_init_data[TORCH_INTENSITY].config;
+	__lfs_to_v4l2_ctrl_config(&flash_cfg->torch_intensity, ctrl_cfg);
+	ctrl_cfg->id = V4L2_CID_FLASH_TORCH_INTENSITY;
+	ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
+			  V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
+
+	/* Init INDICATOR_INTENSITY ctrl data */
+	if (v4l2_flash->iled_cdev) {
+		ctrl_init_data[INDICATOR_INTENSITY].cid =
+					V4L2_CID_FLASH_INDICATOR_INTENSITY;
+		ctrl_cfg = &ctrl_init_data[INDICATOR_INTENSITY].config;
+		__lfs_to_v4l2_ctrl_config(&flash_cfg->indicator_intensity,
+					  ctrl_cfg);
+		ctrl_cfg->id = V4L2_CID_FLASH_INDICATOR_INTENSITY;
+		ctrl_cfg->min = 0;
+		ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
+				  V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
+	}
+
+	if (!(led_cdev->flags & LED_DEV_CAP_FLASH))
+		return;
+
+	/* Init FLASH_STROBE ctrl data */
+	ctrl_init_data[FLASH_STROBE].cid = V4L2_CID_FLASH_STROBE;
+	ctrl_cfg = &ctrl_init_data[FLASH_STROBE].config;
+	ctrl_cfg->id = V4L2_CID_FLASH_STROBE;
+
+	/* Init STROBE_STOP ctrl data */
+	ctrl_init_data[STROBE_STOP].cid = V4L2_CID_FLASH_STROBE_STOP;
+	ctrl_cfg = &ctrl_init_data[STROBE_STOP].config;
+	ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STOP;
+
+	/* Init FLASH_STROBE_SOURCE ctrl data */
+	if (flash_cfg->has_external_strobe) {
+		mask = (1 << V4L2_FLASH_STROBE_SOURCE_SOFTWARE) |
+		       (1 << V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
+		ctrl_init_data[STROBE_SOURCE].cid =
+					V4L2_CID_FLASH_STROBE_SOURCE;
+		ctrl_cfg = &ctrl_init_data[STROBE_SOURCE].config;
+		ctrl_cfg->id = V4L2_CID_FLASH_STROBE_SOURCE;
+		ctrl_cfg->max = V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
+		ctrl_cfg->menu_skip_mask = ~mask;
+		ctrl_cfg->def = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
+	}
+
+	/* Init STROBE_STATUS ctrl data */
+	if (fled_cdev_ops->strobe_get) {
+		ctrl_init_data[STROBE_STATUS].cid =
+					V4L2_CID_FLASH_STROBE_STATUS;
+		ctrl_cfg = &ctrl_init_data[STROBE_STATUS].config;
+		ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STATUS;
+		ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
+				  V4L2_CTRL_FLAG_READ_ONLY;
+	}
+
+	/* Init FLASH_TIMEOUT ctrl data */
+	if (fled_cdev_ops->timeout_set) {
+		ctrl_init_data[FLASH_TIMEOUT].cid = V4L2_CID_FLASH_TIMEOUT;
+		ctrl_cfg = &ctrl_init_data[FLASH_TIMEOUT].config;
+		__lfs_to_v4l2_ctrl_config(&fled_cdev->timeout, ctrl_cfg);
+		ctrl_cfg->id = V4L2_CID_FLASH_TIMEOUT;
+	}
+
+	/* Init FLASH_INTENSITY ctrl data */
+	if (fled_cdev_ops->flash_brightness_set) {
+		ctrl_init_data[FLASH_INTENSITY].cid = V4L2_CID_FLASH_INTENSITY;
+		ctrl_cfg = &ctrl_init_data[FLASH_INTENSITY].config;
+		__lfs_to_v4l2_ctrl_config(&fled_cdev->brightness, ctrl_cfg);
+		ctrl_cfg->id = V4L2_CID_FLASH_INTENSITY;
+		ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
+				  V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
+	}
+}
+
+static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash,
+				struct v4l2_flash_config *flash_cfg)
+
+{
+	struct v4l2_flash_ctrl_data *ctrl_init_data;
+	struct v4l2_ctrl *ctrl;
+	struct v4l2_ctrl_config *ctrl_cfg;
+	int i, ret, num_ctrls = 0;
+
+	v4l2_flash->ctrls = devm_kzalloc(v4l2_flash->sd.dev,
+					sizeof(*v4l2_flash->ctrls) *
+					(STROBE_SOURCE + 1), GFP_KERNEL);
+	if (!v4l2_flash->ctrls)
+		return -ENOMEM;
+
+	/* allocate memory dynamically so as not to exceed stack frame size */
+	ctrl_init_data = kcalloc(NUM_FLASH_CTRLS, sizeof(*ctrl_init_data),
+					GFP_KERNEL);
+	if (!ctrl_init_data)
+		return -ENOMEM;
+
+	__fill_ctrl_init_data(v4l2_flash, flash_cfg, ctrl_init_data);
+
+	for (i = 0; i < NUM_FLASH_CTRLS; ++i)
+		if (ctrl_init_data[i].cid)
+			++num_ctrls;
+
+	v4l2_ctrl_handler_init(&v4l2_flash->hdl, num_ctrls);
+
+	for (i = 0; i < NUM_FLASH_CTRLS; ++i) {
+		ctrl_cfg = &ctrl_init_data[i].config;
+		if (!ctrl_init_data[i].cid)
+			continue;
+
+		if (ctrl_cfg->id == V4L2_CID_FLASH_LED_MODE ||
+		    ctrl_cfg->id == V4L2_CID_FLASH_STROBE_SOURCE)
+			ctrl = v4l2_ctrl_new_std_menu(&v4l2_flash->hdl,
+						&v4l2_flash_ctrl_ops,
+						ctrl_cfg->id,
+						ctrl_cfg->max,
+						ctrl_cfg->menu_skip_mask,
+						ctrl_cfg->def);
+		else
+			ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl,
+						&v4l2_flash_ctrl_ops,
+						ctrl_cfg->id,
+						ctrl_cfg->min,
+						ctrl_cfg->max,
+						ctrl_cfg->step,
+						ctrl_cfg->def);
+
+		if (ctrl)
+			ctrl->flags |= ctrl_cfg->flags;
+
+		if (i <= STROBE_SOURCE)
+			v4l2_flash->ctrls[i] = ctrl;
+	}
+
+	kfree(ctrl_init_data);
+
+	if (v4l2_flash->hdl.error) {
+		ret = v4l2_flash->hdl.error;
+		goto error_free_handler;
+	}
+
+	v4l2_ctrl_handler_setup(&v4l2_flash->hdl);
+
+	v4l2_flash->sd.ctrl_handler = &v4l2_flash->hdl;
+
+	return 0;
+
+error_free_handler:
+	v4l2_ctrl_handler_free(&v4l2_flash->hdl);
+	return ret;
+}
+
+static int __sync_device_with_v4l2_controls(struct v4l2_flash *v4l2_flash)
+{
+	struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
+	struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
+	int ret = 0;
+
+	v4l2_flash_set_led_brightness(v4l2_flash, ctrls[TORCH_INTENSITY]);
+
+	if (ctrls[INDICATOR_INTENSITY])
+		v4l2_flash_set_led_brightness(v4l2_flash,
+						ctrls[INDICATOR_INTENSITY]);
+
+	if (ctrls[FLASH_TIMEOUT]) {
+		ret = led_set_flash_timeout(fled_cdev,
+					ctrls[FLASH_TIMEOUT]->val);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (ctrls[FLASH_INTENSITY]) {
+		ret = led_set_flash_brightness(fled_cdev,
+					ctrls[FLASH_INTENSITY]->val);
+		if (ret < 0)
+			return ret;
+	}
+
+	/*
+	 * For some hardware arrangements setting strobe source may affect
+	 * torch mode. Synchronize strobe source setting only if not in torch
+	 * mode. For torch mode case it will get synchronized upon switching
+	 * to flash mode.
+	 */
+	if (ctrls[STROBE_SOURCE] &&
+	    ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
+		ret = call_flash_op(v4l2_flash, external_strobe_set,
+					ctrls[STROBE_SOURCE]->val);
+
+	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 *fled_cdev = v4l2_flash->fled_cdev;
+	struct led_classdev *led_cdev = &fled_cdev->led_cdev;
+	struct led_classdev_flash *iled_cdev = v4l2_flash->iled_cdev;
+	struct led_classdev *led_cdev_ind = NULL;
+	int ret = 0;
+
+	if (!v4l2_fh_is_singular(&fh->vfh))
+		return 0;
+
+	mutex_lock(&led_cdev->led_access);
+
+	led_sysfs_disable(led_cdev);
+	led_trigger_remove(led_cdev);
+
+	mutex_unlock(&led_cdev->led_access);
+
+	if (iled_cdev) {
+		led_cdev_ind = &iled_cdev->led_cdev;
+
+		mutex_lock(&led_cdev_ind->led_access);
+
+		led_sysfs_disable(led_cdev_ind);
+		led_trigger_remove(led_cdev_ind);
+
+		mutex_unlock(&led_cdev_ind->led_access);
+	}
+
+	ret = __sync_device_with_v4l2_controls(v4l2_flash);
+	if (ret < 0)
+		goto out_sync_device;
+
+	return 0;
+out_sync_device:
+	mutex_lock(&led_cdev->led_access);
+	led_sysfs_enable(led_cdev);
+	mutex_unlock(&led_cdev->led_access);
+
+	if (led_cdev_ind) {
+		mutex_lock(&led_cdev_ind->led_access);
+		led_sysfs_enable(led_cdev_ind);
+		mutex_unlock(&led_cdev_ind->led_access);
+	}
+
+	return ret;
+}
+
+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 *fled_cdev = v4l2_flash->fled_cdev;
+	struct led_classdev *led_cdev = &fled_cdev->led_cdev;
+	struct led_classdev_flash *iled_cdev = v4l2_flash->iled_cdev;
+	int ret = 0;
+
+	if (!v4l2_fh_is_singular(&fh->vfh))
+		return 0;
+
+	mutex_lock(&led_cdev->led_access);
+
+	if (v4l2_flash->ctrls[STROBE_SOURCE])
+		ret = v4l2_ctrl_s_ctrl(v4l2_flash->ctrls[STROBE_SOURCE],
+				V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
+	led_sysfs_enable(led_cdev);
+
+	mutex_unlock(&led_cdev->led_access);
+
+	if (iled_cdev) {
+		struct led_classdev *led_cdev_ind = &iled_cdev->led_cdev;
+
+		mutex_lock(&led_cdev_ind->led_access);
+		led_sysfs_enable(led_cdev_ind);
+		mutex_unlock(&led_cdev_ind->led_access);
+	}
+
+	return ret;
+}
+
+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,
+};
+
+struct v4l2_flash *v4l2_flash_init(
+	struct device *dev, struct device_node *of_node,
+	struct led_classdev_flash *fled_cdev,
+	struct led_classdev_flash *iled_cdev,
+	const struct v4l2_flash_ops *ops,
+	struct v4l2_flash_config *config)
+{
+	struct v4l2_flash *v4l2_flash;
+	struct led_classdev *led_cdev = &fled_cdev->led_cdev;
+	struct v4l2_subdev *sd;
+	int ret;
+
+	if (!fled_cdev || !ops || !config)
+		return ERR_PTR(-EINVAL);
+
+	v4l2_flash = devm_kzalloc(led_cdev->dev, sizeof(*v4l2_flash),
+					GFP_KERNEL);
+	if (!v4l2_flash)
+		return ERR_PTR(-ENOMEM);
+
+	sd = &v4l2_flash->sd;
+	v4l2_flash->fled_cdev = fled_cdev;
+	v4l2_flash->iled_cdev = iled_cdev;
+	v4l2_flash->ops = ops;
+	sd->dev = dev;
+	sd->of_node = of_node;
+	v4l2_subdev_init(sd, &v4l2_flash_subdev_ops);
+	sd->internal_ops = &v4l2_flash_subdev_internal_ops;
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	strlcpy(sd->name, config->dev_name, sizeof(sd->name));
+
+	ret = media_entity_init(&sd->entity, 0, NULL, 0);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
+
+	ret = v4l2_flash_init_controls(v4l2_flash, config);
+	if (ret < 0)
+		goto err_init_controls;
+
+	if (sd->of_node)
+		of_node_get(sd->of_node);
+	else
+		of_node_get(led_cdev->dev->of_node);
+
+	ret = v4l2_async_register_subdev(sd);
+	if (ret < 0)
+		goto err_async_register_sd;
+
+	return v4l2_flash;
+
+err_async_register_sd:
+	of_node_put(led_cdev->dev->of_node);
+	v4l2_ctrl_handler_free(sd->ctrl_handler);
+err_init_controls:
+	media_entity_cleanup(&sd->entity);
+
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(v4l2_flash_init);
+
+void v4l2_flash_release(struct v4l2_flash *v4l2_flash)
+{
+	struct v4l2_subdev *sd;
+	struct led_classdev *led_cdev;
+
+	if (IS_ERR_OR_NULL(v4l2_flash))
+		return;
+
+	sd = &v4l2_flash->sd;
+	led_cdev = &v4l2_flash->fled_cdev->led_cdev;
+
+	v4l2_async_unregister_subdev(sd);
+
+	if (sd->of_node)
+		of_node_put(sd->of_node);
+	else
+		of_node_put(led_cdev->dev->of_node);
+
+	v4l2_ctrl_handler_free(sd->ctrl_handler);
+	media_entity_cleanup(&sd->entity);
+}
+EXPORT_SYMBOL_GPL(v4l2_flash_release);
+
+MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
+MODULE_DESCRIPTION("V4L2 Flash sub-device helpers");
+MODULE_LICENSE("GPL v2");
diff --git a/include/media/v4l2-flash-led-class.h b/include/media/v4l2-flash-led-class.h
new file mode 100644
index 0000000..098236c
--- /dev/null
+++ b/include/media/v4l2-flash-led-class.h
@@ -0,0 +1,148 @@
+/*
+ * V4L2 flash LED sub-device registration helpers.
+ *
+ *	Copyright (C) 2015 Samsung Electronics Co., Ltd
+ *	Author: Jacek Anaszewski <j.anaszewski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _V4L2_FLASH_H
+#define _V4L2_FLASH_H
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+struct led_classdev_flash;
+struct led_classdev;
+struct v4l2_flash;
+enum led_brightness;
+
+/*
+ * struct v4l2_flash_ctrl_data - flash control initialization data, filled
+ *				basing on the features declared by the LED flash
+ *				class driver in the v4l2_flash_config
+ * @config:	initialization data for a control
+ * @cid:	contains v4l2 flash control id if the config
+ *		field was initialized, 0 otherwise
+ */
+struct v4l2_flash_ctrl_data {
+	struct v4l2_ctrl_config config;
+	u32 cid;
+};
+
+struct v4l2_flash_ops {
+	/* setup strobing the flash by hardware pin state assertion */
+	int (*external_strobe_set)(struct v4l2_flash *v4l2_flash,
+					bool enable);
+	/* convert intensity to brightness in a device specific manner */
+	enum led_brightness (*intensity_to_led_brightness)
+		(struct v4l2_flash *v4l2_flash, s32 intensity);
+	/* convert brightness to intensity in a device specific manner */
+	s32 (*led_brightness_to_intensity)
+		(struct v4l2_flash *v4l2_flash, enum led_brightness);
+};
+
+/**
+ * struct v4l2_flash_config - V4L2 Flash sub-device initialization data
+ * @dev_name:			the name of the media entity,
+				unique in the system
+ * @torch_intensity:		constraints for the LED in torch mode
+ * @indicator_intensity:	constraints for the indicator LED
+ * @flash_faults:		bitmask of flash faults that the LED flash class
+				device can report; corresponding LED_FAULT* bit
+				definitions are available in the header file
+				<linux/led-class-flash.h>
+ * @has_external_strobe:	external strobe capability
+ */
+struct v4l2_flash_config {
+	char dev_name[32];
+	struct led_flash_setting torch_intensity;
+	struct led_flash_setting indicator_intensity;
+	u32 flash_faults;
+	unsigned int has_external_strobe:1;
+};
+
+/**
+ * struct v4l2_flash - Flash sub-device context
+ * @fled_cdev:		LED flash class device controlled by this sub-device
+ * @iled_cdev:		LED class device representing indicator LED associated
+ *			with the LED flash class device
+ * @ops:		V4L2 specific flash ops
+ * @sd:			V4L2 sub-device
+ * @hdl:		flash controls handler
+ * @ctrls:		array of pointers to controls, whose values define
+ *			the sub-device state
+ */
+struct v4l2_flash {
+	struct led_classdev_flash *fled_cdev;
+	struct led_classdev_flash *iled_cdev;
+	const struct v4l2_flash_ops *ops;
+
+	struct v4l2_subdev sd;
+	struct v4l2_ctrl_handler hdl;
+	struct v4l2_ctrl **ctrls;
+};
+
+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);
+}
+
+#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
+/**
+ * v4l2_flash_init - initialize V4L2 flash led sub-device
+ * @dev:	flash device, e.g. an I2C device
+ * @of_node:	of_node of the LED, may be NULL if the same as device's
+ * @fled_cdev:	LED flash class device to wrap
+ * @iled_cdev:	LED flash class device representing indicator LED associated
+ *		with fled_cdev, may be NULL
+ * @flash_ops:	V4L2 Flash device ops
+ * @config:	initialization data for V4L2 Flash sub-device
+ *
+ * Create V4L2 Flash sub-device wrapping given LED subsystem device.
+ *
+ * Returns: A valid pointer, or, when an error occurs, the return
+ * value is encoded using ERR_PTR(). Use IS_ERR() to check and
+ * PTR_ERR() to obtain the numeric return value.
+ */
+struct v4l2_flash *v4l2_flash_init(
+	struct device *dev, struct device_node *of_node,
+	struct led_classdev_flash *fled_cdev,
+	struct led_classdev_flash *iled_cdev,
+	const struct v4l2_flash_ops *ops,
+	struct v4l2_flash_config *config);
+
+/**
+ * v4l2_flash_release - release V4L2 Flash sub-device
+ * @flash: the V4L2 Flash sub-device to release
+ *
+ * Release V4L2 Flash sub-device.
+ */
+void v4l2_flash_release(struct v4l2_flash *v4l2_flash);
+
+#else
+static inline struct v4l2_flash *v4l2_flash_init(
+	struct device *dev, struct device_node *of_node,
+	struct led_classdev_flash *fled_cdev,
+	struct led_classdev_flash *iled_cdev,
+	const struct v4l2_flash_ops *ops,
+	struct v4l2_flash_config *config)
+{
+	return NULL;
+}
+
+static inline void v4l2_flash_release(struct v4l2_flash *v4l2_flash)
+{
+}
+#endif /* CONFIG_V4L2_FLASH_LED_CLASS */
+
+#endif /* _V4L2_FLASH_H */
-- 
1.7.9.5


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

* [PATCH v10 3/8] leds: max77693: add support for V4L2 Flash sub-device
  2015-06-08  9:02 [PATCH v10 0/8] LED / flash API integration Jacek Anaszewski
  2015-06-08  9:02 ` [PATCH v10 1/8] Documentation: leds: Add description of v4l2-flash sub-device Jacek Anaszewski
  2015-06-08  9:02 ` [PATCH v10 2/8] media: Add registration helpers for V4L2 flash sub-devices Jacek Anaszewski
@ 2015-06-08  9:02 ` Jacek Anaszewski
  2015-06-10 18:12   ` Bryan Wu
  2015-06-08  9:02 ` [PATCH v10 4/8] DT: aat1290: Document handling external strobe sources Jacek Anaszewski
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 24+ messages in thread
From: Jacek Anaszewski @ 2015-06-08  9:02 UTC (permalink / raw)
  To: linux-leds, linux-media
  Cc: kyungmin.park, pavel, cooloney, rpurdie, sakari.ailus,
	s.nawrocki, Jacek Anaszewski

Add support for V4L2 Flash sub-device to the max77693 LED Flash class
driver. The support allows for V4L2 Flash sub-device to take the control
of the LED Flash class device.

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: Sakari Ailus <sakari.ailus@iki.fi>
---
 drivers/leds/leds-max77693.c |  129 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 123 insertions(+), 6 deletions(-)

diff --git a/drivers/leds/leds-max77693.c b/drivers/leds/leds-max77693.c
index eecaa92..b8b0eec 100644
--- a/drivers/leds/leds-max77693.c
+++ b/drivers/leds/leds-max77693.c
@@ -20,6 +20,7 @@
 #include <linux/regmap.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
+#include <media/v4l2-flash-led-class.h>
 
 #define MODE_OFF		0
 #define MODE_FLASH(a)		(1 << (a))
@@ -62,6 +63,8 @@ struct max77693_sub_led {
 	struct led_classdev_flash fled_cdev;
 	/* assures led-triggers compatibility */
 	struct work_struct work_brightness_set;
+	/* V4L2 Flash device */
+	struct v4l2_flash *v4l2_flash;
 
 	/* brightness cache */
 	unsigned int torch_brightness;
@@ -627,7 +630,8 @@ static int max77693_led_flash_timeout_set(
 }
 
 static int max77693_led_parse_dt(struct max77693_led_device *led,
-				struct max77693_led_config_data *cfg)
+				struct max77693_led_config_data *cfg,
+				struct device_node **sub_nodes)
 {
 	struct device *dev = &led->pdev->dev;
 	struct max77693_sub_led *sub_leds = led->sub_leds;
@@ -674,6 +678,13 @@ static int max77693_led_parse_dt(struct max77693_led_device *led,
 			return -EINVAL;
 		}
 
+		if (sub_nodes[fled_id]) {
+			dev_err(dev,
+				"Conflicting \"led-sources\" DT properties\n");
+			return -EINVAL;
+		}
+
+		sub_nodes[fled_id] = child_node;
 		sub_leds[fled_id].fled_id = fled_id;
 
 		cfg->label[fled_id] =
@@ -786,11 +797,12 @@ static void max77693_led_validate_configuration(struct max77693_led_device *led,
 }
 
 static int max77693_led_get_configuration(struct max77693_led_device *led,
-				struct max77693_led_config_data *cfg)
+				struct max77693_led_config_data *cfg,
+				struct device_node **sub_nodes)
 {
 	int ret;
 
-	ret = max77693_led_parse_dt(led, cfg);
+	ret = max77693_led_parse_dt(led, cfg, sub_nodes);
 	if (ret < 0)
 		return ret;
 
@@ -838,6 +850,71 @@ static void max77693_init_flash_settings(struct max77693_sub_led *sub_led,
 	setting->val = setting->max;
 }
 
+#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
+
+static int max77693_led_external_strobe_set(
+				struct v4l2_flash *v4l2_flash,
+				bool enable)
+{
+	struct max77693_sub_led *sub_led =
+				flcdev_to_sub_led(v4l2_flash->fled_cdev);
+	struct max77693_led_device *led = sub_led_to_led(sub_led);
+	int fled_id = sub_led->fled_id;
+	int ret;
+
+	mutex_lock(&led->lock);
+
+	if (enable)
+		ret = max77693_add_mode(led, MODE_FLASH_EXTERNAL(fled_id));
+	else
+		ret = max77693_clear_mode(led, MODE_FLASH_EXTERNAL(fled_id));
+
+	mutex_unlock(&led->lock);
+
+	return ret;
+}
+
+static void max77693_init_v4l2_flash_config(struct max77693_sub_led *sub_led,
+				struct max77693_led_config_data *led_cfg,
+				struct v4l2_flash_config *v4l2_sd_cfg)
+{
+	struct max77693_led_device *led = sub_led_to_led(sub_led);
+	struct device *dev = &led->pdev->dev;
+	struct max77693_dev *iodev = dev_get_drvdata(dev->parent);
+	struct i2c_client *i2c = iodev->i2c;
+	struct led_flash_setting *s;
+
+	snprintf(v4l2_sd_cfg->dev_name, sizeof(v4l2_sd_cfg->dev_name),
+		 "%s %d-%04x", sub_led->fled_cdev.led_cdev.name,
+		 i2c_adapter_id(i2c->adapter), i2c->addr);
+
+	s = &v4l2_sd_cfg->torch_intensity;
+	s->min = TORCH_IOUT_MIN;
+	s->max = sub_led->fled_cdev.led_cdev.max_brightness * TORCH_IOUT_STEP;
+	s->step = TORCH_IOUT_STEP;
+	s->val = s->max;
+
+	/* Init flash faults config */
+	v4l2_sd_cfg->flash_faults = LED_FAULT_OVER_VOLTAGE |
+				LED_FAULT_SHORT_CIRCUIT |
+				LED_FAULT_OVER_CURRENT;
+
+	v4l2_sd_cfg->has_external_strobe = true;
+}
+
+static const struct v4l2_flash_ops v4l2_flash_ops = {
+	.external_strobe_set = max77693_led_external_strobe_set,
+};
+#else
+static inline void max77693_init_v4l2_flash_config(
+				struct max77693_sub_led *sub_led,
+				struct max77693_led_config_data *led_cfg,
+				struct v4l2_flash_config *v4l2_sd_cfg)
+{
+}
+static const struct v4l2_flash_ops v4l2_flash_ops;
+#endif
+
 static void max77693_init_fled_cdev(struct max77693_sub_led *sub_led,
 				struct max77693_led_config_data *led_cfg)
 {
@@ -870,12 +947,45 @@ static void max77693_init_fled_cdev(struct max77693_sub_led *sub_led,
 	sub_led->flash_timeout = fled_cdev->timeout.val;
 }
 
+static int max77693_register_led(struct max77693_sub_led *sub_led,
+				 struct max77693_led_config_data *led_cfg,
+				 struct device_node *sub_node)
+{
+	struct max77693_led_device *led = sub_led_to_led(sub_led);
+	struct led_classdev_flash *fled_cdev = &sub_led->fled_cdev;
+	struct device *dev = &led->pdev->dev;
+	struct v4l2_flash_config v4l2_sd_cfg = {};
+	int ret;
+
+	/* Register in the LED subsystem */
+	ret = led_classdev_flash_register(dev, fled_cdev);
+	if (ret < 0)
+		return ret;
+
+	max77693_init_v4l2_flash_config(sub_led, led_cfg, &v4l2_sd_cfg);
+
+	/* Register in the V4L2 subsystem. */
+	sub_led->v4l2_flash = v4l2_flash_init(dev, sub_node, fled_cdev, NULL,
+					      &v4l2_flash_ops, &v4l2_sd_cfg);
+	if (IS_ERR(sub_led->v4l2_flash)) {
+		ret = PTR_ERR(sub_led->v4l2_flash);
+		goto err_v4l2_flash_init;
+	}
+
+	return 0;
+
+err_v4l2_flash_init:
+	led_classdev_flash_unregister(fled_cdev);
+	return ret;
+}
+
 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_device *led;
 	struct max77693_sub_led *sub_leds;
+	struct device_node *sub_nodes[2] = {};
 	struct max77693_led_config_data led_cfg = {};
 	int init_fled_cdev[2], i, ret;
 
@@ -889,7 +999,7 @@ static int max77693_led_probe(struct platform_device *pdev)
 	sub_leds = led->sub_leds;
 
 	platform_set_drvdata(pdev, led);
-	ret = max77693_led_get_configuration(led, &led_cfg);
+	ret = max77693_led_get_configuration(led, &led_cfg, sub_nodes);
 	if (ret < 0)
 		return ret;
 
@@ -911,8 +1021,12 @@ static int max77693_led_probe(struct platform_device *pdev)
 		/* Initialize LED Flash class device */
 		max77693_init_fled_cdev(&sub_leds[i], &led_cfg);
 
-		/* Register LED Flash class device */
-		ret = led_classdev_flash_register(dev, &sub_leds[i].fled_cdev);
+		/*
+		 * Register LED Flash class device and corresponding
+		 * V4L2 Flash device.
+		 */
+		ret = max77693_register_led(&sub_leds[i], &led_cfg,
+						sub_nodes[i]);
 		if (ret < 0) {
 			/*
 			 * At this moment FLED1 might have been already
@@ -931,6 +1045,7 @@ err_register_led2:
 	/* It is possible than only FLED2 was to be registered */
 	if (!init_fled_cdev[FLED1])
 		goto err_register_led1;
+	v4l2_flash_release(sub_leds[FLED1].v4l2_flash);
 	led_classdev_flash_unregister(&sub_leds[FLED1].fled_cdev);
 err_register_led1:
 	mutex_destroy(&led->lock);
@@ -944,11 +1059,13 @@ static int max77693_led_remove(struct platform_device *pdev)
 	struct max77693_sub_led *sub_leds = led->sub_leds;
 
 	if (led->iout_joint || max77693_fled_used(led, FLED1)) {
+		v4l2_flash_release(sub_leds[FLED1].v4l2_flash);
 		led_classdev_flash_unregister(&sub_leds[FLED1].fled_cdev);
 		cancel_work_sync(&sub_leds[FLED1].work_brightness_set);
 	}
 
 	if (!led->iout_joint && max77693_fled_used(led, FLED2)) {
+		v4l2_flash_release(sub_leds[FLED2].v4l2_flash);
 		led_classdev_flash_unregister(&sub_leds[FLED2].fled_cdev);
 		cancel_work_sync(&sub_leds[FLED2].work_brightness_set);
 	}
-- 
1.7.9.5


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

* [PATCH v10 4/8] DT: aat1290: Document handling external strobe sources
  2015-06-08  9:02 [PATCH v10 0/8] LED / flash API integration Jacek Anaszewski
                   ` (2 preceding siblings ...)
  2015-06-08  9:02 ` [PATCH v10 3/8] leds: max77693: add support for V4L2 Flash sub-device Jacek Anaszewski
@ 2015-06-08  9:02 ` Jacek Anaszewski
  2015-06-10 18:12   ` Bryan Wu
  2015-06-08  9:02 ` [PATCH v10 5/8] leds: aat1290: add support for V4L2 Flash sub-device Jacek Anaszewski
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 24+ messages in thread
From: Jacek Anaszewski @ 2015-06-08  9:02 UTC (permalink / raw)
  To: linux-leds, linux-media
  Cc: kyungmin.park, pavel, cooloney, rpurdie, sakari.ailus,
	s.nawrocki, Jacek Anaszewski, devicetree

This patch adds documentation for a pinctrl-names property.
The property, when present, is used for switching the source
of the strobe signal for the device.

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: Sakari Ailus <sakari.ailus@iki.fi>
Cc: devicetree@vger.kernel.org
---
 .../devicetree/bindings/leds/leds-aat1290.txt      |   36 ++++++++++++++++++--
 1 file changed, 34 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/leds/leds-aat1290.txt b/Documentation/devicetree/bindings/leds/leds-aat1290.txt
index ef88b9c..c05ed91 100644
--- a/Documentation/devicetree/bindings/leds/leds-aat1290.txt
+++ b/Documentation/devicetree/bindings/leds/leds-aat1290.txt
@@ -2,7 +2,9 @@
 
 The device is controlled through two pins: FL_EN and EN_SET. The pins when,
 asserted high, enable flash strobe and movie mode (max 1/2 of flash current)
-respectively.
+respectively. In order to add a capability of selecting the strobe signal source
+(e.g. CPU or camera sensor) there is an additional switch required, independent
+of the flash chip. The switch is controlled with pin control.
 
 Required properties:
 
@@ -10,6 +12,13 @@ Required properties:
 - flen-gpios : Must be device tree identifier of the flash device FL_EN pin.
 - enset-gpios : Must be device tree identifier of the flash device EN_SET pin.
 
+Optional properties:
+- pinctrl-names : Must contain entries: "default", "host", "isp". Entries
+		"default" and "host" must refer to the same pin configuration
+		node, which sets the host as a strobe signal provider. Entry
+		"isp" must refer to the pin configuration node, which sets the
+		ISP as a strobe signal provider.
+
 A discrete LED element connected to the device must be represented by a child
 node - see Documentation/devicetree/bindings/leds/common.txt.
 
@@ -25,13 +34,22 @@ Required properties of the LED child node:
 Optional properties of the LED child node:
 - label : see Documentation/devicetree/bindings/leds/common.txt
 
-Example (by Ct = 220nF, Rset = 160kohm):
+Example (by Ct = 220nF, Rset = 160kohm and exynos4412-trats2 board with
+a switch that allows for routing strobe signal either from the host or from
+the camera sensor):
+
+#include "exynos4412.dtsi"
 
 aat1290 {
 	compatible = "skyworks,aat1290";
 	flen-gpios = <&gpj1 1 GPIO_ACTIVE_HIGH>;
 	enset-gpios = <&gpj1 2 GPIO_ACTIVE_HIGH>;
 
+	pinctrl-names = "default", "host", "isp";
+	pinctrl-0 = <&camera_flash_host>;
+	pinctrl-1 = <&camera_flash_host>;
+	pinctrl-2 = <&camera_flash_isp>;
+
 	camera_flash: flash-led {
 		label = "aat1290-flash";
 		led-max-microamp = <520833>;
@@ -39,3 +57,17 @@ aat1290 {
 		flash-timeout-us = <1940000>;
 	};
 };
+
+&pinctrl_0 {
+	camera_flash_host: camera-flash-host {
+		samsung,pins = "gpj1-0";
+		samsung,pin-function = <1>;
+		samsung,pin-val = <0>;
+	};
+
+	camera_flash_isp: camera-flash-isp {
+		samsung,pins = "gpj1-0";
+		samsung,pin-function = <1>;
+		samsung,pin-val = <1>;
+	};
+};
-- 
1.7.9.5


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

* [PATCH v10 5/8] leds: aat1290: add support for V4L2 Flash sub-device
  2015-06-08  9:02 [PATCH v10 0/8] LED / flash API integration Jacek Anaszewski
                   ` (3 preceding siblings ...)
  2015-06-08  9:02 ` [PATCH v10 4/8] DT: aat1290: Document handling external strobe sources Jacek Anaszewski
@ 2015-06-08  9:02 ` Jacek Anaszewski
  2015-06-10 18:12   ` Bryan Wu
  2015-06-08  9:02 ` [PATCH v10 6/8] exynos4-is: Improve the mechanism of async subdevs verification Jacek Anaszewski
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 24+ messages in thread
From: Jacek Anaszewski @ 2015-06-08  9:02 UTC (permalink / raw)
  To: linux-leds, linux-media
  Cc: kyungmin.park, pavel, cooloney, rpurdie, sakari.ailus,
	s.nawrocki, Jacek Anaszewski

Add support for V4L2 Flash sub-device to the aat1290 LED Flash class
driver. The support allows for V4L2 Flash sub-device to take the control
of the LED Flash class device.

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: Sakari Ailus <sakari.ailus@iki.fi>
---
 drivers/leds/Kconfig        |    1 +
 drivers/leds/leds-aat1290.c |  137 +++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 132 insertions(+), 6 deletions(-)

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 86de046..b04f82c 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -47,6 +47,7 @@ config LEDS_AAT1290
 	depends on LEDS_CLASS_FLASH
 	depends on GPIOLIB
 	depends on OF
+	depends on PINCTRL
 	help
 	 This option enables support for the LEDs on the AAT1290.
 
diff --git a/drivers/leds/leds-aat1290.c b/drivers/leds/leds-aat1290.c
index 6ea1d54..d3215d5 100644
--- a/drivers/leds/leds-aat1290.c
+++ b/drivers/leds/leds-aat1290.c
@@ -17,9 +17,11 @@
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
+#include <media/v4l2-flash-led-class.h>
 
 #define AAT1290_MOVIE_MODE_CURRENT_ADDR	17
 #define AAT1290_MAX_MM_CURR_PERCENT_0	16
@@ -52,6 +54,8 @@ struct aat1290_led_config_data {
 	u32 max_flash_current;
 	/* maximum flash timeout */
 	u32 max_flash_tm;
+	/* external strobe capability */
+	bool has_external_strobe;
 	/* max LED brightness level */
 	enum led_brightness max_brightness;
 };
@@ -64,6 +68,8 @@ struct aat1290_led {
 
 	/* corresponding LED Flash class device */
 	struct led_classdev_flash fled_cdev;
+	/* V4L2 Flash device */
+	struct v4l2_flash *v4l2_flash;
 
 	/* FLEN pin */
 	struct gpio_desc *gpio_fl_en;
@@ -230,11 +236,15 @@ static int aat1290_led_flash_timeout_set(struct led_classdev_flash *fled_cdev,
 }
 
 static int aat1290_led_parse_dt(struct aat1290_led *led,
-			struct aat1290_led_config_data *cfg)
+			struct aat1290_led_config_data *cfg,
+			struct device_node **sub_node)
 {
 	struct led_classdev *led_cdev = &led->fled_cdev.led_cdev;
 	struct device *dev = &led->pdev->dev;
 	struct device_node *child_node;
+#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
+	struct pinctrl *pinctrl;
+#endif
 	int ret = 0;
 
 	led->gpio_fl_en = devm_gpiod_get(dev, "flen");
@@ -251,6 +261,17 @@ static int aat1290_led_parse_dt(struct aat1290_led *led,
 		return ret;
 	}
 
+#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
+	pinctrl = devm_pinctrl_get_select_default(&led->pdev->dev);
+	if (IS_ERR(pinctrl)) {
+		cfg->has_external_strobe = false;
+		dev_info(dev,
+			 "No support for external strobe detected.\n");
+	} else {
+		cfg->has_external_strobe = true;
+	}
+#endif
+
 	child_node = of_get_next_available_child(dev->of_node, NULL);
 	if (!child_node) {
 		dev_err(dev, "No DT child node found for connected LED.\n");
@@ -288,6 +309,8 @@ static int aat1290_led_parse_dt(struct aat1290_led *led,
 
 	of_node_put(child_node);
 
+	*sub_node = child_node;
+
 	return ret;
 }
 
@@ -316,7 +339,8 @@ int init_mm_current_scale(struct aat1290_led *led,
 	int i, max_mm_current =
 			AAT1290_MAX_MM_CURRENT(cfg->max_flash_current);
 
-	led->mm_current_scale = kzalloc(sizeof(max_mm_current_percent),
+	led->mm_current_scale = devm_kzalloc(&led->pdev->dev,
+					sizeof(max_mm_current_percent),
 					GFP_KERNEL);
 	if (!led->mm_current_scale)
 		return -ENOMEM;
@@ -329,11 +353,12 @@ int init_mm_current_scale(struct aat1290_led *led,
 }
 
 static int aat1290_led_get_configuration(struct aat1290_led *led,
-					struct aat1290_led_config_data *cfg)
+					struct aat1290_led_config_data *cfg,
+					struct device_node **sub_node)
 {
 	int ret;
 
-	ret = aat1290_led_parse_dt(led, cfg);
+	ret = aat1290_led_parse_dt(led, cfg, sub_node);
 	if (ret < 0)
 		return ret;
 	/*
@@ -346,7 +371,10 @@ static int aat1290_led_get_configuration(struct aat1290_led *led,
 
 	aat1290_led_validate_mm_current(led, cfg);
 
-	kfree(led->mm_current_scale);
+#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
+#else
+	devm_kfree(&led->pdev->dev, led->mm_current_scale);
+#endif
 
 	return 0;
 }
@@ -365,6 +393,88 @@ static void aat1290_init_flash_timeout(struct aat1290_led *led,
 	setting->val = setting->max;
 }
 
+#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
+enum led_brightness aat1290_intensity_to_brightness(
+					struct v4l2_flash *v4l2_flash,
+					s32 intensity)
+{
+	struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
+	struct aat1290_led *led = fled_cdev_to_led(fled_cdev);
+	int i;
+
+	for (i = AAT1290_MM_CURRENT_SCALE_SIZE - 1; i >= 0; --i)
+		if (intensity >= led->mm_current_scale[i])
+			return i + 1;
+
+	return 1;
+}
+
+s32 aat1290_brightness_to_intensity(struct v4l2_flash *v4l2_flash,
+					enum led_brightness brightness)
+{
+	struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
+	struct aat1290_led *led = fled_cdev_to_led(fled_cdev);
+
+	return led->mm_current_scale[brightness - 1];
+}
+
+static int aat1290_led_external_strobe_set(struct v4l2_flash *v4l2_flash,
+						bool enable)
+{
+	struct aat1290_led *led = fled_cdev_to_led(v4l2_flash->fled_cdev);
+	struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
+	struct led_classdev *led_cdev = &fled_cdev->led_cdev;
+	struct pinctrl *pinctrl;
+
+	gpiod_direction_output(led->gpio_fl_en, 0);
+	gpiod_direction_output(led->gpio_en_set, 0);
+
+	led->movie_mode = false;
+	led_cdev->brightness = 0;
+
+	pinctrl = devm_pinctrl_get_select(&led->pdev->dev,
+						enable ? "isp" : "host");
+	if (IS_ERR(pinctrl)) {
+		dev_warn(&led->pdev->dev, "Unable to switch strobe source.\n");
+		return PTR_ERR(pinctrl);
+	}
+
+	return 0;
+}
+
+static void aat1290_init_v4l2_flash_config(struct aat1290_led *led,
+					struct aat1290_led_config_data *led_cfg,
+					struct v4l2_flash_config *v4l2_sd_cfg)
+{
+	struct led_classdev *led_cdev = &led->fled_cdev.led_cdev;
+	struct led_flash_setting *s;
+
+	strlcpy(v4l2_sd_cfg->dev_name, led_cdev->name,
+		sizeof(v4l2_sd_cfg->dev_name));
+
+	s = &v4l2_sd_cfg->torch_intensity;
+	s->min = led->mm_current_scale[0];
+	s->max = led_cfg->max_mm_current;
+	s->step = 1;
+	s->val = s->max;
+
+	v4l2_sd_cfg->has_external_strobe = led_cfg->has_external_strobe;
+}
+
+static const struct v4l2_flash_ops v4l2_flash_ops = {
+	.external_strobe_set = aat1290_led_external_strobe_set,
+	.intensity_to_led_brightness = aat1290_intensity_to_brightness,
+	.led_brightness_to_intensity = aat1290_brightness_to_intensity,
+};
+#else
+static inline void aat1290_init_v4l2_flash_config(struct aat1290_led *led,
+				struct aat1290_led_config_data *led_cfg,
+				struct v4l2_flash_config *v4l2_sd_cfg)
+{
+}
+static const struct v4l2_flash_ops v4l2_flash_ops;
+#endif
+
 static const struct led_flash_ops flash_ops = {
 	.strobe_set = aat1290_led_flash_strobe_set,
 	.timeout_set = aat1290_led_flash_timeout_set,
@@ -373,10 +483,12 @@ static const struct led_flash_ops flash_ops = {
 static int aat1290_led_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
+	struct device_node *sub_node = NULL;
 	struct aat1290_led *led;
 	struct led_classdev *led_cdev;
 	struct led_classdev_flash *fled_cdev;
 	struct aat1290_led_config_data led_cfg = {};
+	struct v4l2_flash_config v4l2_sd_cfg = {};
 	int ret;
 
 	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
@@ -390,7 +502,7 @@ static int aat1290_led_probe(struct platform_device *pdev)
 	fled_cdev->ops = &flash_ops;
 	led_cdev = &fled_cdev->led_cdev;
 
-	ret = aat1290_led_get_configuration(led, &led_cfg);
+	ret = aat1290_led_get_configuration(led, &led_cfg, &sub_node);
 	if (ret < 0)
 		return ret;
 
@@ -410,8 +522,20 @@ static int aat1290_led_probe(struct platform_device *pdev)
 	if (ret < 0)
 		goto err_flash_register;
 
+	aat1290_init_v4l2_flash_config(led, &led_cfg, &v4l2_sd_cfg);
+
+	/* Create V4L2 Flash subdev. */
+	led->v4l2_flash = v4l2_flash_init(dev, sub_node, fled_cdev, NULL,
+					  &v4l2_flash_ops, &v4l2_sd_cfg);
+	if (IS_ERR(led->v4l2_flash)) {
+		ret = PTR_ERR(led->v4l2_flash);
+		goto error_v4l2_flash_init;
+	}
+
 	return 0;
 
+error_v4l2_flash_init:
+	led_classdev_flash_unregister(fled_cdev);
 err_flash_register:
 	mutex_destroy(&led->lock);
 
@@ -422,6 +546,7 @@ 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->fled_cdev);
 	cancel_work_sync(&led->work_brightness_set);
 
-- 
1.7.9.5


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

* [PATCH v10 6/8] exynos4-is: Improve the mechanism of async subdevs verification
  2015-06-08  9:02 [PATCH v10 0/8] LED / flash API integration Jacek Anaszewski
                   ` (4 preceding siblings ...)
  2015-06-08  9:02 ` [PATCH v10 5/8] leds: aat1290: add support for V4L2 Flash sub-device Jacek Anaszewski
@ 2015-06-08  9:02 ` Jacek Anaszewski
  2015-06-10 18:17   ` Bryan Wu
  2015-06-08  9:02 ` [PATCH v10 7/8] DT: Add documentation for exynos4-is 'flashes' property Jacek Anaszewski
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 24+ messages in thread
From: Jacek Anaszewski @ 2015-06-08  9:02 UTC (permalink / raw)
  To: linux-leds, linux-media
  Cc: kyungmin.park, pavel, cooloney, rpurdie, sakari.ailus,
	s.nawrocki, Jacek Anaszewski

Avoid verifying bound async sensor sub-devices by their of_nodes,
which duplicates v4l2-async functionality, in favour of matching
them by the corresponding struct v4l2_async_subdev. The structures
are now being aggregated in the newly introduced struct fimc_async_subdevs
which allows for categorizing async sub-devices by their type upon
DT node parsing and recognizing the type easily when they're being bound.

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Sylwester Nawrocki <s.nawrocki@samsung.com>
---
 drivers/media/platform/exynos4-is/media-dev.c |   34 +++++++++++++++++++------
 drivers/media/platform/exynos4-is/media-dev.h |    8 ++++--
 2 files changed, 32 insertions(+), 10 deletions(-)

diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index 4f5586a..e3d7b70 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -331,6 +331,8 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
 				   unsigned int index)
 {
 	struct fimc_source_info *pd = &fmd->sensor[index].pdata;
+	struct v4l2_async_notifier *notifier = &fmd->subdev_notifier;
+	struct v4l2_async_subdev *asd;
 	struct device_node *rem, *ep, *np;
 	struct v4l2_of_endpoint endpoint;
 
@@ -387,9 +389,11 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
 	if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor)))
 		return -EINVAL;
 
-	fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_OF;
-	fmd->sensor[index].asd.match.of.node = rem;
-	fmd->async_subdevs[index] = &fmd->sensor[index].asd;
+	asd = &fmd->async_subdevs.sensors[index];
+	asd->match_type = V4L2_ASYNC_MATCH_OF;
+	asd->match.of.node = rem;
+	notifier->subdevs[notifier->num_subdevs++] = asd;
+	fmd->sensor[index].asd = asd;
 
 	fmd->num_sensors++;
 
@@ -1272,12 +1276,13 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
 				 struct v4l2_async_subdev *asd)
 {
 	struct fimc_md *fmd = notifier_to_fimc_md(notifier);
+	struct fimc_async_subdevs *async_subdevs = &fmd->async_subdevs;
 	struct fimc_sensor_info *si = NULL;
 	int i;
 
 	/* Find platform data for this sensor subdev */
-	for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++)
-		if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node)
+	for (i = 0; i < ARRAY_SIZE(async_subdevs->sensors); i++)
+		if (fmd->sensor[i].asd == asd)
 			si = &fmd->sensor[i];
 
 	if (si == NULL)
@@ -1317,6 +1322,19 @@ unlock:
 	return ret;
 }
 
+static int fimc_md_register_async_entities(struct fimc_md *fmd)
+{
+	struct device *dev = fmd->media_dev.dev;
+	struct v4l2_async_notifier *notifier = &fmd->subdev_notifier;
+
+	notifier->subdevs = devm_kcalloc(dev, FIMC_MAX_SENSORS,
+					sizeof(*notifier->subdevs), GFP_KERNEL);
+	if (!notifier->subdevs)
+		return -ENOMEM;
+
+	return fimc_md_register_sensor_entities(fmd);
+}
+
 static int fimc_md_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -1379,7 +1397,7 @@ static int fimc_md_probe(struct platform_device *pdev)
 		goto err_clk;
 	}
 
-	ret = fimc_md_register_sensor_entities(fmd);
+	ret = fimc_md_register_async_entities(fmd);
 	if (ret) {
 		mutex_unlock(&fmd->media_dev.graph_mutex);
 		goto err_m_ent;
@@ -1402,8 +1420,6 @@ static int fimc_md_probe(struct platform_device *pdev)
 	}
 
 	if (fmd->num_sensors > 0) {
-		fmd->subdev_notifier.subdevs = fmd->async_subdevs;
-		fmd->subdev_notifier.num_subdevs = fmd->num_sensors;
 		fmd->subdev_notifier.bound = subdev_notifier_bound;
 		fmd->subdev_notifier.complete = subdev_notifier_complete;
 		fmd->num_sensors = 0;
@@ -1412,6 +1428,8 @@ static int fimc_md_probe(struct platform_device *pdev)
 						&fmd->subdev_notifier);
 		if (ret)
 			goto err_clk_p;
+	} else {
+		devm_kfree(dev, fmd->subdev_notifier.subdevs);
 	}
 
 	return 0;
diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
index 0321454..ff6d020 100644
--- a/drivers/media/platform/exynos4-is/media-dev.h
+++ b/drivers/media/platform/exynos4-is/media-dev.h
@@ -88,11 +88,15 @@ struct fimc_camclk_info {
  */
 struct fimc_sensor_info {
 	struct fimc_source_info pdata;
-	struct v4l2_async_subdev asd;
+	struct v4l2_async_subdev *asd;
 	struct v4l2_subdev *subdev;
 	struct fimc_dev *host;
 };
 
+struct fimc_async_subdevs {
+	struct v4l2_async_subdev sensors[FIMC_MAX_SENSORS];
+};
+
 struct cam_clk {
 	struct clk_hw hw;
 	struct fimc_md *fmd;
@@ -149,7 +153,7 @@ struct fimc_md {
 	} clk_provider;
 
 	struct v4l2_async_notifier subdev_notifier;
-	struct v4l2_async_subdev *async_subdevs[FIMC_MAX_SENSORS];
+	struct fimc_async_subdevs async_subdevs;
 
 	bool user_subdev_api;
 	spinlock_t slock;
-- 
1.7.9.5


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

* [PATCH v10 7/8] DT: Add documentation for exynos4-is 'flashes' property
  2015-06-08  9:02 [PATCH v10 0/8] LED / flash API integration Jacek Anaszewski
                   ` (5 preceding siblings ...)
  2015-06-08  9:02 ` [PATCH v10 6/8] exynos4-is: Improve the mechanism of async subdevs verification Jacek Anaszewski
@ 2015-06-08  9:02 ` Jacek Anaszewski
  2015-06-10 18:18   ` Bryan Wu
  2015-06-08  9:02 ` [PATCH v10 8/8] exynos4-is: Add support for v4l2-flash subdevs Jacek Anaszewski
  2015-06-09  8:59 ` [PATCH v10 0/8] LED / flash API integration Sakari Ailus
  8 siblings, 1 reply; 24+ messages in thread
From: Jacek Anaszewski @ 2015-06-08  9:02 UTC (permalink / raw)
  To: linux-leds, linux-media
  Cc: kyungmin.park, pavel, cooloney, rpurdie, sakari.ailus,
	s.nawrocki, Jacek Anaszewski, devicetree

This patch adds a description of 'samsung,camera-flashes'
property to the samsung-fimc.txt.

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Sylwester Nawrocki <s.nawrocki@samsung.com>
Cc: devicetree@vger.kernel.org
---
 .../devicetree/bindings/media/samsung-fimc.txt     |   10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt
index 922d6f8..0554cad 100644
--- a/Documentation/devicetree/bindings/media/samsung-fimc.txt
+++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt
@@ -40,6 +40,14 @@ should be inactive. For the "active-a" state the camera port A must be activated
 and the port B deactivated and for the state "active-b" it should be the other
 way around.
 
+Optional properties:
+
+- samsung,camera-flashes - Array of pairs of phandles to the camera sensor
+	devices and flash LEDs respectively. The pairs must reflect the board
+	configuration, i.e. a sensor has to be able to strobe a flash LED by
+	hardware. Flash LED is represented by a child node of a flash LED
+	device (see Documentation/devicetree/bindings/leds/common.txt).
+
 The 'camera' node must include at least one 'fimc' child node.
 
 
@@ -166,6 +174,8 @@ Example:
 		clock-output-names = "cam_a_clkout", "cam_b_clkout";
 		pinctrl-names = "default";
 		pinctrl-0 = <&cam_port_a_clk_active>;
+		samsung,camera-flashes = <&rear_camera &rear_flash>,
+					 <&front_camera &front_flash>;
 		status = "okay";
 		#address-cells = <1>;
 		#size-cells = <1>;
-- 
1.7.9.5


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

* [PATCH v10 8/8] exynos4-is: Add support for v4l2-flash subdevs
  2015-06-08  9:02 [PATCH v10 0/8] LED / flash API integration Jacek Anaszewski
                   ` (6 preceding siblings ...)
  2015-06-08  9:02 ` [PATCH v10 7/8] DT: Add documentation for exynos4-is 'flashes' property Jacek Anaszewski
@ 2015-06-08  9:02 ` Jacek Anaszewski
  2015-06-10 18:18   ` Bryan Wu
  2015-06-09  8:59 ` [PATCH v10 0/8] LED / flash API integration Sakari Ailus
  8 siblings, 1 reply; 24+ messages in thread
From: Jacek Anaszewski @ 2015-06-08  9:02 UTC (permalink / raw)
  To: linux-leds, linux-media
  Cc: kyungmin.park, pavel, cooloney, rpurdie, sakari.ailus,
	s.nawrocki, Jacek Anaszewski

This patch adds support for external v4l2-flash devices.
The support includes parsing "camera-flashes" DT property
and asynchronous sub-device registration.

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Sylwester Nawrocki <s.nawrocki@samsung.com>
---
 drivers/media/platform/exynos4-is/media-dev.c |   75 ++++++++++++++++++++++++-
 drivers/media/platform/exynos4-is/media-dev.h |    4 ++
 2 files changed, 77 insertions(+), 2 deletions(-)

diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index e3d7b70..e387fd2 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -455,6 +455,63 @@ rpm_put:
 	return ret;
 }
 
+static int fimc_md_register_flash_entities(struct fimc_md *fmd)
+{
+	struct device_node *parent = fmd->pdev->dev.of_node, *np_sensor,
+		*np_flash;
+	struct v4l2_async_notifier *notifier = &fmd->subdev_notifier;
+	struct device *dev = &fmd->pdev->dev;
+	struct v4l2_async_subdev *asd;
+	int i, j, num_flashes = 0, num_elems;
+
+	num_elems = of_property_count_u32_elems(parent,
+						"samsung,camera-flashes");
+	/* samsung,camera-flashes property is optional */
+	if (num_elems < 0)
+		return 0;
+
+	/* samsung,camera-flashes array must have even number of elements */
+	if ((num_elems & 1) || (num_elems > FIMC_MAX_SENSORS * 2))
+		return -EINVAL;
+
+	for (i = 0; i < num_elems; i += 2) {
+		/*
+		 * The pair of camera sensor and flash LED phandles reflects
+		 * the physical connection on the board, which allows for the
+		 * camera sensor to strobe the flash by raising a hardware pin.
+		 * This property just describes the association.
+		 */
+		np_sensor = of_parse_phandle(parent,
+					     "samsung,camera-flashes", i);
+
+		for (j = 0; j < fmd->num_sensors; j++)
+			if (fmd->async_subdevs.sensors[j].match.of.node ==
+			    np_sensor)
+				break;
+
+		of_node_put(np_sensor);
+
+		/*
+		 * If the camera sensor phandle isn't known to the media device
+		 * controller, then raise a warning only.
+		 */
+		if (j == fmd->num_sensors)
+			dev_warn(dev, "sensor verification failed for a flash\n");
+
+		np_flash = of_parse_phandle(parent, "samsung,camera-flashes",
+						i + 1);
+
+		asd = &fmd->async_subdevs.flashes[num_flashes++];
+		asd->match_type = V4L2_ASYNC_MATCH_OF;
+		asd->match.of.node = np_flash;
+		notifier->subdevs[notifier->num_subdevs++] = asd;
+
+		of_node_put(np_flash);
+	}
+
+	return 0;
+}
+
 static int __of_get_csis_id(struct device_node *np)
 {
 	u32 reg = 0;
@@ -1280,6 +1337,15 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
 	struct fimc_sensor_info *si = NULL;
 	int i;
 
+	/*
+	 * Flash sub-devices are controlled independently of ISP, and thus
+	 * verify only that the sub-device being matched was previously
+	 * registered.
+	 */
+	for (i = 0; i < ARRAY_SIZE(async_subdevs->flashes); i++)
+		if (&async_subdevs->flashes[i] == asd)
+			return 0;
+
 	/* Find platform data for this sensor subdev */
 	for (i = 0; i < ARRAY_SIZE(async_subdevs->sensors); i++)
 		if (fmd->sensor[i].asd == asd)
@@ -1326,13 +1392,18 @@ static int fimc_md_register_async_entities(struct fimc_md *fmd)
 {
 	struct device *dev = fmd->media_dev.dev;
 	struct v4l2_async_notifier *notifier = &fmd->subdev_notifier;
+	int ret;
 
-	notifier->subdevs = devm_kcalloc(dev, FIMC_MAX_SENSORS,
+	notifier->subdevs = devm_kcalloc(dev, FIMC_MAX_ASYNC_ENTITIES,
 					sizeof(*notifier->subdevs), GFP_KERNEL);
 	if (!notifier->subdevs)
 		return -ENOMEM;
 
-	return fimc_md_register_sensor_entities(fmd);
+	ret = fimc_md_register_sensor_entities(fmd);
+	if (ret)
+		return -EINVAL;
+
+	return fimc_md_register_flash_entities(fmd);
 }
 
 static int fimc_md_probe(struct platform_device *pdev)
diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
index ff6d020..be9205c 100644
--- a/drivers/media/platform/exynos4-is/media-dev.h
+++ b/drivers/media/platform/exynos4-is/media-dev.h
@@ -33,7 +33,10 @@
 #define PINCTRL_STATE_IDLE	"idle"
 
 #define FIMC_MAX_SENSORS	4
+#define FIMC_MAX_FLASHES	4
+#define FIMC_MAX_ASYNC_ENTITIES	(FIMC_MAX_SENSORS + FIMC_MAX_FLASHES)
 #define FIMC_MAX_CAMCLKS	2
+
 #define DEFAULT_SENSOR_CLK_FREQ	24000000U
 
 /* LCD/ISP Writeback clocks (PIXELASYNCMx) */
@@ -95,6 +98,7 @@ struct fimc_sensor_info {
 
 struct fimc_async_subdevs {
 	struct v4l2_async_subdev sensors[FIMC_MAX_SENSORS];
+	struct v4l2_async_subdev flashes[FIMC_MAX_FLASHES];
 };
 
 struct cam_clk {
-- 
1.7.9.5


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

* Re: [PATCH v10 0/8] LED / flash API integration
  2015-06-08  9:02 [PATCH v10 0/8] LED / flash API integration Jacek Anaszewski
                   ` (7 preceding siblings ...)
  2015-06-08  9:02 ` [PATCH v10 8/8] exynos4-is: Add support for v4l2-flash subdevs Jacek Anaszewski
@ 2015-06-09  8:59 ` Sakari Ailus
  8 siblings, 0 replies; 24+ messages in thread
From: Sakari Ailus @ 2015-06-09  8:59 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, linux-media, kyungmin.park, pavel, cooloney, rpurdie,
	s.nawrocki

Thanks, Jacek!!

On Mon, Jun 08, 2015 at 11:02:17AM +0200, Jacek Anaszewski wrote:
> This is tenth non-RFC version of LED / flash API integration
> series [1]. It is based on linux_next-20150605 with patch [2].

For the set:

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] 24+ messages in thread

* Re: [PATCH v10 2/8] media: Add registration helpers for V4L2 flash sub-devices
  2015-06-08  9:02 ` [PATCH v10 2/8] media: Add registration helpers for V4L2 flash sub-devices Jacek Anaszewski
@ 2015-06-10 17:57   ` Bryan Wu
  2015-06-10 18:01     ` Bryan Wu
  0 siblings, 1 reply; 24+ messages in thread
From: Bryan Wu @ 2015-06-10 17:57 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: Linux LED Subsystem, linux-media, Kyungmin Park, Pavel Machek,
	rpurdie, Sakari Ailus, Sylwester Nawrocki, Hans Verkuil

On Mon, Jun 8, 2015 at 2:02 AM, Jacek Anaszewski
<j.anaszewski@samsung.com> wrote:
> This patch adds helper functions for registering/unregistering
> LED Flash class devices as V4L2 sub-devices. 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.
>

Please go ahead with my Ack

Acked-by: Bryan Wu <cooloney@gmail.com>


> 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-led-class.c |  708 ++++++++++++++++++++++++
>  include/media/v4l2-flash-led-class.h           |  148 +++++
>  4 files changed, 869 insertions(+)
>  create mode 100644 drivers/media/v4l2-core/v4l2-flash-led-class.c
>  create mode 100644 include/media/v4l2-flash-led-class.h
>
> diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
> index f7a01a7..b4b0229 100644
> --- a/drivers/media/v4l2-core/Kconfig
> +++ b/drivers/media/v4l2-core/Kconfig
> @@ -44,6 +44,17 @@ config V4L2_MEM2MEM_DEV
>          tristate
>          depends on VIDEOBUF2_CORE
>
> +# Used by LED subsystem flash drivers
> +config V4L2_FLASH_LED_CLASS
> +       tristate "V4L2 flash API for LED flash class devices"
> +       depends on VIDEO_V4L2_SUBDEV_API
> +       depends on LEDS_CLASS_FLASH
> +       ---help---
> +         Say Y here to enable V4L2 flash API support for LED flash
> +         class drivers.
> +
> +         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..dc3de00 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-led-class.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-led-class.c b/drivers/media/v4l2-core/v4l2-flash-led-class.c
> new file mode 100644
> index 0000000..4e19dac
> --- /dev/null
> +++ b/drivers/media/v4l2-core/v4l2-flash-led-class.c
> @@ -0,0 +1,708 @@
> +/*
> + * V4L2 flash LED sub-device registration helpers.
> + *
> + *     Copyright (C) 2015 Samsung Electronics Co., Ltd
> + *     Author: Jacek Anaszewski <j.anaszewski@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/led-class-flash.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +#include <media/v4l2-flash-led-class.h>
> +
> +#define has_flash_op(v4l2_flash, op)                           \
> +       (v4l2_flash && v4l2_flash->ops->op)
> +
> +#define call_flash_op(v4l2_flash, op, arg)                     \
> +               (has_flash_op(v4l2_flash, op) ?                 \
> +                       v4l2_flash->ops->op(v4l2_flash, arg) :  \
> +                       -EINVAL)
> +
> +enum ctrl_init_data_id {
> +       LED_MODE,
> +       TORCH_INTENSITY,
> +       FLASH_INTENSITY,
> +       INDICATOR_INTENSITY,
> +       FLASH_TIMEOUT,
> +       STROBE_SOURCE,
> +       /*
> +        * Only above values are applicable to
> +        * the 'ctrls' array in the struct v4l2_flash.
> +        */
> +       FLASH_STROBE,
> +       STROBE_STOP,
> +       STROBE_STATUS,
> +       FLASH_FAULT,
> +       NUM_FLASH_CTRLS,
> +};
> +
> +static enum led_brightness __intensity_to_led_brightness(
> +                                       struct v4l2_ctrl *ctrl, s32 intensity)
> +{
> +       intensity -= ctrl->minimum;
> +       intensity /= (u32) ctrl->step;
> +
> +       /*
> +        * Indicator LEDs, unlike torch LEDs, are turned on/off basing on
> +        * the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only.
> +        * Therefore it must be possible to set it to 0 level which in
> +        * the LED subsystem reflects LED_OFF state.
> +        */
> +       if (ctrl->minimum)
> +               ++intensity;
> +
> +       return intensity;
> +}
> +
> +static s32 __led_brightness_to_intensity(struct v4l2_ctrl *ctrl,
> +                                        enum led_brightness brightness)
> +{
> +       /*
> +        * Indicator LEDs, unlike torch LEDs, are turned on/off basing on
> +        * the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only.
> +        * Do not decrement brightness read from the LED subsystem for
> +        * indicator LED as it may equal 0. For torch LEDs this function
> +        * is called only when V4L2_FLASH_LED_MODE_TORCH is set and the
> +        * brightness read is guaranteed to be greater than 0. In the mode
> +        * V4L2_FLASH_LED_MODE_NONE the cached torch intensity value is used.
> +        */
> +       if (ctrl->id != V4L2_CID_FLASH_INDICATOR_INTENSITY)
> +               --brightness;
> +
> +       return (brightness * ctrl->step) + ctrl->minimum;
> +}
> +
> +static void v4l2_flash_set_led_brightness(struct v4l2_flash *v4l2_flash,
> +                                       struct v4l2_ctrl *ctrl)
> +{
> +       struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
> +       enum led_brightness brightness;
> +
> +       if (has_flash_op(v4l2_flash, intensity_to_led_brightness))
> +               brightness = call_flash_op(v4l2_flash,
> +                                       intensity_to_led_brightness,
> +                                       ctrl->val);
> +       else
> +               brightness = __intensity_to_led_brightness(ctrl, ctrl->val);
> +       /*
> +        * In case a LED Flash class driver provides ops for custom
> +        * brightness <-> intensity conversion, it also must have defined
> +        * related v4l2 control step == 1. In such a case a backward conversion
> +        * from led brightness to v4l2 intensity is required to find out the
> +        * the aligned intensity value.
> +        */
> +       if (has_flash_op(v4l2_flash, led_brightness_to_intensity))
> +               ctrl->val = call_flash_op(v4l2_flash,
> +                                       led_brightness_to_intensity,
> +                                       brightness);
> +
> +       if (ctrl == ctrls[TORCH_INTENSITY]) {
> +               if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
> +                       return;
> +
> +               led_set_brightness(&v4l2_flash->fled_cdev->led_cdev,
> +                                       brightness);
> +       } else {
> +               led_set_brightness(&v4l2_flash->iled_cdev->led_cdev,
> +                                       brightness);
> +       }
> +}
> +
> +static int v4l2_flash_update_led_brightness(struct v4l2_flash *v4l2_flash,
> +                                       struct v4l2_ctrl *ctrl)
> +{
> +       struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
> +       struct led_classdev *led_cdev;
> +       int ret;
> +
> +       if (ctrl == ctrls[TORCH_INTENSITY]) {
> +               /*
> +                * Update torch brightness only if in TORCH_MODE. In other modes
> +                * torch led is turned off, which would spuriously inform the
> +                * user space that V4L2_CID_FLASH_TORCH_INTENSITY control value
> +                * has changed to 0.
> +                */
> +               if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
> +                       return 0;
> +               led_cdev = &v4l2_flash->fled_cdev->led_cdev;
> +       } else {
> +               led_cdev = &v4l2_flash->iled_cdev->led_cdev;
> +       }
> +
> +       ret = led_update_brightness(led_cdev);
> +       if (ret < 0)
> +               return ret;
> +
> +       if (has_flash_op(v4l2_flash, led_brightness_to_intensity))
> +               ctrl->val = call_flash_op(v4l2_flash,
> +                                               led_brightness_to_intensity,
> +                                               led_cdev->brightness);
> +       else
> +               ctrl->val = __led_brightness_to_intensity(ctrl,
> +                                               led_cdev->brightness);
> +
> +       return 0;
> +}
> +
> +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 *fled_cdev = v4l2_flash->fled_cdev;
> +       bool is_strobing;
> +       int ret;
> +
> +       switch (c->id) {
> +       case V4L2_CID_FLASH_TORCH_INTENSITY:
> +       case V4L2_CID_FLASH_INDICATOR_INTENSITY:
> +               return v4l2_flash_update_led_brightness(v4l2_flash, c);
> +       case V4L2_CID_FLASH_INTENSITY:
> +               ret = led_update_flash_brightness(fled_cdev);
> +               if (ret < 0)
> +                       return ret;
> +               /*
> +                * No conversion is needed as LED Flash class also uses
> +                * microamperes for flash intensity units.
> +                */
> +               c->val = fled_cdev->brightness.val;
> +               return 0;
> +       case V4L2_CID_FLASH_STROBE_STATUS:
> +               ret = led_get_flash_strobe(fled_cdev, &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 */
> +               return led_get_flash_fault(fled_cdev, &c->val);
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static bool __software_strobe_mode_inactive(struct v4l2_ctrl **ctrls)
> +{
> +       return ((ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH) ||
> +               (ctrls[STROBE_SOURCE] && (ctrls[STROBE_SOURCE]->val !=
> +                               V4L2_FLASH_STROBE_SOURCE_SOFTWARE)));
> +}
> +
> +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 *fled_cdev = v4l2_flash->fled_cdev;
> +       struct led_classdev *led_cdev = &fled_cdev->led_cdev;
> +       struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
> +       bool external_strobe;
> +       int ret = 0;
> +
> +       switch (c->id) {
> +       case V4L2_CID_FLASH_LED_MODE:
> +               switch (c->val) {
> +               case V4L2_FLASH_LED_MODE_NONE:
> +                       led_set_brightness(led_cdev, LED_OFF);
> +                       return led_set_flash_strobe(fled_cdev, false);
> +               case V4L2_FLASH_LED_MODE_FLASH:
> +                       /* Turn the torch LED off */
> +                       led_set_brightness(led_cdev, LED_OFF);
> +                       if (ctrls[STROBE_SOURCE]) {
> +                               external_strobe = (ctrls[STROBE_SOURCE]->val ==
> +                                       V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
> +
> +                               ret = call_flash_op(v4l2_flash,
> +                                               external_strobe_set,
> +                                               external_strobe);
> +                       }
> +                       return ret;
> +               case V4L2_FLASH_LED_MODE_TORCH:
> +                       if (ctrls[STROBE_SOURCE]) {
> +                               ret = call_flash_op(v4l2_flash,
> +                                               external_strobe_set,
> +                                               false);
> +                               if (ret < 0)
> +                                       return ret;
> +                       }
> +                       /* Stop flash strobing */
> +                       ret = led_set_flash_strobe(fled_cdev, false);
> +                       if (ret < 0)
> +                               return ret;
> +
> +                       v4l2_flash_set_led_brightness(v4l2_flash,
> +                                                       ctrls[TORCH_INTENSITY]);
> +                       return 0;
> +               }
> +               break;
> +       case V4L2_CID_FLASH_STROBE_SOURCE:
> +               external_strobe = (c->val == V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
> +               /*
> +                * For some hardware arrangements setting strobe source may
> +                * affect torch mode. Therefore, if not in the flash mode,
> +                * cache only this setting. It will be applied upon switching
> +                * to flash mode.
> +                */
> +               if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH)
> +                       return 0;
> +
> +               return call_flash_op(v4l2_flash, external_strobe_set,
> +                                       external_strobe);
> +       case V4L2_CID_FLASH_STROBE:
> +               if (__software_strobe_mode_inactive(ctrls))
> +                       return -EBUSY;
> +               return led_set_flash_strobe(fled_cdev, true);
> +       case V4L2_CID_FLASH_STROBE_STOP:
> +               if (__software_strobe_mode_inactive(ctrls))
> +                       return -EBUSY;
> +               return led_set_flash_strobe(fled_cdev, false);
> +       case V4L2_CID_FLASH_TIMEOUT:
> +               /*
> +                * No conversion is needed as LED Flash class also uses
> +                * microseconds for flash timeout units.
> +                */
> +               return led_set_flash_timeout(fled_cdev, c->val);
> +       case V4L2_CID_FLASH_INTENSITY:
> +               /*
> +                * No conversion is needed as LED Flash class also uses
> +                * microamperes for flash intensity units.
> +                */
> +               return led_set_flash_brightness(fled_cdev, c->val);
> +       case V4L2_CID_FLASH_TORCH_INTENSITY:
> +       case V4L2_CID_FLASH_INDICATOR_INTENSITY:
> +               v4l2_flash_set_led_brightness(v4l2_flash, c);
> +               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 void __lfs_to_v4l2_ctrl_config(struct led_flash_setting *s,
> +                               struct v4l2_ctrl_config *c)
> +{
> +       c->min = s->min;
> +       c->max = s->max;
> +       c->step = s->step;
> +       c->def = s->val;
> +}
> +
> +static void __fill_ctrl_init_data(struct v4l2_flash *v4l2_flash,
> +                         struct v4l2_flash_config *flash_cfg,
> +                         struct v4l2_flash_ctrl_data *ctrl_init_data)
> +{
> +       struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
> +       const struct led_flash_ops *fled_cdev_ops = fled_cdev->ops;
> +       struct led_classdev *led_cdev = &fled_cdev->led_cdev;
> +       struct v4l2_ctrl_config *ctrl_cfg;
> +       u32 mask;
> +
> +       /* Init FLASH_FAULT ctrl data */
> +       if (flash_cfg->flash_faults) {
> +               ctrl_init_data[FLASH_FAULT].cid = V4L2_CID_FLASH_FAULT;
> +               ctrl_cfg = &ctrl_init_data[FLASH_FAULT].config;
> +               ctrl_cfg->id = V4L2_CID_FLASH_FAULT;
> +               ctrl_cfg->max = flash_cfg->flash_faults;
> +               ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
> +                                 V4L2_CTRL_FLAG_READ_ONLY;
> +       }
> +
> +       /* Init FLASH_LED_MODE ctrl data */
> +       mask = 1 << V4L2_FLASH_LED_MODE_NONE |
> +              1 << V4L2_FLASH_LED_MODE_TORCH;
> +       if (led_cdev->flags & LED_DEV_CAP_FLASH)
> +               mask |= 1 << V4L2_FLASH_LED_MODE_FLASH;
> +
> +       ctrl_init_data[LED_MODE].cid = V4L2_CID_FLASH_LED_MODE;
> +       ctrl_cfg = &ctrl_init_data[LED_MODE].config;
> +       ctrl_cfg->id = V4L2_CID_FLASH_LED_MODE;
> +       ctrl_cfg->max = V4L2_FLASH_LED_MODE_TORCH;
> +       ctrl_cfg->menu_skip_mask = ~mask;
> +       ctrl_cfg->def = V4L2_FLASH_LED_MODE_NONE;
> +       ctrl_cfg->flags = 0;
> +
> +       /* Init TORCH_INTENSITY ctrl data */
> +       ctrl_init_data[TORCH_INTENSITY].cid = V4L2_CID_FLASH_TORCH_INTENSITY;
> +       ctrl_cfg = &ctrl_init_data[TORCH_INTENSITY].config;
> +       __lfs_to_v4l2_ctrl_config(&flash_cfg->torch_intensity, ctrl_cfg);
> +       ctrl_cfg->id = V4L2_CID_FLASH_TORCH_INTENSITY;
> +       ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
> +                         V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
> +
> +       /* Init INDICATOR_INTENSITY ctrl data */
> +       if (v4l2_flash->iled_cdev) {
> +               ctrl_init_data[INDICATOR_INTENSITY].cid =
> +                                       V4L2_CID_FLASH_INDICATOR_INTENSITY;
> +               ctrl_cfg = &ctrl_init_data[INDICATOR_INTENSITY].config;
> +               __lfs_to_v4l2_ctrl_config(&flash_cfg->indicator_intensity,
> +                                         ctrl_cfg);
> +               ctrl_cfg->id = V4L2_CID_FLASH_INDICATOR_INTENSITY;
> +               ctrl_cfg->min = 0;
> +               ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
> +                                 V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
> +       }
> +
> +       if (!(led_cdev->flags & LED_DEV_CAP_FLASH))
> +               return;
> +
> +       /* Init FLASH_STROBE ctrl data */
> +       ctrl_init_data[FLASH_STROBE].cid = V4L2_CID_FLASH_STROBE;
> +       ctrl_cfg = &ctrl_init_data[FLASH_STROBE].config;
> +       ctrl_cfg->id = V4L2_CID_FLASH_STROBE;
> +
> +       /* Init STROBE_STOP ctrl data */
> +       ctrl_init_data[STROBE_STOP].cid = V4L2_CID_FLASH_STROBE_STOP;
> +       ctrl_cfg = &ctrl_init_data[STROBE_STOP].config;
> +       ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STOP;
> +
> +       /* Init FLASH_STROBE_SOURCE ctrl data */
> +       if (flash_cfg->has_external_strobe) {
> +               mask = (1 << V4L2_FLASH_STROBE_SOURCE_SOFTWARE) |
> +                      (1 << V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
> +               ctrl_init_data[STROBE_SOURCE].cid =
> +                                       V4L2_CID_FLASH_STROBE_SOURCE;
> +               ctrl_cfg = &ctrl_init_data[STROBE_SOURCE].config;
> +               ctrl_cfg->id = V4L2_CID_FLASH_STROBE_SOURCE;
> +               ctrl_cfg->max = V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
> +               ctrl_cfg->menu_skip_mask = ~mask;
> +               ctrl_cfg->def = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
> +       }
> +
> +       /* Init STROBE_STATUS ctrl data */
> +       if (fled_cdev_ops->strobe_get) {
> +               ctrl_init_data[STROBE_STATUS].cid =
> +                                       V4L2_CID_FLASH_STROBE_STATUS;
> +               ctrl_cfg = &ctrl_init_data[STROBE_STATUS].config;
> +               ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STATUS;
> +               ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
> +                                 V4L2_CTRL_FLAG_READ_ONLY;
> +       }
> +
> +       /* Init FLASH_TIMEOUT ctrl data */
> +       if (fled_cdev_ops->timeout_set) {
> +               ctrl_init_data[FLASH_TIMEOUT].cid = V4L2_CID_FLASH_TIMEOUT;
> +               ctrl_cfg = &ctrl_init_data[FLASH_TIMEOUT].config;
> +               __lfs_to_v4l2_ctrl_config(&fled_cdev->timeout, ctrl_cfg);
> +               ctrl_cfg->id = V4L2_CID_FLASH_TIMEOUT;
> +       }
> +
> +       /* Init FLASH_INTENSITY ctrl data */
> +       if (fled_cdev_ops->flash_brightness_set) {
> +               ctrl_init_data[FLASH_INTENSITY].cid = V4L2_CID_FLASH_INTENSITY;
> +               ctrl_cfg = &ctrl_init_data[FLASH_INTENSITY].config;
> +               __lfs_to_v4l2_ctrl_config(&fled_cdev->brightness, ctrl_cfg);
> +               ctrl_cfg->id = V4L2_CID_FLASH_INTENSITY;
> +               ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
> +                                 V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
> +       }
> +}
> +
> +static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash,
> +                               struct v4l2_flash_config *flash_cfg)
> +
> +{
> +       struct v4l2_flash_ctrl_data *ctrl_init_data;
> +       struct v4l2_ctrl *ctrl;
> +       struct v4l2_ctrl_config *ctrl_cfg;
> +       int i, ret, num_ctrls = 0;
> +
> +       v4l2_flash->ctrls = devm_kzalloc(v4l2_flash->sd.dev,
> +                                       sizeof(*v4l2_flash->ctrls) *
> +                                       (STROBE_SOURCE + 1), GFP_KERNEL);
> +       if (!v4l2_flash->ctrls)
> +               return -ENOMEM;
> +
> +       /* allocate memory dynamically so as not to exceed stack frame size */
> +       ctrl_init_data = kcalloc(NUM_FLASH_CTRLS, sizeof(*ctrl_init_data),
> +                                       GFP_KERNEL);
> +       if (!ctrl_init_data)
> +               return -ENOMEM;
> +
> +       __fill_ctrl_init_data(v4l2_flash, flash_cfg, ctrl_init_data);
> +
> +       for (i = 0; i < NUM_FLASH_CTRLS; ++i)
> +               if (ctrl_init_data[i].cid)
> +                       ++num_ctrls;
> +
> +       v4l2_ctrl_handler_init(&v4l2_flash->hdl, num_ctrls);
> +
> +       for (i = 0; i < NUM_FLASH_CTRLS; ++i) {
> +               ctrl_cfg = &ctrl_init_data[i].config;
> +               if (!ctrl_init_data[i].cid)
> +                       continue;
> +
> +               if (ctrl_cfg->id == V4L2_CID_FLASH_LED_MODE ||
> +                   ctrl_cfg->id == V4L2_CID_FLASH_STROBE_SOURCE)
> +                       ctrl = v4l2_ctrl_new_std_menu(&v4l2_flash->hdl,
> +                                               &v4l2_flash_ctrl_ops,
> +                                               ctrl_cfg->id,
> +                                               ctrl_cfg->max,
> +                                               ctrl_cfg->menu_skip_mask,
> +                                               ctrl_cfg->def);
> +               else
> +                       ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl,
> +                                               &v4l2_flash_ctrl_ops,
> +                                               ctrl_cfg->id,
> +                                               ctrl_cfg->min,
> +                                               ctrl_cfg->max,
> +                                               ctrl_cfg->step,
> +                                               ctrl_cfg->def);
> +
> +               if (ctrl)
> +                       ctrl->flags |= ctrl_cfg->flags;
> +
> +               if (i <= STROBE_SOURCE)
> +                       v4l2_flash->ctrls[i] = ctrl;
> +       }
> +
> +       kfree(ctrl_init_data);
> +
> +       if (v4l2_flash->hdl.error) {
> +               ret = v4l2_flash->hdl.error;
> +               goto error_free_handler;
> +       }
> +
> +       v4l2_ctrl_handler_setup(&v4l2_flash->hdl);
> +
> +       v4l2_flash->sd.ctrl_handler = &v4l2_flash->hdl;
> +
> +       return 0;
> +
> +error_free_handler:
> +       v4l2_ctrl_handler_free(&v4l2_flash->hdl);
> +       return ret;
> +}
> +
> +static int __sync_device_with_v4l2_controls(struct v4l2_flash *v4l2_flash)
> +{
> +       struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
> +       struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
> +       int ret = 0;
> +
> +       v4l2_flash_set_led_brightness(v4l2_flash, ctrls[TORCH_INTENSITY]);
> +
> +       if (ctrls[INDICATOR_INTENSITY])
> +               v4l2_flash_set_led_brightness(v4l2_flash,
> +                                               ctrls[INDICATOR_INTENSITY]);
> +
> +       if (ctrls[FLASH_TIMEOUT]) {
> +               ret = led_set_flash_timeout(fled_cdev,
> +                                       ctrls[FLASH_TIMEOUT]->val);
> +               if (ret < 0)
> +                       return ret;
> +       }
> +
> +       if (ctrls[FLASH_INTENSITY]) {
> +               ret = led_set_flash_brightness(fled_cdev,
> +                                       ctrls[FLASH_INTENSITY]->val);
> +               if (ret < 0)
> +                       return ret;
> +       }
> +
> +       /*
> +        * For some hardware arrangements setting strobe source may affect
> +        * torch mode. Synchronize strobe source setting only if not in torch
> +        * mode. For torch mode case it will get synchronized upon switching
> +        * to flash mode.
> +        */
> +       if (ctrls[STROBE_SOURCE] &&
> +           ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
> +               ret = call_flash_op(v4l2_flash, external_strobe_set,
> +                                       ctrls[STROBE_SOURCE]->val);
> +
> +       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 *fled_cdev = v4l2_flash->fled_cdev;
> +       struct led_classdev *led_cdev = &fled_cdev->led_cdev;
> +       struct led_classdev_flash *iled_cdev = v4l2_flash->iled_cdev;
> +       struct led_classdev *led_cdev_ind = NULL;
> +       int ret = 0;
> +
> +       if (!v4l2_fh_is_singular(&fh->vfh))
> +               return 0;
> +
> +       mutex_lock(&led_cdev->led_access);
> +
> +       led_sysfs_disable(led_cdev);
> +       led_trigger_remove(led_cdev);
> +
> +       mutex_unlock(&led_cdev->led_access);
> +
> +       if (iled_cdev) {
> +               led_cdev_ind = &iled_cdev->led_cdev;
> +
> +               mutex_lock(&led_cdev_ind->led_access);
> +
> +               led_sysfs_disable(led_cdev_ind);
> +               led_trigger_remove(led_cdev_ind);
> +
> +               mutex_unlock(&led_cdev_ind->led_access);
> +       }
> +
> +       ret = __sync_device_with_v4l2_controls(v4l2_flash);
> +       if (ret < 0)
> +               goto out_sync_device;
> +
> +       return 0;
> +out_sync_device:
> +       mutex_lock(&led_cdev->led_access);
> +       led_sysfs_enable(led_cdev);
> +       mutex_unlock(&led_cdev->led_access);
> +
> +       if (led_cdev_ind) {
> +               mutex_lock(&led_cdev_ind->led_access);
> +               led_sysfs_enable(led_cdev_ind);
> +               mutex_unlock(&led_cdev_ind->led_access);
> +       }
> +
> +       return ret;
> +}
> +
> +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 *fled_cdev = v4l2_flash->fled_cdev;
> +       struct led_classdev *led_cdev = &fled_cdev->led_cdev;
> +       struct led_classdev_flash *iled_cdev = v4l2_flash->iled_cdev;
> +       int ret = 0;
> +
> +       if (!v4l2_fh_is_singular(&fh->vfh))
> +               return 0;
> +
> +       mutex_lock(&led_cdev->led_access);
> +
> +       if (v4l2_flash->ctrls[STROBE_SOURCE])
> +               ret = v4l2_ctrl_s_ctrl(v4l2_flash->ctrls[STROBE_SOURCE],
> +                               V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
> +       led_sysfs_enable(led_cdev);
> +
> +       mutex_unlock(&led_cdev->led_access);
> +
> +       if (iled_cdev) {
> +               struct led_classdev *led_cdev_ind = &iled_cdev->led_cdev;
> +
> +               mutex_lock(&led_cdev_ind->led_access);
> +               led_sysfs_enable(led_cdev_ind);
> +               mutex_unlock(&led_cdev_ind->led_access);
> +       }
> +
> +       return ret;
> +}
> +
> +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,
> +};
> +
> +struct v4l2_flash *v4l2_flash_init(
> +       struct device *dev, struct device_node *of_node,
> +       struct led_classdev_flash *fled_cdev,
> +       struct led_classdev_flash *iled_cdev,
> +       const struct v4l2_flash_ops *ops,
> +       struct v4l2_flash_config *config)
> +{
> +       struct v4l2_flash *v4l2_flash;
> +       struct led_classdev *led_cdev = &fled_cdev->led_cdev;
> +       struct v4l2_subdev *sd;
> +       int ret;
> +
> +       if (!fled_cdev || !ops || !config)
> +               return ERR_PTR(-EINVAL);
> +
> +       v4l2_flash = devm_kzalloc(led_cdev->dev, sizeof(*v4l2_flash),
> +                                       GFP_KERNEL);
> +       if (!v4l2_flash)
> +               return ERR_PTR(-ENOMEM);
> +
> +       sd = &v4l2_flash->sd;
> +       v4l2_flash->fled_cdev = fled_cdev;
> +       v4l2_flash->iled_cdev = iled_cdev;
> +       v4l2_flash->ops = ops;
> +       sd->dev = dev;
> +       sd->of_node = of_node;
> +       v4l2_subdev_init(sd, &v4l2_flash_subdev_ops);
> +       sd->internal_ops = &v4l2_flash_subdev_internal_ops;
> +       sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +       strlcpy(sd->name, config->dev_name, sizeof(sd->name));
> +
> +       ret = media_entity_init(&sd->entity, 0, NULL, 0);
> +       if (ret < 0)
> +               return ERR_PTR(ret);
> +
> +       sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
> +
> +       ret = v4l2_flash_init_controls(v4l2_flash, config);
> +       if (ret < 0)
> +               goto err_init_controls;
> +
> +       if (sd->of_node)
> +               of_node_get(sd->of_node);
> +       else
> +               of_node_get(led_cdev->dev->of_node);
> +
> +       ret = v4l2_async_register_subdev(sd);
> +       if (ret < 0)
> +               goto err_async_register_sd;
> +
> +       return v4l2_flash;
> +
> +err_async_register_sd:
> +       of_node_put(led_cdev->dev->of_node);
> +       v4l2_ctrl_handler_free(sd->ctrl_handler);
> +err_init_controls:
> +       media_entity_cleanup(&sd->entity);
> +
> +       return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(v4l2_flash_init);
> +
> +void v4l2_flash_release(struct v4l2_flash *v4l2_flash)
> +{
> +       struct v4l2_subdev *sd;
> +       struct led_classdev *led_cdev;
> +
> +       if (IS_ERR_OR_NULL(v4l2_flash))
> +               return;
> +
> +       sd = &v4l2_flash->sd;
> +       led_cdev = &v4l2_flash->fled_cdev->led_cdev;
> +
> +       v4l2_async_unregister_subdev(sd);
> +
> +       if (sd->of_node)
> +               of_node_put(sd->of_node);
> +       else
> +               of_node_put(led_cdev->dev->of_node);
> +
> +       v4l2_ctrl_handler_free(sd->ctrl_handler);
> +       media_entity_cleanup(&sd->entity);
> +}
> +EXPORT_SYMBOL_GPL(v4l2_flash_release);
> +
> +MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
> +MODULE_DESCRIPTION("V4L2 Flash sub-device helpers");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/media/v4l2-flash-led-class.h b/include/media/v4l2-flash-led-class.h
> new file mode 100644
> index 0000000..098236c
> --- /dev/null
> +++ b/include/media/v4l2-flash-led-class.h
> @@ -0,0 +1,148 @@
> +/*
> + * V4L2 flash LED sub-device registration helpers.
> + *
> + *     Copyright (C) 2015 Samsung Electronics Co., Ltd
> + *     Author: Jacek Anaszewski <j.anaszewski@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#ifndef _V4L2_FLASH_H
> +#define _V4L2_FLASH_H
> +
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-subdev.h>
> +
> +struct led_classdev_flash;
> +struct led_classdev;
> +struct v4l2_flash;
> +enum led_brightness;
> +
> +/*
> + * struct v4l2_flash_ctrl_data - flash control initialization data, filled
> + *                             basing on the features declared by the LED flash
> + *                             class driver in the v4l2_flash_config
> + * @config:    initialization data for a control
> + * @cid:       contains v4l2 flash control id if the config
> + *             field was initialized, 0 otherwise
> + */
> +struct v4l2_flash_ctrl_data {
> +       struct v4l2_ctrl_config config;
> +       u32 cid;
> +};
> +
> +struct v4l2_flash_ops {
> +       /* setup strobing the flash by hardware pin state assertion */
> +       int (*external_strobe_set)(struct v4l2_flash *v4l2_flash,
> +                                       bool enable);
> +       /* convert intensity to brightness in a device specific manner */
> +       enum led_brightness (*intensity_to_led_brightness)
> +               (struct v4l2_flash *v4l2_flash, s32 intensity);
> +       /* convert brightness to intensity in a device specific manner */
> +       s32 (*led_brightness_to_intensity)
> +               (struct v4l2_flash *v4l2_flash, enum led_brightness);
> +};
> +
> +/**
> + * struct v4l2_flash_config - V4L2 Flash sub-device initialization data
> + * @dev_name:                  the name of the media entity,
> +                               unique in the system
> + * @torch_intensity:           constraints for the LED in torch mode
> + * @indicator_intensity:       constraints for the indicator LED
> + * @flash_faults:              bitmask of flash faults that the LED flash class
> +                               device can report; corresponding LED_FAULT* bit
> +                               definitions are available in the header file
> +                               <linux/led-class-flash.h>
> + * @has_external_strobe:       external strobe capability
> + */
> +struct v4l2_flash_config {
> +       char dev_name[32];
> +       struct led_flash_setting torch_intensity;
> +       struct led_flash_setting indicator_intensity;
> +       u32 flash_faults;
> +       unsigned int has_external_strobe:1;
> +};
> +
> +/**
> + * struct v4l2_flash - Flash sub-device context
> + * @fled_cdev:         LED flash class device controlled by this sub-device
> + * @iled_cdev:         LED class device representing indicator LED associated
> + *                     with the LED flash class device
> + * @ops:               V4L2 specific flash ops
> + * @sd:                        V4L2 sub-device
> + * @hdl:               flash controls handler
> + * @ctrls:             array of pointers to controls, whose values define
> + *                     the sub-device state
> + */
> +struct v4l2_flash {
> +       struct led_classdev_flash *fled_cdev;
> +       struct led_classdev_flash *iled_cdev;
> +       const struct v4l2_flash_ops *ops;
> +
> +       struct v4l2_subdev sd;
> +       struct v4l2_ctrl_handler hdl;
> +       struct v4l2_ctrl **ctrls;
> +};
> +
> +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);
> +}
> +
> +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
> +/**
> + * v4l2_flash_init - initialize V4L2 flash led sub-device
> + * @dev:       flash device, e.g. an I2C device
> + * @of_node:   of_node of the LED, may be NULL if the same as device's
> + * @fled_cdev: LED flash class device to wrap
> + * @iled_cdev: LED flash class device representing indicator LED associated
> + *             with fled_cdev, may be NULL
> + * @flash_ops: V4L2 Flash device ops
> + * @config:    initialization data for V4L2 Flash sub-device
> + *
> + * Create V4L2 Flash sub-device wrapping given LED subsystem device.
> + *
> + * Returns: A valid pointer, or, when an error occurs, the return
> + * value is encoded using ERR_PTR(). Use IS_ERR() to check and
> + * PTR_ERR() to obtain the numeric return value.
> + */
> +struct v4l2_flash *v4l2_flash_init(
> +       struct device *dev, struct device_node *of_node,
> +       struct led_classdev_flash *fled_cdev,
> +       struct led_classdev_flash *iled_cdev,
> +       const struct v4l2_flash_ops *ops,
> +       struct v4l2_flash_config *config);
> +
> +/**
> + * v4l2_flash_release - release V4L2 Flash sub-device
> + * @flash: the V4L2 Flash sub-device to release
> + *
> + * Release V4L2 Flash sub-device.
> + */
> +void v4l2_flash_release(struct v4l2_flash *v4l2_flash);
> +
> +#else
> +static inline struct v4l2_flash *v4l2_flash_init(
> +       struct device *dev, struct device_node *of_node,
> +       struct led_classdev_flash *fled_cdev,
> +       struct led_classdev_flash *iled_cdev,
> +       const struct v4l2_flash_ops *ops,
> +       struct v4l2_flash_config *config)
> +{
> +       return NULL;
> +}
> +
> +static inline void v4l2_flash_release(struct v4l2_flash *v4l2_flash)
> +{
> +}
> +#endif /* CONFIG_V4L2_FLASH_LED_CLASS */
> +
> +#endif /* _V4L2_FLASH_H */
> --
> 1.7.9.5
>

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

* Re: [PATCH v10 2/8] media: Add registration helpers for V4L2 flash sub-devices
  2015-06-10 17:57   ` Bryan Wu
@ 2015-06-10 18:01     ` Bryan Wu
  2015-06-10 18:12       ` Bryan Wu
  0 siblings, 1 reply; 24+ messages in thread
From: Bryan Wu @ 2015-06-10 18:01 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: Linux LED Subsystem, linux-media, Kyungmin Park, Pavel Machek,
	rpurdie, Sakari Ailus, Sylwester Nawrocki, Hans Verkuil

On Wed, Jun 10, 2015 at 10:57 AM, Bryan Wu <cooloney@gmail.com> wrote:
> On Mon, Jun 8, 2015 at 2:02 AM, Jacek Anaszewski
> <j.anaszewski@samsung.com> wrote:
>> This patch adds helper functions for registering/unregistering
>> LED Flash class devices as V4L2 sub-devices. 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.
>>
>
> Please go ahead with my Ack
>
> Acked-by: Bryan Wu <cooloney@gmail.com>
>

I found the rest of LED patches depend on this one. What about merging
this through my tree?

-Bryan


>
>> 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-led-class.c |  708 ++++++++++++++++++++++++
>>  include/media/v4l2-flash-led-class.h           |  148 +++++
>>  4 files changed, 869 insertions(+)
>>  create mode 100644 drivers/media/v4l2-core/v4l2-flash-led-class.c
>>  create mode 100644 include/media/v4l2-flash-led-class.h
>>
>> diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
>> index f7a01a7..b4b0229 100644
>> --- a/drivers/media/v4l2-core/Kconfig
>> +++ b/drivers/media/v4l2-core/Kconfig
>> @@ -44,6 +44,17 @@ config V4L2_MEM2MEM_DEV
>>          tristate
>>          depends on VIDEOBUF2_CORE
>>
>> +# Used by LED subsystem flash drivers
>> +config V4L2_FLASH_LED_CLASS
>> +       tristate "V4L2 flash API for LED flash class devices"
>> +       depends on VIDEO_V4L2_SUBDEV_API
>> +       depends on LEDS_CLASS_FLASH
>> +       ---help---
>> +         Say Y here to enable V4L2 flash API support for LED flash
>> +         class drivers.
>> +
>> +         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..dc3de00 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-led-class.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-led-class.c b/drivers/media/v4l2-core/v4l2-flash-led-class.c
>> new file mode 100644
>> index 0000000..4e19dac
>> --- /dev/null
>> +++ b/drivers/media/v4l2-core/v4l2-flash-led-class.c
>> @@ -0,0 +1,708 @@
>> +/*
>> + * V4L2 flash LED sub-device registration helpers.
>> + *
>> + *     Copyright (C) 2015 Samsung Electronics Co., Ltd
>> + *     Author: Jacek Anaszewski <j.anaszewski@samsung.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include <linux/led-class-flash.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +#include <linux/slab.h>
>> +#include <linux/types.h>
>> +#include <media/v4l2-flash-led-class.h>
>> +
>> +#define has_flash_op(v4l2_flash, op)                           \
>> +       (v4l2_flash && v4l2_flash->ops->op)
>> +
>> +#define call_flash_op(v4l2_flash, op, arg)                     \
>> +               (has_flash_op(v4l2_flash, op) ?                 \
>> +                       v4l2_flash->ops->op(v4l2_flash, arg) :  \
>> +                       -EINVAL)
>> +
>> +enum ctrl_init_data_id {
>> +       LED_MODE,
>> +       TORCH_INTENSITY,
>> +       FLASH_INTENSITY,
>> +       INDICATOR_INTENSITY,
>> +       FLASH_TIMEOUT,
>> +       STROBE_SOURCE,
>> +       /*
>> +        * Only above values are applicable to
>> +        * the 'ctrls' array in the struct v4l2_flash.
>> +        */
>> +       FLASH_STROBE,
>> +       STROBE_STOP,
>> +       STROBE_STATUS,
>> +       FLASH_FAULT,
>> +       NUM_FLASH_CTRLS,
>> +};
>> +
>> +static enum led_brightness __intensity_to_led_brightness(
>> +                                       struct v4l2_ctrl *ctrl, s32 intensity)
>> +{
>> +       intensity -= ctrl->minimum;
>> +       intensity /= (u32) ctrl->step;
>> +
>> +       /*
>> +        * Indicator LEDs, unlike torch LEDs, are turned on/off basing on
>> +        * the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only.
>> +        * Therefore it must be possible to set it to 0 level which in
>> +        * the LED subsystem reflects LED_OFF state.
>> +        */
>> +       if (ctrl->minimum)
>> +               ++intensity;
>> +
>> +       return intensity;
>> +}
>> +
>> +static s32 __led_brightness_to_intensity(struct v4l2_ctrl *ctrl,
>> +                                        enum led_brightness brightness)
>> +{
>> +       /*
>> +        * Indicator LEDs, unlike torch LEDs, are turned on/off basing on
>> +        * the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only.
>> +        * Do not decrement brightness read from the LED subsystem for
>> +        * indicator LED as it may equal 0. For torch LEDs this function
>> +        * is called only when V4L2_FLASH_LED_MODE_TORCH is set and the
>> +        * brightness read is guaranteed to be greater than 0. In the mode
>> +        * V4L2_FLASH_LED_MODE_NONE the cached torch intensity value is used.
>> +        */
>> +       if (ctrl->id != V4L2_CID_FLASH_INDICATOR_INTENSITY)
>> +               --brightness;
>> +
>> +       return (brightness * ctrl->step) + ctrl->minimum;
>> +}
>> +
>> +static void v4l2_flash_set_led_brightness(struct v4l2_flash *v4l2_flash,
>> +                                       struct v4l2_ctrl *ctrl)
>> +{
>> +       struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
>> +       enum led_brightness brightness;
>> +
>> +       if (has_flash_op(v4l2_flash, intensity_to_led_brightness))
>> +               brightness = call_flash_op(v4l2_flash,
>> +                                       intensity_to_led_brightness,
>> +                                       ctrl->val);
>> +       else
>> +               brightness = __intensity_to_led_brightness(ctrl, ctrl->val);
>> +       /*
>> +        * In case a LED Flash class driver provides ops for custom
>> +        * brightness <-> intensity conversion, it also must have defined
>> +        * related v4l2 control step == 1. In such a case a backward conversion
>> +        * from led brightness to v4l2 intensity is required to find out the
>> +        * the aligned intensity value.
>> +        */
>> +       if (has_flash_op(v4l2_flash, led_brightness_to_intensity))
>> +               ctrl->val = call_flash_op(v4l2_flash,
>> +                                       led_brightness_to_intensity,
>> +                                       brightness);
>> +
>> +       if (ctrl == ctrls[TORCH_INTENSITY]) {
>> +               if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
>> +                       return;
>> +
>> +               led_set_brightness(&v4l2_flash->fled_cdev->led_cdev,
>> +                                       brightness);
>> +       } else {
>> +               led_set_brightness(&v4l2_flash->iled_cdev->led_cdev,
>> +                                       brightness);
>> +       }
>> +}
>> +
>> +static int v4l2_flash_update_led_brightness(struct v4l2_flash *v4l2_flash,
>> +                                       struct v4l2_ctrl *ctrl)
>> +{
>> +       struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
>> +       struct led_classdev *led_cdev;
>> +       int ret;
>> +
>> +       if (ctrl == ctrls[TORCH_INTENSITY]) {
>> +               /*
>> +                * Update torch brightness only if in TORCH_MODE. In other modes
>> +                * torch led is turned off, which would spuriously inform the
>> +                * user space that V4L2_CID_FLASH_TORCH_INTENSITY control value
>> +                * has changed to 0.
>> +                */
>> +               if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
>> +                       return 0;
>> +               led_cdev = &v4l2_flash->fled_cdev->led_cdev;
>> +       } else {
>> +               led_cdev = &v4l2_flash->iled_cdev->led_cdev;
>> +       }
>> +
>> +       ret = led_update_brightness(led_cdev);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       if (has_flash_op(v4l2_flash, led_brightness_to_intensity))
>> +               ctrl->val = call_flash_op(v4l2_flash,
>> +                                               led_brightness_to_intensity,
>> +                                               led_cdev->brightness);
>> +       else
>> +               ctrl->val = __led_brightness_to_intensity(ctrl,
>> +                                               led_cdev->brightness);
>> +
>> +       return 0;
>> +}
>> +
>> +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 *fled_cdev = v4l2_flash->fled_cdev;
>> +       bool is_strobing;
>> +       int ret;
>> +
>> +       switch (c->id) {
>> +       case V4L2_CID_FLASH_TORCH_INTENSITY:
>> +       case V4L2_CID_FLASH_INDICATOR_INTENSITY:
>> +               return v4l2_flash_update_led_brightness(v4l2_flash, c);
>> +       case V4L2_CID_FLASH_INTENSITY:
>> +               ret = led_update_flash_brightness(fled_cdev);
>> +               if (ret < 0)
>> +                       return ret;
>> +               /*
>> +                * No conversion is needed as LED Flash class also uses
>> +                * microamperes for flash intensity units.
>> +                */
>> +               c->val = fled_cdev->brightness.val;
>> +               return 0;
>> +       case V4L2_CID_FLASH_STROBE_STATUS:
>> +               ret = led_get_flash_strobe(fled_cdev, &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 */
>> +               return led_get_flash_fault(fled_cdev, &c->val);
>> +       default:
>> +               return -EINVAL;
>> +       }
>> +}
>> +
>> +static bool __software_strobe_mode_inactive(struct v4l2_ctrl **ctrls)
>> +{
>> +       return ((ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH) ||
>> +               (ctrls[STROBE_SOURCE] && (ctrls[STROBE_SOURCE]->val !=
>> +                               V4L2_FLASH_STROBE_SOURCE_SOFTWARE)));
>> +}
>> +
>> +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 *fled_cdev = v4l2_flash->fled_cdev;
>> +       struct led_classdev *led_cdev = &fled_cdev->led_cdev;
>> +       struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
>> +       bool external_strobe;
>> +       int ret = 0;
>> +
>> +       switch (c->id) {
>> +       case V4L2_CID_FLASH_LED_MODE:
>> +               switch (c->val) {
>> +               case V4L2_FLASH_LED_MODE_NONE:
>> +                       led_set_brightness(led_cdev, LED_OFF);
>> +                       return led_set_flash_strobe(fled_cdev, false);
>> +               case V4L2_FLASH_LED_MODE_FLASH:
>> +                       /* Turn the torch LED off */
>> +                       led_set_brightness(led_cdev, LED_OFF);
>> +                       if (ctrls[STROBE_SOURCE]) {
>> +                               external_strobe = (ctrls[STROBE_SOURCE]->val ==
>> +                                       V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
>> +
>> +                               ret = call_flash_op(v4l2_flash,
>> +                                               external_strobe_set,
>> +                                               external_strobe);
>> +                       }
>> +                       return ret;
>> +               case V4L2_FLASH_LED_MODE_TORCH:
>> +                       if (ctrls[STROBE_SOURCE]) {
>> +                               ret = call_flash_op(v4l2_flash,
>> +                                               external_strobe_set,
>> +                                               false);
>> +                               if (ret < 0)
>> +                                       return ret;
>> +                       }
>> +                       /* Stop flash strobing */
>> +                       ret = led_set_flash_strobe(fled_cdev, false);
>> +                       if (ret < 0)
>> +                               return ret;
>> +
>> +                       v4l2_flash_set_led_brightness(v4l2_flash,
>> +                                                       ctrls[TORCH_INTENSITY]);
>> +                       return 0;
>> +               }
>> +               break;
>> +       case V4L2_CID_FLASH_STROBE_SOURCE:
>> +               external_strobe = (c->val == V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
>> +               /*
>> +                * For some hardware arrangements setting strobe source may
>> +                * affect torch mode. Therefore, if not in the flash mode,
>> +                * cache only this setting. It will be applied upon switching
>> +                * to flash mode.
>> +                */
>> +               if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH)
>> +                       return 0;
>> +
>> +               return call_flash_op(v4l2_flash, external_strobe_set,
>> +                                       external_strobe);
>> +       case V4L2_CID_FLASH_STROBE:
>> +               if (__software_strobe_mode_inactive(ctrls))
>> +                       return -EBUSY;
>> +               return led_set_flash_strobe(fled_cdev, true);
>> +       case V4L2_CID_FLASH_STROBE_STOP:
>> +               if (__software_strobe_mode_inactive(ctrls))
>> +                       return -EBUSY;
>> +               return led_set_flash_strobe(fled_cdev, false);
>> +       case V4L2_CID_FLASH_TIMEOUT:
>> +               /*
>> +                * No conversion is needed as LED Flash class also uses
>> +                * microseconds for flash timeout units.
>> +                */
>> +               return led_set_flash_timeout(fled_cdev, c->val);
>> +       case V4L2_CID_FLASH_INTENSITY:
>> +               /*
>> +                * No conversion is needed as LED Flash class also uses
>> +                * microamperes for flash intensity units.
>> +                */
>> +               return led_set_flash_brightness(fled_cdev, c->val);
>> +       case V4L2_CID_FLASH_TORCH_INTENSITY:
>> +       case V4L2_CID_FLASH_INDICATOR_INTENSITY:
>> +               v4l2_flash_set_led_brightness(v4l2_flash, c);
>> +               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 void __lfs_to_v4l2_ctrl_config(struct led_flash_setting *s,
>> +                               struct v4l2_ctrl_config *c)
>> +{
>> +       c->min = s->min;
>> +       c->max = s->max;
>> +       c->step = s->step;
>> +       c->def = s->val;
>> +}
>> +
>> +static void __fill_ctrl_init_data(struct v4l2_flash *v4l2_flash,
>> +                         struct v4l2_flash_config *flash_cfg,
>> +                         struct v4l2_flash_ctrl_data *ctrl_init_data)
>> +{
>> +       struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
>> +       const struct led_flash_ops *fled_cdev_ops = fled_cdev->ops;
>> +       struct led_classdev *led_cdev = &fled_cdev->led_cdev;
>> +       struct v4l2_ctrl_config *ctrl_cfg;
>> +       u32 mask;
>> +
>> +       /* Init FLASH_FAULT ctrl data */
>> +       if (flash_cfg->flash_faults) {
>> +               ctrl_init_data[FLASH_FAULT].cid = V4L2_CID_FLASH_FAULT;
>> +               ctrl_cfg = &ctrl_init_data[FLASH_FAULT].config;
>> +               ctrl_cfg->id = V4L2_CID_FLASH_FAULT;
>> +               ctrl_cfg->max = flash_cfg->flash_faults;
>> +               ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
>> +                                 V4L2_CTRL_FLAG_READ_ONLY;
>> +       }
>> +
>> +       /* Init FLASH_LED_MODE ctrl data */
>> +       mask = 1 << V4L2_FLASH_LED_MODE_NONE |
>> +              1 << V4L2_FLASH_LED_MODE_TORCH;
>> +       if (led_cdev->flags & LED_DEV_CAP_FLASH)
>> +               mask |= 1 << V4L2_FLASH_LED_MODE_FLASH;
>> +
>> +       ctrl_init_data[LED_MODE].cid = V4L2_CID_FLASH_LED_MODE;
>> +       ctrl_cfg = &ctrl_init_data[LED_MODE].config;
>> +       ctrl_cfg->id = V4L2_CID_FLASH_LED_MODE;
>> +       ctrl_cfg->max = V4L2_FLASH_LED_MODE_TORCH;
>> +       ctrl_cfg->menu_skip_mask = ~mask;
>> +       ctrl_cfg->def = V4L2_FLASH_LED_MODE_NONE;
>> +       ctrl_cfg->flags = 0;
>> +
>> +       /* Init TORCH_INTENSITY ctrl data */
>> +       ctrl_init_data[TORCH_INTENSITY].cid = V4L2_CID_FLASH_TORCH_INTENSITY;
>> +       ctrl_cfg = &ctrl_init_data[TORCH_INTENSITY].config;
>> +       __lfs_to_v4l2_ctrl_config(&flash_cfg->torch_intensity, ctrl_cfg);
>> +       ctrl_cfg->id = V4L2_CID_FLASH_TORCH_INTENSITY;
>> +       ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
>> +                         V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
>> +
>> +       /* Init INDICATOR_INTENSITY ctrl data */
>> +       if (v4l2_flash->iled_cdev) {
>> +               ctrl_init_data[INDICATOR_INTENSITY].cid =
>> +                                       V4L2_CID_FLASH_INDICATOR_INTENSITY;
>> +               ctrl_cfg = &ctrl_init_data[INDICATOR_INTENSITY].config;
>> +               __lfs_to_v4l2_ctrl_config(&flash_cfg->indicator_intensity,
>> +                                         ctrl_cfg);
>> +               ctrl_cfg->id = V4L2_CID_FLASH_INDICATOR_INTENSITY;
>> +               ctrl_cfg->min = 0;
>> +               ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
>> +                                 V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
>> +       }
>> +
>> +       if (!(led_cdev->flags & LED_DEV_CAP_FLASH))
>> +               return;
>> +
>> +       /* Init FLASH_STROBE ctrl data */
>> +       ctrl_init_data[FLASH_STROBE].cid = V4L2_CID_FLASH_STROBE;
>> +       ctrl_cfg = &ctrl_init_data[FLASH_STROBE].config;
>> +       ctrl_cfg->id = V4L2_CID_FLASH_STROBE;
>> +
>> +       /* Init STROBE_STOP ctrl data */
>> +       ctrl_init_data[STROBE_STOP].cid = V4L2_CID_FLASH_STROBE_STOP;
>> +       ctrl_cfg = &ctrl_init_data[STROBE_STOP].config;
>> +       ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STOP;
>> +
>> +       /* Init FLASH_STROBE_SOURCE ctrl data */
>> +       if (flash_cfg->has_external_strobe) {
>> +               mask = (1 << V4L2_FLASH_STROBE_SOURCE_SOFTWARE) |
>> +                      (1 << V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
>> +               ctrl_init_data[STROBE_SOURCE].cid =
>> +                                       V4L2_CID_FLASH_STROBE_SOURCE;
>> +               ctrl_cfg = &ctrl_init_data[STROBE_SOURCE].config;
>> +               ctrl_cfg->id = V4L2_CID_FLASH_STROBE_SOURCE;
>> +               ctrl_cfg->max = V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
>> +               ctrl_cfg->menu_skip_mask = ~mask;
>> +               ctrl_cfg->def = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
>> +       }
>> +
>> +       /* Init STROBE_STATUS ctrl data */
>> +       if (fled_cdev_ops->strobe_get) {
>> +               ctrl_init_data[STROBE_STATUS].cid =
>> +                                       V4L2_CID_FLASH_STROBE_STATUS;
>> +               ctrl_cfg = &ctrl_init_data[STROBE_STATUS].config;
>> +               ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STATUS;
>> +               ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
>> +                                 V4L2_CTRL_FLAG_READ_ONLY;
>> +       }
>> +
>> +       /* Init FLASH_TIMEOUT ctrl data */
>> +       if (fled_cdev_ops->timeout_set) {
>> +               ctrl_init_data[FLASH_TIMEOUT].cid = V4L2_CID_FLASH_TIMEOUT;
>> +               ctrl_cfg = &ctrl_init_data[FLASH_TIMEOUT].config;
>> +               __lfs_to_v4l2_ctrl_config(&fled_cdev->timeout, ctrl_cfg);
>> +               ctrl_cfg->id = V4L2_CID_FLASH_TIMEOUT;
>> +       }
>> +
>> +       /* Init FLASH_INTENSITY ctrl data */
>> +       if (fled_cdev_ops->flash_brightness_set) {
>> +               ctrl_init_data[FLASH_INTENSITY].cid = V4L2_CID_FLASH_INTENSITY;
>> +               ctrl_cfg = &ctrl_init_data[FLASH_INTENSITY].config;
>> +               __lfs_to_v4l2_ctrl_config(&fled_cdev->brightness, ctrl_cfg);
>> +               ctrl_cfg->id = V4L2_CID_FLASH_INTENSITY;
>> +               ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
>> +                                 V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
>> +       }
>> +}
>> +
>> +static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash,
>> +                               struct v4l2_flash_config *flash_cfg)
>> +
>> +{
>> +       struct v4l2_flash_ctrl_data *ctrl_init_data;
>> +       struct v4l2_ctrl *ctrl;
>> +       struct v4l2_ctrl_config *ctrl_cfg;
>> +       int i, ret, num_ctrls = 0;
>> +
>> +       v4l2_flash->ctrls = devm_kzalloc(v4l2_flash->sd.dev,
>> +                                       sizeof(*v4l2_flash->ctrls) *
>> +                                       (STROBE_SOURCE + 1), GFP_KERNEL);
>> +       if (!v4l2_flash->ctrls)
>> +               return -ENOMEM;
>> +
>> +       /* allocate memory dynamically so as not to exceed stack frame size */
>> +       ctrl_init_data = kcalloc(NUM_FLASH_CTRLS, sizeof(*ctrl_init_data),
>> +                                       GFP_KERNEL);
>> +       if (!ctrl_init_data)
>> +               return -ENOMEM;
>> +
>> +       __fill_ctrl_init_data(v4l2_flash, flash_cfg, ctrl_init_data);
>> +
>> +       for (i = 0; i < NUM_FLASH_CTRLS; ++i)
>> +               if (ctrl_init_data[i].cid)
>> +                       ++num_ctrls;
>> +
>> +       v4l2_ctrl_handler_init(&v4l2_flash->hdl, num_ctrls);
>> +
>> +       for (i = 0; i < NUM_FLASH_CTRLS; ++i) {
>> +               ctrl_cfg = &ctrl_init_data[i].config;
>> +               if (!ctrl_init_data[i].cid)
>> +                       continue;
>> +
>> +               if (ctrl_cfg->id == V4L2_CID_FLASH_LED_MODE ||
>> +                   ctrl_cfg->id == V4L2_CID_FLASH_STROBE_SOURCE)
>> +                       ctrl = v4l2_ctrl_new_std_menu(&v4l2_flash->hdl,
>> +                                               &v4l2_flash_ctrl_ops,
>> +                                               ctrl_cfg->id,
>> +                                               ctrl_cfg->max,
>> +                                               ctrl_cfg->menu_skip_mask,
>> +                                               ctrl_cfg->def);
>> +               else
>> +                       ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl,
>> +                                               &v4l2_flash_ctrl_ops,
>> +                                               ctrl_cfg->id,
>> +                                               ctrl_cfg->min,
>> +                                               ctrl_cfg->max,
>> +                                               ctrl_cfg->step,
>> +                                               ctrl_cfg->def);
>> +
>> +               if (ctrl)
>> +                       ctrl->flags |= ctrl_cfg->flags;
>> +
>> +               if (i <= STROBE_SOURCE)
>> +                       v4l2_flash->ctrls[i] = ctrl;
>> +       }
>> +
>> +       kfree(ctrl_init_data);
>> +
>> +       if (v4l2_flash->hdl.error) {
>> +               ret = v4l2_flash->hdl.error;
>> +               goto error_free_handler;
>> +       }
>> +
>> +       v4l2_ctrl_handler_setup(&v4l2_flash->hdl);
>> +
>> +       v4l2_flash->sd.ctrl_handler = &v4l2_flash->hdl;
>> +
>> +       return 0;
>> +
>> +error_free_handler:
>> +       v4l2_ctrl_handler_free(&v4l2_flash->hdl);
>> +       return ret;
>> +}
>> +
>> +static int __sync_device_with_v4l2_controls(struct v4l2_flash *v4l2_flash)
>> +{
>> +       struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
>> +       struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
>> +       int ret = 0;
>> +
>> +       v4l2_flash_set_led_brightness(v4l2_flash, ctrls[TORCH_INTENSITY]);
>> +
>> +       if (ctrls[INDICATOR_INTENSITY])
>> +               v4l2_flash_set_led_brightness(v4l2_flash,
>> +                                               ctrls[INDICATOR_INTENSITY]);
>> +
>> +       if (ctrls[FLASH_TIMEOUT]) {
>> +               ret = led_set_flash_timeout(fled_cdev,
>> +                                       ctrls[FLASH_TIMEOUT]->val);
>> +               if (ret < 0)
>> +                       return ret;
>> +       }
>> +
>> +       if (ctrls[FLASH_INTENSITY]) {
>> +               ret = led_set_flash_brightness(fled_cdev,
>> +                                       ctrls[FLASH_INTENSITY]->val);
>> +               if (ret < 0)
>> +                       return ret;
>> +       }
>> +
>> +       /*
>> +        * For some hardware arrangements setting strobe source may affect
>> +        * torch mode. Synchronize strobe source setting only if not in torch
>> +        * mode. For torch mode case it will get synchronized upon switching
>> +        * to flash mode.
>> +        */
>> +       if (ctrls[STROBE_SOURCE] &&
>> +           ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
>> +               ret = call_flash_op(v4l2_flash, external_strobe_set,
>> +                                       ctrls[STROBE_SOURCE]->val);
>> +
>> +       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 *fled_cdev = v4l2_flash->fled_cdev;
>> +       struct led_classdev *led_cdev = &fled_cdev->led_cdev;
>> +       struct led_classdev_flash *iled_cdev = v4l2_flash->iled_cdev;
>> +       struct led_classdev *led_cdev_ind = NULL;
>> +       int ret = 0;
>> +
>> +       if (!v4l2_fh_is_singular(&fh->vfh))
>> +               return 0;
>> +
>> +       mutex_lock(&led_cdev->led_access);
>> +
>> +       led_sysfs_disable(led_cdev);
>> +       led_trigger_remove(led_cdev);
>> +
>> +       mutex_unlock(&led_cdev->led_access);
>> +
>> +       if (iled_cdev) {
>> +               led_cdev_ind = &iled_cdev->led_cdev;
>> +
>> +               mutex_lock(&led_cdev_ind->led_access);
>> +
>> +               led_sysfs_disable(led_cdev_ind);
>> +               led_trigger_remove(led_cdev_ind);
>> +
>> +               mutex_unlock(&led_cdev_ind->led_access);
>> +       }
>> +
>> +       ret = __sync_device_with_v4l2_controls(v4l2_flash);
>> +       if (ret < 0)
>> +               goto out_sync_device;
>> +
>> +       return 0;
>> +out_sync_device:
>> +       mutex_lock(&led_cdev->led_access);
>> +       led_sysfs_enable(led_cdev);
>> +       mutex_unlock(&led_cdev->led_access);
>> +
>> +       if (led_cdev_ind) {
>> +               mutex_lock(&led_cdev_ind->led_access);
>> +               led_sysfs_enable(led_cdev_ind);
>> +               mutex_unlock(&led_cdev_ind->led_access);
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +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 *fled_cdev = v4l2_flash->fled_cdev;
>> +       struct led_classdev *led_cdev = &fled_cdev->led_cdev;
>> +       struct led_classdev_flash *iled_cdev = v4l2_flash->iled_cdev;
>> +       int ret = 0;
>> +
>> +       if (!v4l2_fh_is_singular(&fh->vfh))
>> +               return 0;
>> +
>> +       mutex_lock(&led_cdev->led_access);
>> +
>> +       if (v4l2_flash->ctrls[STROBE_SOURCE])
>> +               ret = v4l2_ctrl_s_ctrl(v4l2_flash->ctrls[STROBE_SOURCE],
>> +                               V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
>> +       led_sysfs_enable(led_cdev);
>> +
>> +       mutex_unlock(&led_cdev->led_access);
>> +
>> +       if (iled_cdev) {
>> +               struct led_classdev *led_cdev_ind = &iled_cdev->led_cdev;
>> +
>> +               mutex_lock(&led_cdev_ind->led_access);
>> +               led_sysfs_enable(led_cdev_ind);
>> +               mutex_unlock(&led_cdev_ind->led_access);
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +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,
>> +};
>> +
>> +struct v4l2_flash *v4l2_flash_init(
>> +       struct device *dev, struct device_node *of_node,
>> +       struct led_classdev_flash *fled_cdev,
>> +       struct led_classdev_flash *iled_cdev,
>> +       const struct v4l2_flash_ops *ops,
>> +       struct v4l2_flash_config *config)
>> +{
>> +       struct v4l2_flash *v4l2_flash;
>> +       struct led_classdev *led_cdev = &fled_cdev->led_cdev;
>> +       struct v4l2_subdev *sd;
>> +       int ret;
>> +
>> +       if (!fled_cdev || !ops || !config)
>> +               return ERR_PTR(-EINVAL);
>> +
>> +       v4l2_flash = devm_kzalloc(led_cdev->dev, sizeof(*v4l2_flash),
>> +                                       GFP_KERNEL);
>> +       if (!v4l2_flash)
>> +               return ERR_PTR(-ENOMEM);
>> +
>> +       sd = &v4l2_flash->sd;
>> +       v4l2_flash->fled_cdev = fled_cdev;
>> +       v4l2_flash->iled_cdev = iled_cdev;
>> +       v4l2_flash->ops = ops;
>> +       sd->dev = dev;
>> +       sd->of_node = of_node;
>> +       v4l2_subdev_init(sd, &v4l2_flash_subdev_ops);
>> +       sd->internal_ops = &v4l2_flash_subdev_internal_ops;
>> +       sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>> +       strlcpy(sd->name, config->dev_name, sizeof(sd->name));
>> +
>> +       ret = media_entity_init(&sd->entity, 0, NULL, 0);
>> +       if (ret < 0)
>> +               return ERR_PTR(ret);
>> +
>> +       sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
>> +
>> +       ret = v4l2_flash_init_controls(v4l2_flash, config);
>> +       if (ret < 0)
>> +               goto err_init_controls;
>> +
>> +       if (sd->of_node)
>> +               of_node_get(sd->of_node);
>> +       else
>> +               of_node_get(led_cdev->dev->of_node);
>> +
>> +       ret = v4l2_async_register_subdev(sd);
>> +       if (ret < 0)
>> +               goto err_async_register_sd;
>> +
>> +       return v4l2_flash;
>> +
>> +err_async_register_sd:
>> +       of_node_put(led_cdev->dev->of_node);
>> +       v4l2_ctrl_handler_free(sd->ctrl_handler);
>> +err_init_controls:
>> +       media_entity_cleanup(&sd->entity);
>> +
>> +       return ERR_PTR(ret);
>> +}
>> +EXPORT_SYMBOL_GPL(v4l2_flash_init);
>> +
>> +void v4l2_flash_release(struct v4l2_flash *v4l2_flash)
>> +{
>> +       struct v4l2_subdev *sd;
>> +       struct led_classdev *led_cdev;
>> +
>> +       if (IS_ERR_OR_NULL(v4l2_flash))
>> +               return;
>> +
>> +       sd = &v4l2_flash->sd;
>> +       led_cdev = &v4l2_flash->fled_cdev->led_cdev;
>> +
>> +       v4l2_async_unregister_subdev(sd);
>> +
>> +       if (sd->of_node)
>> +               of_node_put(sd->of_node);
>> +       else
>> +               of_node_put(led_cdev->dev->of_node);
>> +
>> +       v4l2_ctrl_handler_free(sd->ctrl_handler);
>> +       media_entity_cleanup(&sd->entity);
>> +}
>> +EXPORT_SYMBOL_GPL(v4l2_flash_release);
>> +
>> +MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
>> +MODULE_DESCRIPTION("V4L2 Flash sub-device helpers");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/include/media/v4l2-flash-led-class.h b/include/media/v4l2-flash-led-class.h
>> new file mode 100644
>> index 0000000..098236c
>> --- /dev/null
>> +++ b/include/media/v4l2-flash-led-class.h
>> @@ -0,0 +1,148 @@
>> +/*
>> + * V4L2 flash LED sub-device registration helpers.
>> + *
>> + *     Copyright (C) 2015 Samsung Electronics Co., Ltd
>> + *     Author: Jacek Anaszewski <j.anaszewski@samsung.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#ifndef _V4L2_FLASH_H
>> +#define _V4L2_FLASH_H
>> +
>> +#include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-subdev.h>
>> +
>> +struct led_classdev_flash;
>> +struct led_classdev;
>> +struct v4l2_flash;
>> +enum led_brightness;
>> +
>> +/*
>> + * struct v4l2_flash_ctrl_data - flash control initialization data, filled
>> + *                             basing on the features declared by the LED flash
>> + *                             class driver in the v4l2_flash_config
>> + * @config:    initialization data for a control
>> + * @cid:       contains v4l2 flash control id if the config
>> + *             field was initialized, 0 otherwise
>> + */
>> +struct v4l2_flash_ctrl_data {
>> +       struct v4l2_ctrl_config config;
>> +       u32 cid;
>> +};
>> +
>> +struct v4l2_flash_ops {
>> +       /* setup strobing the flash by hardware pin state assertion */
>> +       int (*external_strobe_set)(struct v4l2_flash *v4l2_flash,
>> +                                       bool enable);
>> +       /* convert intensity to brightness in a device specific manner */
>> +       enum led_brightness (*intensity_to_led_brightness)
>> +               (struct v4l2_flash *v4l2_flash, s32 intensity);
>> +       /* convert brightness to intensity in a device specific manner */
>> +       s32 (*led_brightness_to_intensity)
>> +               (struct v4l2_flash *v4l2_flash, enum led_brightness);
>> +};
>> +
>> +/**
>> + * struct v4l2_flash_config - V4L2 Flash sub-device initialization data
>> + * @dev_name:                  the name of the media entity,
>> +                               unique in the system
>> + * @torch_intensity:           constraints for the LED in torch mode
>> + * @indicator_intensity:       constraints for the indicator LED
>> + * @flash_faults:              bitmask of flash faults that the LED flash class
>> +                               device can report; corresponding LED_FAULT* bit
>> +                               definitions are available in the header file
>> +                               <linux/led-class-flash.h>
>> + * @has_external_strobe:       external strobe capability
>> + */
>> +struct v4l2_flash_config {
>> +       char dev_name[32];
>> +       struct led_flash_setting torch_intensity;
>> +       struct led_flash_setting indicator_intensity;
>> +       u32 flash_faults;
>> +       unsigned int has_external_strobe:1;
>> +};
>> +
>> +/**
>> + * struct v4l2_flash - Flash sub-device context
>> + * @fled_cdev:         LED flash class device controlled by this sub-device
>> + * @iled_cdev:         LED class device representing indicator LED associated
>> + *                     with the LED flash class device
>> + * @ops:               V4L2 specific flash ops
>> + * @sd:                        V4L2 sub-device
>> + * @hdl:               flash controls handler
>> + * @ctrls:             array of pointers to controls, whose values define
>> + *                     the sub-device state
>> + */
>> +struct v4l2_flash {
>> +       struct led_classdev_flash *fled_cdev;
>> +       struct led_classdev_flash *iled_cdev;
>> +       const struct v4l2_flash_ops *ops;
>> +
>> +       struct v4l2_subdev sd;
>> +       struct v4l2_ctrl_handler hdl;
>> +       struct v4l2_ctrl **ctrls;
>> +};
>> +
>> +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);
>> +}
>> +
>> +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
>> +/**
>> + * v4l2_flash_init - initialize V4L2 flash led sub-device
>> + * @dev:       flash device, e.g. an I2C device
>> + * @of_node:   of_node of the LED, may be NULL if the same as device's
>> + * @fled_cdev: LED flash class device to wrap
>> + * @iled_cdev: LED flash class device representing indicator LED associated
>> + *             with fled_cdev, may be NULL
>> + * @flash_ops: V4L2 Flash device ops
>> + * @config:    initialization data for V4L2 Flash sub-device
>> + *
>> + * Create V4L2 Flash sub-device wrapping given LED subsystem device.
>> + *
>> + * Returns: A valid pointer, or, when an error occurs, the return
>> + * value is encoded using ERR_PTR(). Use IS_ERR() to check and
>> + * PTR_ERR() to obtain the numeric return value.
>> + */
>> +struct v4l2_flash *v4l2_flash_init(
>> +       struct device *dev, struct device_node *of_node,
>> +       struct led_classdev_flash *fled_cdev,
>> +       struct led_classdev_flash *iled_cdev,
>> +       const struct v4l2_flash_ops *ops,
>> +       struct v4l2_flash_config *config);
>> +
>> +/**
>> + * v4l2_flash_release - release V4L2 Flash sub-device
>> + * @flash: the V4L2 Flash sub-device to release
>> + *
>> + * Release V4L2 Flash sub-device.
>> + */
>> +void v4l2_flash_release(struct v4l2_flash *v4l2_flash);
>> +
>> +#else
>> +static inline struct v4l2_flash *v4l2_flash_init(
>> +       struct device *dev, struct device_node *of_node,
>> +       struct led_classdev_flash *fled_cdev,
>> +       struct led_classdev_flash *iled_cdev,
>> +       const struct v4l2_flash_ops *ops,
>> +       struct v4l2_flash_config *config)
>> +{
>> +       return NULL;
>> +}
>> +
>> +static inline void v4l2_flash_release(struct v4l2_flash *v4l2_flash)
>> +{
>> +}
>> +#endif /* CONFIG_V4L2_FLASH_LED_CLASS */
>> +
>> +#endif /* _V4L2_FLASH_H */
>> --
>> 1.7.9.5
>>

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

* Re: [PATCH v10 3/8] leds: max77693: add support for V4L2 Flash sub-device
  2015-06-08  9:02 ` [PATCH v10 3/8] leds: max77693: add support for V4L2 Flash sub-device Jacek Anaszewski
@ 2015-06-10 18:12   ` Bryan Wu
  0 siblings, 0 replies; 24+ messages in thread
From: Bryan Wu @ 2015-06-10 18:12 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: Linux LED Subsystem, linux-media, Kyungmin Park, Pavel Machek,
	rpurdie, Sakari Ailus, Sylwester Nawrocki

On Mon, Jun 8, 2015 at 2:02 AM, Jacek Anaszewski
<j.anaszewski@samsung.com> wrote:
> Add support for V4L2 Flash sub-device to the max77693 LED Flash class
> driver. The support allows for V4L2 Flash sub-device to take the control
> of the LED Flash class device.
>

Merged into my -devel branch and it won't be merged into 4.2.0 merge
window but wait for one more cycle, since now it's quite late in 4.1.0
cycle.

Thanks,
-Bryan

> 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: Sakari Ailus <sakari.ailus@iki.fi>
> ---
>  drivers/leds/leds-max77693.c |  129 ++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 123 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/leds/leds-max77693.c b/drivers/leds/leds-max77693.c
> index eecaa92..b8b0eec 100644
> --- a/drivers/leds/leds-max77693.c
> +++ b/drivers/leds/leds-max77693.c
> @@ -20,6 +20,7 @@
>  #include <linux/regmap.h>
>  #include <linux/slab.h>
>  #include <linux/workqueue.h>
> +#include <media/v4l2-flash-led-class.h>
>
>  #define MODE_OFF               0
>  #define MODE_FLASH(a)          (1 << (a))
> @@ -62,6 +63,8 @@ struct max77693_sub_led {
>         struct led_classdev_flash fled_cdev;
>         /* assures led-triggers compatibility */
>         struct work_struct work_brightness_set;
> +       /* V4L2 Flash device */
> +       struct v4l2_flash *v4l2_flash;
>
>         /* brightness cache */
>         unsigned int torch_brightness;
> @@ -627,7 +630,8 @@ static int max77693_led_flash_timeout_set(
>  }
>
>  static int max77693_led_parse_dt(struct max77693_led_device *led,
> -                               struct max77693_led_config_data *cfg)
> +                               struct max77693_led_config_data *cfg,
> +                               struct device_node **sub_nodes)
>  {
>         struct device *dev = &led->pdev->dev;
>         struct max77693_sub_led *sub_leds = led->sub_leds;
> @@ -674,6 +678,13 @@ static int max77693_led_parse_dt(struct max77693_led_device *led,
>                         return -EINVAL;
>                 }
>
> +               if (sub_nodes[fled_id]) {
> +                       dev_err(dev,
> +                               "Conflicting \"led-sources\" DT properties\n");
> +                       return -EINVAL;
> +               }
> +
> +               sub_nodes[fled_id] = child_node;
>                 sub_leds[fled_id].fled_id = fled_id;
>
>                 cfg->label[fled_id] =
> @@ -786,11 +797,12 @@ static void max77693_led_validate_configuration(struct max77693_led_device *led,
>  }
>
>  static int max77693_led_get_configuration(struct max77693_led_device *led,
> -                               struct max77693_led_config_data *cfg)
> +                               struct max77693_led_config_data *cfg,
> +                               struct device_node **sub_nodes)
>  {
>         int ret;
>
> -       ret = max77693_led_parse_dt(led, cfg);
> +       ret = max77693_led_parse_dt(led, cfg, sub_nodes);
>         if (ret < 0)
>                 return ret;
>
> @@ -838,6 +850,71 @@ static void max77693_init_flash_settings(struct max77693_sub_led *sub_led,
>         setting->val = setting->max;
>  }
>
> +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
> +
> +static int max77693_led_external_strobe_set(
> +                               struct v4l2_flash *v4l2_flash,
> +                               bool enable)
> +{
> +       struct max77693_sub_led *sub_led =
> +                               flcdev_to_sub_led(v4l2_flash->fled_cdev);
> +       struct max77693_led_device *led = sub_led_to_led(sub_led);
> +       int fled_id = sub_led->fled_id;
> +       int ret;
> +
> +       mutex_lock(&led->lock);
> +
> +       if (enable)
> +               ret = max77693_add_mode(led, MODE_FLASH_EXTERNAL(fled_id));
> +       else
> +               ret = max77693_clear_mode(led, MODE_FLASH_EXTERNAL(fled_id));
> +
> +       mutex_unlock(&led->lock);
> +
> +       return ret;
> +}
> +
> +static void max77693_init_v4l2_flash_config(struct max77693_sub_led *sub_led,
> +                               struct max77693_led_config_data *led_cfg,
> +                               struct v4l2_flash_config *v4l2_sd_cfg)
> +{
> +       struct max77693_led_device *led = sub_led_to_led(sub_led);
> +       struct device *dev = &led->pdev->dev;
> +       struct max77693_dev *iodev = dev_get_drvdata(dev->parent);
> +       struct i2c_client *i2c = iodev->i2c;
> +       struct led_flash_setting *s;
> +
> +       snprintf(v4l2_sd_cfg->dev_name, sizeof(v4l2_sd_cfg->dev_name),
> +                "%s %d-%04x", sub_led->fled_cdev.led_cdev.name,
> +                i2c_adapter_id(i2c->adapter), i2c->addr);
> +
> +       s = &v4l2_sd_cfg->torch_intensity;
> +       s->min = TORCH_IOUT_MIN;
> +       s->max = sub_led->fled_cdev.led_cdev.max_brightness * TORCH_IOUT_STEP;
> +       s->step = TORCH_IOUT_STEP;
> +       s->val = s->max;
> +
> +       /* Init flash faults config */
> +       v4l2_sd_cfg->flash_faults = LED_FAULT_OVER_VOLTAGE |
> +                               LED_FAULT_SHORT_CIRCUIT |
> +                               LED_FAULT_OVER_CURRENT;
> +
> +       v4l2_sd_cfg->has_external_strobe = true;
> +}
> +
> +static const struct v4l2_flash_ops v4l2_flash_ops = {
> +       .external_strobe_set = max77693_led_external_strobe_set,
> +};
> +#else
> +static inline void max77693_init_v4l2_flash_config(
> +                               struct max77693_sub_led *sub_led,
> +                               struct max77693_led_config_data *led_cfg,
> +                               struct v4l2_flash_config *v4l2_sd_cfg)
> +{
> +}
> +static const struct v4l2_flash_ops v4l2_flash_ops;
> +#endif
> +
>  static void max77693_init_fled_cdev(struct max77693_sub_led *sub_led,
>                                 struct max77693_led_config_data *led_cfg)
>  {
> @@ -870,12 +947,45 @@ static void max77693_init_fled_cdev(struct max77693_sub_led *sub_led,
>         sub_led->flash_timeout = fled_cdev->timeout.val;
>  }
>
> +static int max77693_register_led(struct max77693_sub_led *sub_led,
> +                                struct max77693_led_config_data *led_cfg,
> +                                struct device_node *sub_node)
> +{
> +       struct max77693_led_device *led = sub_led_to_led(sub_led);
> +       struct led_classdev_flash *fled_cdev = &sub_led->fled_cdev;
> +       struct device *dev = &led->pdev->dev;
> +       struct v4l2_flash_config v4l2_sd_cfg = {};
> +       int ret;
> +
> +       /* Register in the LED subsystem */
> +       ret = led_classdev_flash_register(dev, fled_cdev);
> +       if (ret < 0)
> +               return ret;
> +
> +       max77693_init_v4l2_flash_config(sub_led, led_cfg, &v4l2_sd_cfg);
> +
> +       /* Register in the V4L2 subsystem. */
> +       sub_led->v4l2_flash = v4l2_flash_init(dev, sub_node, fled_cdev, NULL,
> +                                             &v4l2_flash_ops, &v4l2_sd_cfg);
> +       if (IS_ERR(sub_led->v4l2_flash)) {
> +               ret = PTR_ERR(sub_led->v4l2_flash);
> +               goto err_v4l2_flash_init;
> +       }
> +
> +       return 0;
> +
> +err_v4l2_flash_init:
> +       led_classdev_flash_unregister(fled_cdev);
> +       return ret;
> +}
> +
>  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_device *led;
>         struct max77693_sub_led *sub_leds;
> +       struct device_node *sub_nodes[2] = {};
>         struct max77693_led_config_data led_cfg = {};
>         int init_fled_cdev[2], i, ret;
>
> @@ -889,7 +999,7 @@ static int max77693_led_probe(struct platform_device *pdev)
>         sub_leds = led->sub_leds;
>
>         platform_set_drvdata(pdev, led);
> -       ret = max77693_led_get_configuration(led, &led_cfg);
> +       ret = max77693_led_get_configuration(led, &led_cfg, sub_nodes);
>         if (ret < 0)
>                 return ret;
>
> @@ -911,8 +1021,12 @@ static int max77693_led_probe(struct platform_device *pdev)
>                 /* Initialize LED Flash class device */
>                 max77693_init_fled_cdev(&sub_leds[i], &led_cfg);
>
> -               /* Register LED Flash class device */
> -               ret = led_classdev_flash_register(dev, &sub_leds[i].fled_cdev);
> +               /*
> +                * Register LED Flash class device and corresponding
> +                * V4L2 Flash device.
> +                */
> +               ret = max77693_register_led(&sub_leds[i], &led_cfg,
> +                                               sub_nodes[i]);
>                 if (ret < 0) {
>                         /*
>                          * At this moment FLED1 might have been already
> @@ -931,6 +1045,7 @@ err_register_led2:
>         /* It is possible than only FLED2 was to be registered */
>         if (!init_fled_cdev[FLED1])
>                 goto err_register_led1;
> +       v4l2_flash_release(sub_leds[FLED1].v4l2_flash);
>         led_classdev_flash_unregister(&sub_leds[FLED1].fled_cdev);
>  err_register_led1:
>         mutex_destroy(&led->lock);
> @@ -944,11 +1059,13 @@ static int max77693_led_remove(struct platform_device *pdev)
>         struct max77693_sub_led *sub_leds = led->sub_leds;
>
>         if (led->iout_joint || max77693_fled_used(led, FLED1)) {
> +               v4l2_flash_release(sub_leds[FLED1].v4l2_flash);
>                 led_classdev_flash_unregister(&sub_leds[FLED1].fled_cdev);
>                 cancel_work_sync(&sub_leds[FLED1].work_brightness_set);
>         }
>
>         if (!led->iout_joint && max77693_fled_used(led, FLED2)) {
> +               v4l2_flash_release(sub_leds[FLED2].v4l2_flash);
>                 led_classdev_flash_unregister(&sub_leds[FLED2].fled_cdev);
>                 cancel_work_sync(&sub_leds[FLED2].work_brightness_set);
>         }
> --
> 1.7.9.5
>

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

* Re: [PATCH v10 4/8] DT: aat1290: Document handling external strobe sources
  2015-06-08  9:02 ` [PATCH v10 4/8] DT: aat1290: Document handling external strobe sources Jacek Anaszewski
@ 2015-06-10 18:12   ` Bryan Wu
  0 siblings, 0 replies; 24+ messages in thread
From: Bryan Wu @ 2015-06-10 18:12 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: Linux LED Subsystem, linux-media, Kyungmin Park, Pavel Machek,
	rpurdie, Sakari Ailus, Sylwester Nawrocki, devicetree

On Mon, Jun 8, 2015 at 2:02 AM, Jacek Anaszewski
<j.anaszewski@samsung.com> wrote:
> This patch adds documentation for a pinctrl-names property.
> The property, when present, is used for switching the source
> of the strobe signal for the device.
>

Merged into my -devel branch and it won't be merged into 4.2.0 merge
window but wait for one more cycle, since now it's quite late in 4.1.0
cycle.

Thanks,
-Bryan

> 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: Sakari Ailus <sakari.ailus@iki.fi>
> Cc: devicetree@vger.kernel.org
> ---
>  .../devicetree/bindings/leds/leds-aat1290.txt      |   36 ++++++++++++++++++--
>  1 file changed, 34 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/leds/leds-aat1290.txt b/Documentation/devicetree/bindings/leds/leds-aat1290.txt
> index ef88b9c..c05ed91 100644
> --- a/Documentation/devicetree/bindings/leds/leds-aat1290.txt
> +++ b/Documentation/devicetree/bindings/leds/leds-aat1290.txt
> @@ -2,7 +2,9 @@
>
>  The device is controlled through two pins: FL_EN and EN_SET. The pins when,
>  asserted high, enable flash strobe and movie mode (max 1/2 of flash current)
> -respectively.
> +respectively. In order to add a capability of selecting the strobe signal source
> +(e.g. CPU or camera sensor) there is an additional switch required, independent
> +of the flash chip. The switch is controlled with pin control.
>
>  Required properties:
>
> @@ -10,6 +12,13 @@ Required properties:
>  - flen-gpios : Must be device tree identifier of the flash device FL_EN pin.
>  - enset-gpios : Must be device tree identifier of the flash device EN_SET pin.
>
> +Optional properties:
> +- pinctrl-names : Must contain entries: "default", "host", "isp". Entries
> +               "default" and "host" must refer to the same pin configuration
> +               node, which sets the host as a strobe signal provider. Entry
> +               "isp" must refer to the pin configuration node, which sets the
> +               ISP as a strobe signal provider.
> +
>  A discrete LED element connected to the device must be represented by a child
>  node - see Documentation/devicetree/bindings/leds/common.txt.
>
> @@ -25,13 +34,22 @@ Required properties of the LED child node:
>  Optional properties of the LED child node:
>  - label : see Documentation/devicetree/bindings/leds/common.txt
>
> -Example (by Ct = 220nF, Rset = 160kohm):
> +Example (by Ct = 220nF, Rset = 160kohm and exynos4412-trats2 board with
> +a switch that allows for routing strobe signal either from the host or from
> +the camera sensor):
> +
> +#include "exynos4412.dtsi"
>
>  aat1290 {
>         compatible = "skyworks,aat1290";
>         flen-gpios = <&gpj1 1 GPIO_ACTIVE_HIGH>;
>         enset-gpios = <&gpj1 2 GPIO_ACTIVE_HIGH>;
>
> +       pinctrl-names = "default", "host", "isp";
> +       pinctrl-0 = <&camera_flash_host>;
> +       pinctrl-1 = <&camera_flash_host>;
> +       pinctrl-2 = <&camera_flash_isp>;
> +
>         camera_flash: flash-led {
>                 label = "aat1290-flash";
>                 led-max-microamp = <520833>;
> @@ -39,3 +57,17 @@ aat1290 {
>                 flash-timeout-us = <1940000>;
>         };
>  };
> +
> +&pinctrl_0 {
> +       camera_flash_host: camera-flash-host {
> +               samsung,pins = "gpj1-0";
> +               samsung,pin-function = <1>;
> +               samsung,pin-val = <0>;
> +       };
> +
> +       camera_flash_isp: camera-flash-isp {
> +               samsung,pins = "gpj1-0";
> +               samsung,pin-function = <1>;
> +               samsung,pin-val = <1>;
> +       };
> +};
> --
> 1.7.9.5
>

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

* Re: [PATCH v10 5/8] leds: aat1290: add support for V4L2 Flash sub-device
  2015-06-08  9:02 ` [PATCH v10 5/8] leds: aat1290: add support for V4L2 Flash sub-device Jacek Anaszewski
@ 2015-06-10 18:12   ` Bryan Wu
  0 siblings, 0 replies; 24+ messages in thread
From: Bryan Wu @ 2015-06-10 18:12 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: Linux LED Subsystem, linux-media, Kyungmin Park, Pavel Machek,
	rpurdie, Sakari Ailus, Sylwester Nawrocki

On Mon, Jun 8, 2015 at 2:02 AM, Jacek Anaszewski
<j.anaszewski@samsung.com> wrote:
> Add support for V4L2 Flash sub-device to the aat1290 LED Flash class
> driver. The support allows for V4L2 Flash sub-device to take the control
> of the LED Flash class device.
>

Merged into my -devel branch and it won't be merged into 4.2.0 merge
window but wait for one more cycle, since now it's quite late in 4.1.0
cycle.

Thanks,
-Bryan

> 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: Sakari Ailus <sakari.ailus@iki.fi>
> ---
>  drivers/leds/Kconfig        |    1 +
>  drivers/leds/leds-aat1290.c |  137 +++++++++++++++++++++++++++++++++++++++++--
>  2 files changed, 132 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
> index 86de046..b04f82c 100644
> --- a/drivers/leds/Kconfig
> +++ b/drivers/leds/Kconfig
> @@ -47,6 +47,7 @@ config LEDS_AAT1290
>         depends on LEDS_CLASS_FLASH
>         depends on GPIOLIB
>         depends on OF
> +       depends on PINCTRL
>         help
>          This option enables support for the LEDs on the AAT1290.
>
> diff --git a/drivers/leds/leds-aat1290.c b/drivers/leds/leds-aat1290.c
> index 6ea1d54..d3215d5 100644
> --- a/drivers/leds/leds-aat1290.c
> +++ b/drivers/leds/leds-aat1290.c
> @@ -17,9 +17,11 @@
>  #include <linux/module.h>
>  #include <linux/mutex.h>
>  #include <linux/of.h>
> +#include <linux/pinctrl/consumer.h>
>  #include <linux/platform_device.h>
>  #include <linux/slab.h>
>  #include <linux/workqueue.h>
> +#include <media/v4l2-flash-led-class.h>
>
>  #define AAT1290_MOVIE_MODE_CURRENT_ADDR        17
>  #define AAT1290_MAX_MM_CURR_PERCENT_0  16
> @@ -52,6 +54,8 @@ struct aat1290_led_config_data {
>         u32 max_flash_current;
>         /* maximum flash timeout */
>         u32 max_flash_tm;
> +       /* external strobe capability */
> +       bool has_external_strobe;
>         /* max LED brightness level */
>         enum led_brightness max_brightness;
>  };
> @@ -64,6 +68,8 @@ struct aat1290_led {
>
>         /* corresponding LED Flash class device */
>         struct led_classdev_flash fled_cdev;
> +       /* V4L2 Flash device */
> +       struct v4l2_flash *v4l2_flash;
>
>         /* FLEN pin */
>         struct gpio_desc *gpio_fl_en;
> @@ -230,11 +236,15 @@ static int aat1290_led_flash_timeout_set(struct led_classdev_flash *fled_cdev,
>  }
>
>  static int aat1290_led_parse_dt(struct aat1290_led *led,
> -                       struct aat1290_led_config_data *cfg)
> +                       struct aat1290_led_config_data *cfg,
> +                       struct device_node **sub_node)
>  {
>         struct led_classdev *led_cdev = &led->fled_cdev.led_cdev;
>         struct device *dev = &led->pdev->dev;
>         struct device_node *child_node;
> +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
> +       struct pinctrl *pinctrl;
> +#endif
>         int ret = 0;
>
>         led->gpio_fl_en = devm_gpiod_get(dev, "flen");
> @@ -251,6 +261,17 @@ static int aat1290_led_parse_dt(struct aat1290_led *led,
>                 return ret;
>         }
>
> +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
> +       pinctrl = devm_pinctrl_get_select_default(&led->pdev->dev);
> +       if (IS_ERR(pinctrl)) {
> +               cfg->has_external_strobe = false;
> +               dev_info(dev,
> +                        "No support for external strobe detected.\n");
> +       } else {
> +               cfg->has_external_strobe = true;
> +       }
> +#endif
> +
>         child_node = of_get_next_available_child(dev->of_node, NULL);
>         if (!child_node) {
>                 dev_err(dev, "No DT child node found for connected LED.\n");
> @@ -288,6 +309,8 @@ static int aat1290_led_parse_dt(struct aat1290_led *led,
>
>         of_node_put(child_node);
>
> +       *sub_node = child_node;
> +
>         return ret;
>  }
>
> @@ -316,7 +339,8 @@ int init_mm_current_scale(struct aat1290_led *led,
>         int i, max_mm_current =
>                         AAT1290_MAX_MM_CURRENT(cfg->max_flash_current);
>
> -       led->mm_current_scale = kzalloc(sizeof(max_mm_current_percent),
> +       led->mm_current_scale = devm_kzalloc(&led->pdev->dev,
> +                                       sizeof(max_mm_current_percent),
>                                         GFP_KERNEL);
>         if (!led->mm_current_scale)
>                 return -ENOMEM;
> @@ -329,11 +353,12 @@ int init_mm_current_scale(struct aat1290_led *led,
>  }
>
>  static int aat1290_led_get_configuration(struct aat1290_led *led,
> -                                       struct aat1290_led_config_data *cfg)
> +                                       struct aat1290_led_config_data *cfg,
> +                                       struct device_node **sub_node)
>  {
>         int ret;
>
> -       ret = aat1290_led_parse_dt(led, cfg);
> +       ret = aat1290_led_parse_dt(led, cfg, sub_node);
>         if (ret < 0)
>                 return ret;
>         /*
> @@ -346,7 +371,10 @@ static int aat1290_led_get_configuration(struct aat1290_led *led,
>
>         aat1290_led_validate_mm_current(led, cfg);
>
> -       kfree(led->mm_current_scale);
> +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
> +#else
> +       devm_kfree(&led->pdev->dev, led->mm_current_scale);
> +#endif
>
>         return 0;
>  }
> @@ -365,6 +393,88 @@ static void aat1290_init_flash_timeout(struct aat1290_led *led,
>         setting->val = setting->max;
>  }
>
> +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
> +enum led_brightness aat1290_intensity_to_brightness(
> +                                       struct v4l2_flash *v4l2_flash,
> +                                       s32 intensity)
> +{
> +       struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
> +       struct aat1290_led *led = fled_cdev_to_led(fled_cdev);
> +       int i;
> +
> +       for (i = AAT1290_MM_CURRENT_SCALE_SIZE - 1; i >= 0; --i)
> +               if (intensity >= led->mm_current_scale[i])
> +                       return i + 1;
> +
> +       return 1;
> +}
> +
> +s32 aat1290_brightness_to_intensity(struct v4l2_flash *v4l2_flash,
> +                                       enum led_brightness brightness)
> +{
> +       struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
> +       struct aat1290_led *led = fled_cdev_to_led(fled_cdev);
> +
> +       return led->mm_current_scale[brightness - 1];
> +}
> +
> +static int aat1290_led_external_strobe_set(struct v4l2_flash *v4l2_flash,
> +                                               bool enable)
> +{
> +       struct aat1290_led *led = fled_cdev_to_led(v4l2_flash->fled_cdev);
> +       struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
> +       struct led_classdev *led_cdev = &fled_cdev->led_cdev;
> +       struct pinctrl *pinctrl;
> +
> +       gpiod_direction_output(led->gpio_fl_en, 0);
> +       gpiod_direction_output(led->gpio_en_set, 0);
> +
> +       led->movie_mode = false;
> +       led_cdev->brightness = 0;
> +
> +       pinctrl = devm_pinctrl_get_select(&led->pdev->dev,
> +                                               enable ? "isp" : "host");
> +       if (IS_ERR(pinctrl)) {
> +               dev_warn(&led->pdev->dev, "Unable to switch strobe source.\n");
> +               return PTR_ERR(pinctrl);
> +       }
> +
> +       return 0;
> +}
> +
> +static void aat1290_init_v4l2_flash_config(struct aat1290_led *led,
> +                                       struct aat1290_led_config_data *led_cfg,
> +                                       struct v4l2_flash_config *v4l2_sd_cfg)
> +{
> +       struct led_classdev *led_cdev = &led->fled_cdev.led_cdev;
> +       struct led_flash_setting *s;
> +
> +       strlcpy(v4l2_sd_cfg->dev_name, led_cdev->name,
> +               sizeof(v4l2_sd_cfg->dev_name));
> +
> +       s = &v4l2_sd_cfg->torch_intensity;
> +       s->min = led->mm_current_scale[0];
> +       s->max = led_cfg->max_mm_current;
> +       s->step = 1;
> +       s->val = s->max;
> +
> +       v4l2_sd_cfg->has_external_strobe = led_cfg->has_external_strobe;
> +}
> +
> +static const struct v4l2_flash_ops v4l2_flash_ops = {
> +       .external_strobe_set = aat1290_led_external_strobe_set,
> +       .intensity_to_led_brightness = aat1290_intensity_to_brightness,
> +       .led_brightness_to_intensity = aat1290_brightness_to_intensity,
> +};
> +#else
> +static inline void aat1290_init_v4l2_flash_config(struct aat1290_led *led,
> +                               struct aat1290_led_config_data *led_cfg,
> +                               struct v4l2_flash_config *v4l2_sd_cfg)
> +{
> +}
> +static const struct v4l2_flash_ops v4l2_flash_ops;
> +#endif
> +
>  static const struct led_flash_ops flash_ops = {
>         .strobe_set = aat1290_led_flash_strobe_set,
>         .timeout_set = aat1290_led_flash_timeout_set,
> @@ -373,10 +483,12 @@ static const struct led_flash_ops flash_ops = {
>  static int aat1290_led_probe(struct platform_device *pdev)
>  {
>         struct device *dev = &pdev->dev;
> +       struct device_node *sub_node = NULL;
>         struct aat1290_led *led;
>         struct led_classdev *led_cdev;
>         struct led_classdev_flash *fled_cdev;
>         struct aat1290_led_config_data led_cfg = {};
> +       struct v4l2_flash_config v4l2_sd_cfg = {};
>         int ret;
>
>         led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
> @@ -390,7 +502,7 @@ static int aat1290_led_probe(struct platform_device *pdev)
>         fled_cdev->ops = &flash_ops;
>         led_cdev = &fled_cdev->led_cdev;
>
> -       ret = aat1290_led_get_configuration(led, &led_cfg);
> +       ret = aat1290_led_get_configuration(led, &led_cfg, &sub_node);
>         if (ret < 0)
>                 return ret;
>
> @@ -410,8 +522,20 @@ static int aat1290_led_probe(struct platform_device *pdev)
>         if (ret < 0)
>                 goto err_flash_register;
>
> +       aat1290_init_v4l2_flash_config(led, &led_cfg, &v4l2_sd_cfg);
> +
> +       /* Create V4L2 Flash subdev. */
> +       led->v4l2_flash = v4l2_flash_init(dev, sub_node, fled_cdev, NULL,
> +                                         &v4l2_flash_ops, &v4l2_sd_cfg);
> +       if (IS_ERR(led->v4l2_flash)) {
> +               ret = PTR_ERR(led->v4l2_flash);
> +               goto error_v4l2_flash_init;
> +       }
> +
>         return 0;
>
> +error_v4l2_flash_init:
> +       led_classdev_flash_unregister(fled_cdev);
>  err_flash_register:
>         mutex_destroy(&led->lock);
>
> @@ -422,6 +546,7 @@ 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->fled_cdev);
>         cancel_work_sync(&led->work_brightness_set);
>
> --
> 1.7.9.5
>

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

* Re: [PATCH v10 2/8] media: Add registration helpers for V4L2 flash sub-devices
  2015-06-10 18:01     ` Bryan Wu
@ 2015-06-10 18:12       ` Bryan Wu
  2015-06-10 18:29         ` Pavel Machek
  2015-06-10 21:34         ` Sakari Ailus
  0 siblings, 2 replies; 24+ messages in thread
From: Bryan Wu @ 2015-06-10 18:12 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: Linux LED Subsystem, linux-media, Kyungmin Park, Pavel Machek,
	rpurdie, Sakari Ailus, Sylwester Nawrocki, Hans Verkuil

On Wed, Jun 10, 2015 at 11:01 AM, Bryan Wu <cooloney@gmail.com> wrote:
> On Wed, Jun 10, 2015 at 10:57 AM, Bryan Wu <cooloney@gmail.com> wrote:
>> On Mon, Jun 8, 2015 at 2:02 AM, Jacek Anaszewski
>> <j.anaszewski@samsung.com> wrote:
>>> This patch adds helper functions for registering/unregistering
>>> LED Flash class devices as V4L2 sub-devices. 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.
>>>
>>
>> Please go ahead with my Ack
>>
>> Acked-by: Bryan Wu <cooloney@gmail.com>
>>
>
> I found the rest of LED patches depend on this one. What about merging
> this through my tree?
>
> -Bryan
>
>

Merged into my -devel branch and it won't be merged into 4.2.0 merge
window but wait for one more cycle, since now it's quite late in 4.1.0
cycle.

Thanks,
-Bryan


>>
>>> 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-led-class.c |  708 ++++++++++++++++++++++++
>>>  include/media/v4l2-flash-led-class.h           |  148 +++++
>>>  4 files changed, 869 insertions(+)
>>>  create mode 100644 drivers/media/v4l2-core/v4l2-flash-led-class.c
>>>  create mode 100644 include/media/v4l2-flash-led-class.h
>>>
>>> diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
>>> index f7a01a7..b4b0229 100644
>>> --- a/drivers/media/v4l2-core/Kconfig
>>> +++ b/drivers/media/v4l2-core/Kconfig
>>> @@ -44,6 +44,17 @@ config V4L2_MEM2MEM_DEV
>>>          tristate
>>>          depends on VIDEOBUF2_CORE
>>>
>>> +# Used by LED subsystem flash drivers
>>> +config V4L2_FLASH_LED_CLASS
>>> +       tristate "V4L2 flash API for LED flash class devices"
>>> +       depends on VIDEO_V4L2_SUBDEV_API
>>> +       depends on LEDS_CLASS_FLASH
>>> +       ---help---
>>> +         Say Y here to enable V4L2 flash API support for LED flash
>>> +         class drivers.
>>> +
>>> +         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..dc3de00 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-led-class.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-led-class.c b/drivers/media/v4l2-core/v4l2-flash-led-class.c
>>> new file mode 100644
>>> index 0000000..4e19dac
>>> --- /dev/null
>>> +++ b/drivers/media/v4l2-core/v4l2-flash-led-class.c
>>> @@ -0,0 +1,708 @@
>>> +/*
>>> + * V4L2 flash LED sub-device registration helpers.
>>> + *
>>> + *     Copyright (C) 2015 Samsung Electronics Co., Ltd
>>> + *     Author: Jacek Anaszewski <j.anaszewski@samsung.com>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License version 2 as
>>> + * published by the Free Software Foundation.
>>> + */
>>> +
>>> +#include <linux/led-class-flash.h>
>>> +#include <linux/module.h>
>>> +#include <linux/mutex.h>
>>> +#include <linux/of.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/types.h>
>>> +#include <media/v4l2-flash-led-class.h>
>>> +
>>> +#define has_flash_op(v4l2_flash, op)                           \
>>> +       (v4l2_flash && v4l2_flash->ops->op)
>>> +
>>> +#define call_flash_op(v4l2_flash, op, arg)                     \
>>> +               (has_flash_op(v4l2_flash, op) ?                 \
>>> +                       v4l2_flash->ops->op(v4l2_flash, arg) :  \
>>> +                       -EINVAL)
>>> +
>>> +enum ctrl_init_data_id {
>>> +       LED_MODE,
>>> +       TORCH_INTENSITY,
>>> +       FLASH_INTENSITY,
>>> +       INDICATOR_INTENSITY,
>>> +       FLASH_TIMEOUT,
>>> +       STROBE_SOURCE,
>>> +       /*
>>> +        * Only above values are applicable to
>>> +        * the 'ctrls' array in the struct v4l2_flash.
>>> +        */
>>> +       FLASH_STROBE,
>>> +       STROBE_STOP,
>>> +       STROBE_STATUS,
>>> +       FLASH_FAULT,
>>> +       NUM_FLASH_CTRLS,
>>> +};
>>> +
>>> +static enum led_brightness __intensity_to_led_brightness(
>>> +                                       struct v4l2_ctrl *ctrl, s32 intensity)
>>> +{
>>> +       intensity -= ctrl->minimum;
>>> +       intensity /= (u32) ctrl->step;
>>> +
>>> +       /*
>>> +        * Indicator LEDs, unlike torch LEDs, are turned on/off basing on
>>> +        * the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only.
>>> +        * Therefore it must be possible to set it to 0 level which in
>>> +        * the LED subsystem reflects LED_OFF state.
>>> +        */
>>> +       if (ctrl->minimum)
>>> +               ++intensity;
>>> +
>>> +       return intensity;
>>> +}
>>> +
>>> +static s32 __led_brightness_to_intensity(struct v4l2_ctrl *ctrl,
>>> +                                        enum led_brightness brightness)
>>> +{
>>> +       /*
>>> +        * Indicator LEDs, unlike torch LEDs, are turned on/off basing on
>>> +        * the state of V4L2_CID_FLASH_INDICATOR_INTENSITY control only.
>>> +        * Do not decrement brightness read from the LED subsystem for
>>> +        * indicator LED as it may equal 0. For torch LEDs this function
>>> +        * is called only when V4L2_FLASH_LED_MODE_TORCH is set and the
>>> +        * brightness read is guaranteed to be greater than 0. In the mode
>>> +        * V4L2_FLASH_LED_MODE_NONE the cached torch intensity value is used.
>>> +        */
>>> +       if (ctrl->id != V4L2_CID_FLASH_INDICATOR_INTENSITY)
>>> +               --brightness;
>>> +
>>> +       return (brightness * ctrl->step) + ctrl->minimum;
>>> +}
>>> +
>>> +static void v4l2_flash_set_led_brightness(struct v4l2_flash *v4l2_flash,
>>> +                                       struct v4l2_ctrl *ctrl)
>>> +{
>>> +       struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
>>> +       enum led_brightness brightness;
>>> +
>>> +       if (has_flash_op(v4l2_flash, intensity_to_led_brightness))
>>> +               brightness = call_flash_op(v4l2_flash,
>>> +                                       intensity_to_led_brightness,
>>> +                                       ctrl->val);
>>> +       else
>>> +               brightness = __intensity_to_led_brightness(ctrl, ctrl->val);
>>> +       /*
>>> +        * In case a LED Flash class driver provides ops for custom
>>> +        * brightness <-> intensity conversion, it also must have defined
>>> +        * related v4l2 control step == 1. In such a case a backward conversion
>>> +        * from led brightness to v4l2 intensity is required to find out the
>>> +        * the aligned intensity value.
>>> +        */
>>> +       if (has_flash_op(v4l2_flash, led_brightness_to_intensity))
>>> +               ctrl->val = call_flash_op(v4l2_flash,
>>> +                                       led_brightness_to_intensity,
>>> +                                       brightness);
>>> +
>>> +       if (ctrl == ctrls[TORCH_INTENSITY]) {
>>> +               if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
>>> +                       return;
>>> +
>>> +               led_set_brightness(&v4l2_flash->fled_cdev->led_cdev,
>>> +                                       brightness);
>>> +       } else {
>>> +               led_set_brightness(&v4l2_flash->iled_cdev->led_cdev,
>>> +                                       brightness);
>>> +       }
>>> +}
>>> +
>>> +static int v4l2_flash_update_led_brightness(struct v4l2_flash *v4l2_flash,
>>> +                                       struct v4l2_ctrl *ctrl)
>>> +{
>>> +       struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
>>> +       struct led_classdev *led_cdev;
>>> +       int ret;
>>> +
>>> +       if (ctrl == ctrls[TORCH_INTENSITY]) {
>>> +               /*
>>> +                * Update torch brightness only if in TORCH_MODE. In other modes
>>> +                * torch led is turned off, which would spuriously inform the
>>> +                * user space that V4L2_CID_FLASH_TORCH_INTENSITY control value
>>> +                * has changed to 0.
>>> +                */
>>> +               if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
>>> +                       return 0;
>>> +               led_cdev = &v4l2_flash->fled_cdev->led_cdev;
>>> +       } else {
>>> +               led_cdev = &v4l2_flash->iled_cdev->led_cdev;
>>> +       }
>>> +
>>> +       ret = led_update_brightness(led_cdev);
>>> +       if (ret < 0)
>>> +               return ret;
>>> +
>>> +       if (has_flash_op(v4l2_flash, led_brightness_to_intensity))
>>> +               ctrl->val = call_flash_op(v4l2_flash,
>>> +                                               led_brightness_to_intensity,
>>> +                                               led_cdev->brightness);
>>> +       else
>>> +               ctrl->val = __led_brightness_to_intensity(ctrl,
>>> +                                               led_cdev->brightness);
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +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 *fled_cdev = v4l2_flash->fled_cdev;
>>> +       bool is_strobing;
>>> +       int ret;
>>> +
>>> +       switch (c->id) {
>>> +       case V4L2_CID_FLASH_TORCH_INTENSITY:
>>> +       case V4L2_CID_FLASH_INDICATOR_INTENSITY:
>>> +               return v4l2_flash_update_led_brightness(v4l2_flash, c);
>>> +       case V4L2_CID_FLASH_INTENSITY:
>>> +               ret = led_update_flash_brightness(fled_cdev);
>>> +               if (ret < 0)
>>> +                       return ret;
>>> +               /*
>>> +                * No conversion is needed as LED Flash class also uses
>>> +                * microamperes for flash intensity units.
>>> +                */
>>> +               c->val = fled_cdev->brightness.val;
>>> +               return 0;
>>> +       case V4L2_CID_FLASH_STROBE_STATUS:
>>> +               ret = led_get_flash_strobe(fled_cdev, &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 */
>>> +               return led_get_flash_fault(fled_cdev, &c->val);
>>> +       default:
>>> +               return -EINVAL;
>>> +       }
>>> +}
>>> +
>>> +static bool __software_strobe_mode_inactive(struct v4l2_ctrl **ctrls)
>>> +{
>>> +       return ((ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH) ||
>>> +               (ctrls[STROBE_SOURCE] && (ctrls[STROBE_SOURCE]->val !=
>>> +                               V4L2_FLASH_STROBE_SOURCE_SOFTWARE)));
>>> +}
>>> +
>>> +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 *fled_cdev = v4l2_flash->fled_cdev;
>>> +       struct led_classdev *led_cdev = &fled_cdev->led_cdev;
>>> +       struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
>>> +       bool external_strobe;
>>> +       int ret = 0;
>>> +
>>> +       switch (c->id) {
>>> +       case V4L2_CID_FLASH_LED_MODE:
>>> +               switch (c->val) {
>>> +               case V4L2_FLASH_LED_MODE_NONE:
>>> +                       led_set_brightness(led_cdev, LED_OFF);
>>> +                       return led_set_flash_strobe(fled_cdev, false);
>>> +               case V4L2_FLASH_LED_MODE_FLASH:
>>> +                       /* Turn the torch LED off */
>>> +                       led_set_brightness(led_cdev, LED_OFF);
>>> +                       if (ctrls[STROBE_SOURCE]) {
>>> +                               external_strobe = (ctrls[STROBE_SOURCE]->val ==
>>> +                                       V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
>>> +
>>> +                               ret = call_flash_op(v4l2_flash,
>>> +                                               external_strobe_set,
>>> +                                               external_strobe);
>>> +                       }
>>> +                       return ret;
>>> +               case V4L2_FLASH_LED_MODE_TORCH:
>>> +                       if (ctrls[STROBE_SOURCE]) {
>>> +                               ret = call_flash_op(v4l2_flash,
>>> +                                               external_strobe_set,
>>> +                                               false);
>>> +                               if (ret < 0)
>>> +                                       return ret;
>>> +                       }
>>> +                       /* Stop flash strobing */
>>> +                       ret = led_set_flash_strobe(fled_cdev, false);
>>> +                       if (ret < 0)
>>> +                               return ret;
>>> +
>>> +                       v4l2_flash_set_led_brightness(v4l2_flash,
>>> +                                                       ctrls[TORCH_INTENSITY]);
>>> +                       return 0;
>>> +               }
>>> +               break;
>>> +       case V4L2_CID_FLASH_STROBE_SOURCE:
>>> +               external_strobe = (c->val == V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
>>> +               /*
>>> +                * For some hardware arrangements setting strobe source may
>>> +                * affect torch mode. Therefore, if not in the flash mode,
>>> +                * cache only this setting. It will be applied upon switching
>>> +                * to flash mode.
>>> +                */
>>> +               if (ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_FLASH)
>>> +                       return 0;
>>> +
>>> +               return call_flash_op(v4l2_flash, external_strobe_set,
>>> +                                       external_strobe);
>>> +       case V4L2_CID_FLASH_STROBE:
>>> +               if (__software_strobe_mode_inactive(ctrls))
>>> +                       return -EBUSY;
>>> +               return led_set_flash_strobe(fled_cdev, true);
>>> +       case V4L2_CID_FLASH_STROBE_STOP:
>>> +               if (__software_strobe_mode_inactive(ctrls))
>>> +                       return -EBUSY;
>>> +               return led_set_flash_strobe(fled_cdev, false);
>>> +       case V4L2_CID_FLASH_TIMEOUT:
>>> +               /*
>>> +                * No conversion is needed as LED Flash class also uses
>>> +                * microseconds for flash timeout units.
>>> +                */
>>> +               return led_set_flash_timeout(fled_cdev, c->val);
>>> +       case V4L2_CID_FLASH_INTENSITY:
>>> +               /*
>>> +                * No conversion is needed as LED Flash class also uses
>>> +                * microamperes for flash intensity units.
>>> +                */
>>> +               return led_set_flash_brightness(fled_cdev, c->val);
>>> +       case V4L2_CID_FLASH_TORCH_INTENSITY:
>>> +       case V4L2_CID_FLASH_INDICATOR_INTENSITY:
>>> +               v4l2_flash_set_led_brightness(v4l2_flash, c);
>>> +               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 void __lfs_to_v4l2_ctrl_config(struct led_flash_setting *s,
>>> +                               struct v4l2_ctrl_config *c)
>>> +{
>>> +       c->min = s->min;
>>> +       c->max = s->max;
>>> +       c->step = s->step;
>>> +       c->def = s->val;
>>> +}
>>> +
>>> +static void __fill_ctrl_init_data(struct v4l2_flash *v4l2_flash,
>>> +                         struct v4l2_flash_config *flash_cfg,
>>> +                         struct v4l2_flash_ctrl_data *ctrl_init_data)
>>> +{
>>> +       struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
>>> +       const struct led_flash_ops *fled_cdev_ops = fled_cdev->ops;
>>> +       struct led_classdev *led_cdev = &fled_cdev->led_cdev;
>>> +       struct v4l2_ctrl_config *ctrl_cfg;
>>> +       u32 mask;
>>> +
>>> +       /* Init FLASH_FAULT ctrl data */
>>> +       if (flash_cfg->flash_faults) {
>>> +               ctrl_init_data[FLASH_FAULT].cid = V4L2_CID_FLASH_FAULT;
>>> +               ctrl_cfg = &ctrl_init_data[FLASH_FAULT].config;
>>> +               ctrl_cfg->id = V4L2_CID_FLASH_FAULT;
>>> +               ctrl_cfg->max = flash_cfg->flash_faults;
>>> +               ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
>>> +                                 V4L2_CTRL_FLAG_READ_ONLY;
>>> +       }
>>> +
>>> +       /* Init FLASH_LED_MODE ctrl data */
>>> +       mask = 1 << V4L2_FLASH_LED_MODE_NONE |
>>> +              1 << V4L2_FLASH_LED_MODE_TORCH;
>>> +       if (led_cdev->flags & LED_DEV_CAP_FLASH)
>>> +               mask |= 1 << V4L2_FLASH_LED_MODE_FLASH;
>>> +
>>> +       ctrl_init_data[LED_MODE].cid = V4L2_CID_FLASH_LED_MODE;
>>> +       ctrl_cfg = &ctrl_init_data[LED_MODE].config;
>>> +       ctrl_cfg->id = V4L2_CID_FLASH_LED_MODE;
>>> +       ctrl_cfg->max = V4L2_FLASH_LED_MODE_TORCH;
>>> +       ctrl_cfg->menu_skip_mask = ~mask;
>>> +       ctrl_cfg->def = V4L2_FLASH_LED_MODE_NONE;
>>> +       ctrl_cfg->flags = 0;
>>> +
>>> +       /* Init TORCH_INTENSITY ctrl data */
>>> +       ctrl_init_data[TORCH_INTENSITY].cid = V4L2_CID_FLASH_TORCH_INTENSITY;
>>> +       ctrl_cfg = &ctrl_init_data[TORCH_INTENSITY].config;
>>> +       __lfs_to_v4l2_ctrl_config(&flash_cfg->torch_intensity, ctrl_cfg);
>>> +       ctrl_cfg->id = V4L2_CID_FLASH_TORCH_INTENSITY;
>>> +       ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
>>> +                         V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
>>> +
>>> +       /* Init INDICATOR_INTENSITY ctrl data */
>>> +       if (v4l2_flash->iled_cdev) {
>>> +               ctrl_init_data[INDICATOR_INTENSITY].cid =
>>> +                                       V4L2_CID_FLASH_INDICATOR_INTENSITY;
>>> +               ctrl_cfg = &ctrl_init_data[INDICATOR_INTENSITY].config;
>>> +               __lfs_to_v4l2_ctrl_config(&flash_cfg->indicator_intensity,
>>> +                                         ctrl_cfg);
>>> +               ctrl_cfg->id = V4L2_CID_FLASH_INDICATOR_INTENSITY;
>>> +               ctrl_cfg->min = 0;
>>> +               ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
>>> +                                 V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
>>> +       }
>>> +
>>> +       if (!(led_cdev->flags & LED_DEV_CAP_FLASH))
>>> +               return;
>>> +
>>> +       /* Init FLASH_STROBE ctrl data */
>>> +       ctrl_init_data[FLASH_STROBE].cid = V4L2_CID_FLASH_STROBE;
>>> +       ctrl_cfg = &ctrl_init_data[FLASH_STROBE].config;
>>> +       ctrl_cfg->id = V4L2_CID_FLASH_STROBE;
>>> +
>>> +       /* Init STROBE_STOP ctrl data */
>>> +       ctrl_init_data[STROBE_STOP].cid = V4L2_CID_FLASH_STROBE_STOP;
>>> +       ctrl_cfg = &ctrl_init_data[STROBE_STOP].config;
>>> +       ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STOP;
>>> +
>>> +       /* Init FLASH_STROBE_SOURCE ctrl data */
>>> +       if (flash_cfg->has_external_strobe) {
>>> +               mask = (1 << V4L2_FLASH_STROBE_SOURCE_SOFTWARE) |
>>> +                      (1 << V4L2_FLASH_STROBE_SOURCE_EXTERNAL);
>>> +               ctrl_init_data[STROBE_SOURCE].cid =
>>> +                                       V4L2_CID_FLASH_STROBE_SOURCE;
>>> +               ctrl_cfg = &ctrl_init_data[STROBE_SOURCE].config;
>>> +               ctrl_cfg->id = V4L2_CID_FLASH_STROBE_SOURCE;
>>> +               ctrl_cfg->max = V4L2_FLASH_STROBE_SOURCE_EXTERNAL;
>>> +               ctrl_cfg->menu_skip_mask = ~mask;
>>> +               ctrl_cfg->def = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
>>> +       }
>>> +
>>> +       /* Init STROBE_STATUS ctrl data */
>>> +       if (fled_cdev_ops->strobe_get) {
>>> +               ctrl_init_data[STROBE_STATUS].cid =
>>> +                                       V4L2_CID_FLASH_STROBE_STATUS;
>>> +               ctrl_cfg = &ctrl_init_data[STROBE_STATUS].config;
>>> +               ctrl_cfg->id = V4L2_CID_FLASH_STROBE_STATUS;
>>> +               ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
>>> +                                 V4L2_CTRL_FLAG_READ_ONLY;
>>> +       }
>>> +
>>> +       /* Init FLASH_TIMEOUT ctrl data */
>>> +       if (fled_cdev_ops->timeout_set) {
>>> +               ctrl_init_data[FLASH_TIMEOUT].cid = V4L2_CID_FLASH_TIMEOUT;
>>> +               ctrl_cfg = &ctrl_init_data[FLASH_TIMEOUT].config;
>>> +               __lfs_to_v4l2_ctrl_config(&fled_cdev->timeout, ctrl_cfg);
>>> +               ctrl_cfg->id = V4L2_CID_FLASH_TIMEOUT;
>>> +       }
>>> +
>>> +       /* Init FLASH_INTENSITY ctrl data */
>>> +       if (fled_cdev_ops->flash_brightness_set) {
>>> +               ctrl_init_data[FLASH_INTENSITY].cid = V4L2_CID_FLASH_INTENSITY;
>>> +               ctrl_cfg = &ctrl_init_data[FLASH_INTENSITY].config;
>>> +               __lfs_to_v4l2_ctrl_config(&fled_cdev->brightness, ctrl_cfg);
>>> +               ctrl_cfg->id = V4L2_CID_FLASH_INTENSITY;
>>> +               ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
>>> +                                 V4L2_CTRL_FLAG_EXECUTE_ON_WRITE;
>>> +       }
>>> +}
>>> +
>>> +static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash,
>>> +                               struct v4l2_flash_config *flash_cfg)
>>> +
>>> +{
>>> +       struct v4l2_flash_ctrl_data *ctrl_init_data;
>>> +       struct v4l2_ctrl *ctrl;
>>> +       struct v4l2_ctrl_config *ctrl_cfg;
>>> +       int i, ret, num_ctrls = 0;
>>> +
>>> +       v4l2_flash->ctrls = devm_kzalloc(v4l2_flash->sd.dev,
>>> +                                       sizeof(*v4l2_flash->ctrls) *
>>> +                                       (STROBE_SOURCE + 1), GFP_KERNEL);
>>> +       if (!v4l2_flash->ctrls)
>>> +               return -ENOMEM;
>>> +
>>> +       /* allocate memory dynamically so as not to exceed stack frame size */
>>> +       ctrl_init_data = kcalloc(NUM_FLASH_CTRLS, sizeof(*ctrl_init_data),
>>> +                                       GFP_KERNEL);
>>> +       if (!ctrl_init_data)
>>> +               return -ENOMEM;
>>> +
>>> +       __fill_ctrl_init_data(v4l2_flash, flash_cfg, ctrl_init_data);
>>> +
>>> +       for (i = 0; i < NUM_FLASH_CTRLS; ++i)
>>> +               if (ctrl_init_data[i].cid)
>>> +                       ++num_ctrls;
>>> +
>>> +       v4l2_ctrl_handler_init(&v4l2_flash->hdl, num_ctrls);
>>> +
>>> +       for (i = 0; i < NUM_FLASH_CTRLS; ++i) {
>>> +               ctrl_cfg = &ctrl_init_data[i].config;
>>> +               if (!ctrl_init_data[i].cid)
>>> +                       continue;
>>> +
>>> +               if (ctrl_cfg->id == V4L2_CID_FLASH_LED_MODE ||
>>> +                   ctrl_cfg->id == V4L2_CID_FLASH_STROBE_SOURCE)
>>> +                       ctrl = v4l2_ctrl_new_std_menu(&v4l2_flash->hdl,
>>> +                                               &v4l2_flash_ctrl_ops,
>>> +                                               ctrl_cfg->id,
>>> +                                               ctrl_cfg->max,
>>> +                                               ctrl_cfg->menu_skip_mask,
>>> +                                               ctrl_cfg->def);
>>> +               else
>>> +                       ctrl = v4l2_ctrl_new_std(&v4l2_flash->hdl,
>>> +                                               &v4l2_flash_ctrl_ops,
>>> +                                               ctrl_cfg->id,
>>> +                                               ctrl_cfg->min,
>>> +                                               ctrl_cfg->max,
>>> +                                               ctrl_cfg->step,
>>> +                                               ctrl_cfg->def);
>>> +
>>> +               if (ctrl)
>>> +                       ctrl->flags |= ctrl_cfg->flags;
>>> +
>>> +               if (i <= STROBE_SOURCE)
>>> +                       v4l2_flash->ctrls[i] = ctrl;
>>> +       }
>>> +
>>> +       kfree(ctrl_init_data);
>>> +
>>> +       if (v4l2_flash->hdl.error) {
>>> +               ret = v4l2_flash->hdl.error;
>>> +               goto error_free_handler;
>>> +       }
>>> +
>>> +       v4l2_ctrl_handler_setup(&v4l2_flash->hdl);
>>> +
>>> +       v4l2_flash->sd.ctrl_handler = &v4l2_flash->hdl;
>>> +
>>> +       return 0;
>>> +
>>> +error_free_handler:
>>> +       v4l2_ctrl_handler_free(&v4l2_flash->hdl);
>>> +       return ret;
>>> +}
>>> +
>>> +static int __sync_device_with_v4l2_controls(struct v4l2_flash *v4l2_flash)
>>> +{
>>> +       struct led_classdev_flash *fled_cdev = v4l2_flash->fled_cdev;
>>> +       struct v4l2_ctrl **ctrls = v4l2_flash->ctrls;
>>> +       int ret = 0;
>>> +
>>> +       v4l2_flash_set_led_brightness(v4l2_flash, ctrls[TORCH_INTENSITY]);
>>> +
>>> +       if (ctrls[INDICATOR_INTENSITY])
>>> +               v4l2_flash_set_led_brightness(v4l2_flash,
>>> +                                               ctrls[INDICATOR_INTENSITY]);
>>> +
>>> +       if (ctrls[FLASH_TIMEOUT]) {
>>> +               ret = led_set_flash_timeout(fled_cdev,
>>> +                                       ctrls[FLASH_TIMEOUT]->val);
>>> +               if (ret < 0)
>>> +                       return ret;
>>> +       }
>>> +
>>> +       if (ctrls[FLASH_INTENSITY]) {
>>> +               ret = led_set_flash_brightness(fled_cdev,
>>> +                                       ctrls[FLASH_INTENSITY]->val);
>>> +               if (ret < 0)
>>> +                       return ret;
>>> +       }
>>> +
>>> +       /*
>>> +        * For some hardware arrangements setting strobe source may affect
>>> +        * torch mode. Synchronize strobe source setting only if not in torch
>>> +        * mode. For torch mode case it will get synchronized upon switching
>>> +        * to flash mode.
>>> +        */
>>> +       if (ctrls[STROBE_SOURCE] &&
>>> +           ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
>>> +               ret = call_flash_op(v4l2_flash, external_strobe_set,
>>> +                                       ctrls[STROBE_SOURCE]->val);
>>> +
>>> +       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 *fled_cdev = v4l2_flash->fled_cdev;
>>> +       struct led_classdev *led_cdev = &fled_cdev->led_cdev;
>>> +       struct led_classdev_flash *iled_cdev = v4l2_flash->iled_cdev;
>>> +       struct led_classdev *led_cdev_ind = NULL;
>>> +       int ret = 0;
>>> +
>>> +       if (!v4l2_fh_is_singular(&fh->vfh))
>>> +               return 0;
>>> +
>>> +       mutex_lock(&led_cdev->led_access);
>>> +
>>> +       led_sysfs_disable(led_cdev);
>>> +       led_trigger_remove(led_cdev);
>>> +
>>> +       mutex_unlock(&led_cdev->led_access);
>>> +
>>> +       if (iled_cdev) {
>>> +               led_cdev_ind = &iled_cdev->led_cdev;
>>> +
>>> +               mutex_lock(&led_cdev_ind->led_access);
>>> +
>>> +               led_sysfs_disable(led_cdev_ind);
>>> +               led_trigger_remove(led_cdev_ind);
>>> +
>>> +               mutex_unlock(&led_cdev_ind->led_access);
>>> +       }
>>> +
>>> +       ret = __sync_device_with_v4l2_controls(v4l2_flash);
>>> +       if (ret < 0)
>>> +               goto out_sync_device;
>>> +
>>> +       return 0;
>>> +out_sync_device:
>>> +       mutex_lock(&led_cdev->led_access);
>>> +       led_sysfs_enable(led_cdev);
>>> +       mutex_unlock(&led_cdev->led_access);
>>> +
>>> +       if (led_cdev_ind) {
>>> +               mutex_lock(&led_cdev_ind->led_access);
>>> +               led_sysfs_enable(led_cdev_ind);
>>> +               mutex_unlock(&led_cdev_ind->led_access);
>>> +       }
>>> +
>>> +       return ret;
>>> +}
>>> +
>>> +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 *fled_cdev = v4l2_flash->fled_cdev;
>>> +       struct led_classdev *led_cdev = &fled_cdev->led_cdev;
>>> +       struct led_classdev_flash *iled_cdev = v4l2_flash->iled_cdev;
>>> +       int ret = 0;
>>> +
>>> +       if (!v4l2_fh_is_singular(&fh->vfh))
>>> +               return 0;
>>> +
>>> +       mutex_lock(&led_cdev->led_access);
>>> +
>>> +       if (v4l2_flash->ctrls[STROBE_SOURCE])
>>> +               ret = v4l2_ctrl_s_ctrl(v4l2_flash->ctrls[STROBE_SOURCE],
>>> +                               V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
>>> +       led_sysfs_enable(led_cdev);
>>> +
>>> +       mutex_unlock(&led_cdev->led_access);
>>> +
>>> +       if (iled_cdev) {
>>> +               struct led_classdev *led_cdev_ind = &iled_cdev->led_cdev;
>>> +
>>> +               mutex_lock(&led_cdev_ind->led_access);
>>> +               led_sysfs_enable(led_cdev_ind);
>>> +               mutex_unlock(&led_cdev_ind->led_access);
>>> +       }
>>> +
>>> +       return ret;
>>> +}
>>> +
>>> +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,
>>> +};
>>> +
>>> +struct v4l2_flash *v4l2_flash_init(
>>> +       struct device *dev, struct device_node *of_node,
>>> +       struct led_classdev_flash *fled_cdev,
>>> +       struct led_classdev_flash *iled_cdev,
>>> +       const struct v4l2_flash_ops *ops,
>>> +       struct v4l2_flash_config *config)
>>> +{
>>> +       struct v4l2_flash *v4l2_flash;
>>> +       struct led_classdev *led_cdev = &fled_cdev->led_cdev;
>>> +       struct v4l2_subdev *sd;
>>> +       int ret;
>>> +
>>> +       if (!fled_cdev || !ops || !config)
>>> +               return ERR_PTR(-EINVAL);
>>> +
>>> +       v4l2_flash = devm_kzalloc(led_cdev->dev, sizeof(*v4l2_flash),
>>> +                                       GFP_KERNEL);
>>> +       if (!v4l2_flash)
>>> +               return ERR_PTR(-ENOMEM);
>>> +
>>> +       sd = &v4l2_flash->sd;
>>> +       v4l2_flash->fled_cdev = fled_cdev;
>>> +       v4l2_flash->iled_cdev = iled_cdev;
>>> +       v4l2_flash->ops = ops;
>>> +       sd->dev = dev;
>>> +       sd->of_node = of_node;
>>> +       v4l2_subdev_init(sd, &v4l2_flash_subdev_ops);
>>> +       sd->internal_ops = &v4l2_flash_subdev_internal_ops;
>>> +       sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>>> +       strlcpy(sd->name, config->dev_name, sizeof(sd->name));
>>> +
>>> +       ret = media_entity_init(&sd->entity, 0, NULL, 0);
>>> +       if (ret < 0)
>>> +               return ERR_PTR(ret);
>>> +
>>> +       sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
>>> +
>>> +       ret = v4l2_flash_init_controls(v4l2_flash, config);
>>> +       if (ret < 0)
>>> +               goto err_init_controls;
>>> +
>>> +       if (sd->of_node)
>>> +               of_node_get(sd->of_node);
>>> +       else
>>> +               of_node_get(led_cdev->dev->of_node);
>>> +
>>> +       ret = v4l2_async_register_subdev(sd);
>>> +       if (ret < 0)
>>> +               goto err_async_register_sd;
>>> +
>>> +       return v4l2_flash;
>>> +
>>> +err_async_register_sd:
>>> +       of_node_put(led_cdev->dev->of_node);
>>> +       v4l2_ctrl_handler_free(sd->ctrl_handler);
>>> +err_init_controls:
>>> +       media_entity_cleanup(&sd->entity);
>>> +
>>> +       return ERR_PTR(ret);
>>> +}
>>> +EXPORT_SYMBOL_GPL(v4l2_flash_init);
>>> +
>>> +void v4l2_flash_release(struct v4l2_flash *v4l2_flash)
>>> +{
>>> +       struct v4l2_subdev *sd;
>>> +       struct led_classdev *led_cdev;
>>> +
>>> +       if (IS_ERR_OR_NULL(v4l2_flash))
>>> +               return;
>>> +
>>> +       sd = &v4l2_flash->sd;
>>> +       led_cdev = &v4l2_flash->fled_cdev->led_cdev;
>>> +
>>> +       v4l2_async_unregister_subdev(sd);
>>> +
>>> +       if (sd->of_node)
>>> +               of_node_put(sd->of_node);
>>> +       else
>>> +               of_node_put(led_cdev->dev->of_node);
>>> +
>>> +       v4l2_ctrl_handler_free(sd->ctrl_handler);
>>> +       media_entity_cleanup(&sd->entity);
>>> +}
>>> +EXPORT_SYMBOL_GPL(v4l2_flash_release);
>>> +
>>> +MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
>>> +MODULE_DESCRIPTION("V4L2 Flash sub-device helpers");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/include/media/v4l2-flash-led-class.h b/include/media/v4l2-flash-led-class.h
>>> new file mode 100644
>>> index 0000000..098236c
>>> --- /dev/null
>>> +++ b/include/media/v4l2-flash-led-class.h
>>> @@ -0,0 +1,148 @@
>>> +/*
>>> + * V4L2 flash LED sub-device registration helpers.
>>> + *
>>> + *     Copyright (C) 2015 Samsung Electronics Co., Ltd
>>> + *     Author: Jacek Anaszewski <j.anaszewski@samsung.com>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License version 2 as
>>> + * published by the Free Software Foundation.
>>> + */
>>> +
>>> +#ifndef _V4L2_FLASH_H
>>> +#define _V4L2_FLASH_H
>>> +
>>> +#include <media/v4l2-ctrls.h>
>>> +#include <media/v4l2-subdev.h>
>>> +
>>> +struct led_classdev_flash;
>>> +struct led_classdev;
>>> +struct v4l2_flash;
>>> +enum led_brightness;
>>> +
>>> +/*
>>> + * struct v4l2_flash_ctrl_data - flash control initialization data, filled
>>> + *                             basing on the features declared by the LED flash
>>> + *                             class driver in the v4l2_flash_config
>>> + * @config:    initialization data for a control
>>> + * @cid:       contains v4l2 flash control id if the config
>>> + *             field was initialized, 0 otherwise
>>> + */
>>> +struct v4l2_flash_ctrl_data {
>>> +       struct v4l2_ctrl_config config;
>>> +       u32 cid;
>>> +};
>>> +
>>> +struct v4l2_flash_ops {
>>> +       /* setup strobing the flash by hardware pin state assertion */
>>> +       int (*external_strobe_set)(struct v4l2_flash *v4l2_flash,
>>> +                                       bool enable);
>>> +       /* convert intensity to brightness in a device specific manner */
>>> +       enum led_brightness (*intensity_to_led_brightness)
>>> +               (struct v4l2_flash *v4l2_flash, s32 intensity);
>>> +       /* convert brightness to intensity in a device specific manner */
>>> +       s32 (*led_brightness_to_intensity)
>>> +               (struct v4l2_flash *v4l2_flash, enum led_brightness);
>>> +};
>>> +
>>> +/**
>>> + * struct v4l2_flash_config - V4L2 Flash sub-device initialization data
>>> + * @dev_name:                  the name of the media entity,
>>> +                               unique in the system
>>> + * @torch_intensity:           constraints for the LED in torch mode
>>> + * @indicator_intensity:       constraints for the indicator LED
>>> + * @flash_faults:              bitmask of flash faults that the LED flash class
>>> +                               device can report; corresponding LED_FAULT* bit
>>> +                               definitions are available in the header file
>>> +                               <linux/led-class-flash.h>
>>> + * @has_external_strobe:       external strobe capability
>>> + */
>>> +struct v4l2_flash_config {
>>> +       char dev_name[32];
>>> +       struct led_flash_setting torch_intensity;
>>> +       struct led_flash_setting indicator_intensity;
>>> +       u32 flash_faults;
>>> +       unsigned int has_external_strobe:1;
>>> +};
>>> +
>>> +/**
>>> + * struct v4l2_flash - Flash sub-device context
>>> + * @fled_cdev:         LED flash class device controlled by this sub-device
>>> + * @iled_cdev:         LED class device representing indicator LED associated
>>> + *                     with the LED flash class device
>>> + * @ops:               V4L2 specific flash ops
>>> + * @sd:                        V4L2 sub-device
>>> + * @hdl:               flash controls handler
>>> + * @ctrls:             array of pointers to controls, whose values define
>>> + *                     the sub-device state
>>> + */
>>> +struct v4l2_flash {
>>> +       struct led_classdev_flash *fled_cdev;
>>> +       struct led_classdev_flash *iled_cdev;
>>> +       const struct v4l2_flash_ops *ops;
>>> +
>>> +       struct v4l2_subdev sd;
>>> +       struct v4l2_ctrl_handler hdl;
>>> +       struct v4l2_ctrl **ctrls;
>>> +};
>>> +
>>> +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);
>>> +}
>>> +
>>> +#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
>>> +/**
>>> + * v4l2_flash_init - initialize V4L2 flash led sub-device
>>> + * @dev:       flash device, e.g. an I2C device
>>> + * @of_node:   of_node of the LED, may be NULL if the same as device's
>>> + * @fled_cdev: LED flash class device to wrap
>>> + * @iled_cdev: LED flash class device representing indicator LED associated
>>> + *             with fled_cdev, may be NULL
>>> + * @flash_ops: V4L2 Flash device ops
>>> + * @config:    initialization data for V4L2 Flash sub-device
>>> + *
>>> + * Create V4L2 Flash sub-device wrapping given LED subsystem device.
>>> + *
>>> + * Returns: A valid pointer, or, when an error occurs, the return
>>> + * value is encoded using ERR_PTR(). Use IS_ERR() to check and
>>> + * PTR_ERR() to obtain the numeric return value.
>>> + */
>>> +struct v4l2_flash *v4l2_flash_init(
>>> +       struct device *dev, struct device_node *of_node,
>>> +       struct led_classdev_flash *fled_cdev,
>>> +       struct led_classdev_flash *iled_cdev,
>>> +       const struct v4l2_flash_ops *ops,
>>> +       struct v4l2_flash_config *config);
>>> +
>>> +/**
>>> + * v4l2_flash_release - release V4L2 Flash sub-device
>>> + * @flash: the V4L2 Flash sub-device to release
>>> + *
>>> + * Release V4L2 Flash sub-device.
>>> + */
>>> +void v4l2_flash_release(struct v4l2_flash *v4l2_flash);
>>> +
>>> +#else
>>> +static inline struct v4l2_flash *v4l2_flash_init(
>>> +       struct device *dev, struct device_node *of_node,
>>> +       struct led_classdev_flash *fled_cdev,
>>> +       struct led_classdev_flash *iled_cdev,
>>> +       const struct v4l2_flash_ops *ops,
>>> +       struct v4l2_flash_config *config)
>>> +{
>>> +       return NULL;
>>> +}
>>> +
>>> +static inline void v4l2_flash_release(struct v4l2_flash *v4l2_flash)
>>> +{
>>> +}
>>> +#endif /* CONFIG_V4L2_FLASH_LED_CLASS */
>>> +
>>> +#endif /* _V4L2_FLASH_H */
>>> --
>>> 1.7.9.5
>>>

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

* Re: [PATCH v10 6/8] exynos4-is: Improve the mechanism of async subdevs verification
  2015-06-08  9:02 ` [PATCH v10 6/8] exynos4-is: Improve the mechanism of async subdevs verification Jacek Anaszewski
@ 2015-06-10 18:17   ` Bryan Wu
  0 siblings, 0 replies; 24+ messages in thread
From: Bryan Wu @ 2015-06-10 18:17 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: Linux LED Subsystem, linux-media, Kyungmin Park, Pavel Machek,
	rpurdie, Sakari Ailus, Sylwester Nawrocki

On Mon, Jun 8, 2015 at 2:02 AM, Jacek Anaszewski
<j.anaszewski@samsung.com> wrote:
> Avoid verifying bound async sensor sub-devices by their of_nodes,
> which duplicates v4l2-async functionality, in favour of matching
> them by the corresponding struct v4l2_async_subdev. The structures
> are now being aggregated in the newly introduced struct fimc_async_subdevs
> which allows for categorizing async sub-devices by their type upon
> DT node parsing and recognizing the type easily when they're being bound.
>

Please go ahead with my Ack
Acked-by: Bryan Wu <cooloney@gmail.com>

Thanks,
-Bryan

> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> Cc: Sylwester Nawrocki <s.nawrocki@samsung.com>
> ---
>  drivers/media/platform/exynos4-is/media-dev.c |   34 +++++++++++++++++++------
>  drivers/media/platform/exynos4-is/media-dev.h |    8 ++++--
>  2 files changed, 32 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
> index 4f5586a..e3d7b70 100644
> --- a/drivers/media/platform/exynos4-is/media-dev.c
> +++ b/drivers/media/platform/exynos4-is/media-dev.c
> @@ -331,6 +331,8 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
>                                    unsigned int index)
>  {
>         struct fimc_source_info *pd = &fmd->sensor[index].pdata;
> +       struct v4l2_async_notifier *notifier = &fmd->subdev_notifier;
> +       struct v4l2_async_subdev *asd;
>         struct device_node *rem, *ep, *np;
>         struct v4l2_of_endpoint endpoint;
>
> @@ -387,9 +389,11 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd,
>         if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor)))
>                 return -EINVAL;
>
> -       fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_OF;
> -       fmd->sensor[index].asd.match.of.node = rem;
> -       fmd->async_subdevs[index] = &fmd->sensor[index].asd;
> +       asd = &fmd->async_subdevs.sensors[index];
> +       asd->match_type = V4L2_ASYNC_MATCH_OF;
> +       asd->match.of.node = rem;
> +       notifier->subdevs[notifier->num_subdevs++] = asd;
> +       fmd->sensor[index].asd = asd;
>
>         fmd->num_sensors++;
>
> @@ -1272,12 +1276,13 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
>                                  struct v4l2_async_subdev *asd)
>  {
>         struct fimc_md *fmd = notifier_to_fimc_md(notifier);
> +       struct fimc_async_subdevs *async_subdevs = &fmd->async_subdevs;
>         struct fimc_sensor_info *si = NULL;
>         int i;
>
>         /* Find platform data for this sensor subdev */
> -       for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++)
> -               if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node)
> +       for (i = 0; i < ARRAY_SIZE(async_subdevs->sensors); i++)
> +               if (fmd->sensor[i].asd == asd)
>                         si = &fmd->sensor[i];
>
>         if (si == NULL)
> @@ -1317,6 +1322,19 @@ unlock:
>         return ret;
>  }
>
> +static int fimc_md_register_async_entities(struct fimc_md *fmd)
> +{
> +       struct device *dev = fmd->media_dev.dev;
> +       struct v4l2_async_notifier *notifier = &fmd->subdev_notifier;
> +
> +       notifier->subdevs = devm_kcalloc(dev, FIMC_MAX_SENSORS,
> +                                       sizeof(*notifier->subdevs), GFP_KERNEL);
> +       if (!notifier->subdevs)
> +               return -ENOMEM;
> +
> +       return fimc_md_register_sensor_entities(fmd);
> +}
> +
>  static int fimc_md_probe(struct platform_device *pdev)
>  {
>         struct device *dev = &pdev->dev;
> @@ -1379,7 +1397,7 @@ static int fimc_md_probe(struct platform_device *pdev)
>                 goto err_clk;
>         }
>
> -       ret = fimc_md_register_sensor_entities(fmd);
> +       ret = fimc_md_register_async_entities(fmd);
>         if (ret) {
>                 mutex_unlock(&fmd->media_dev.graph_mutex);
>                 goto err_m_ent;
> @@ -1402,8 +1420,6 @@ static int fimc_md_probe(struct platform_device *pdev)
>         }
>
>         if (fmd->num_sensors > 0) {
> -               fmd->subdev_notifier.subdevs = fmd->async_subdevs;
> -               fmd->subdev_notifier.num_subdevs = fmd->num_sensors;
>                 fmd->subdev_notifier.bound = subdev_notifier_bound;
>                 fmd->subdev_notifier.complete = subdev_notifier_complete;
>                 fmd->num_sensors = 0;
> @@ -1412,6 +1428,8 @@ static int fimc_md_probe(struct platform_device *pdev)
>                                                 &fmd->subdev_notifier);
>                 if (ret)
>                         goto err_clk_p;
> +       } else {
> +               devm_kfree(dev, fmd->subdev_notifier.subdevs);
>         }
>
>         return 0;
> diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
> index 0321454..ff6d020 100644
> --- a/drivers/media/platform/exynos4-is/media-dev.h
> +++ b/drivers/media/platform/exynos4-is/media-dev.h
> @@ -88,11 +88,15 @@ struct fimc_camclk_info {
>   */
>  struct fimc_sensor_info {
>         struct fimc_source_info pdata;
> -       struct v4l2_async_subdev asd;
> +       struct v4l2_async_subdev *asd;
>         struct v4l2_subdev *subdev;
>         struct fimc_dev *host;
>  };
>
> +struct fimc_async_subdevs {
> +       struct v4l2_async_subdev sensors[FIMC_MAX_SENSORS];
> +};
> +
>  struct cam_clk {
>         struct clk_hw hw;
>         struct fimc_md *fmd;
> @@ -149,7 +153,7 @@ struct fimc_md {
>         } clk_provider;
>
>         struct v4l2_async_notifier subdev_notifier;
> -       struct v4l2_async_subdev *async_subdevs[FIMC_MAX_SENSORS];
> +       struct fimc_async_subdevs async_subdevs;
>
>         bool user_subdev_api;
>         spinlock_t slock;
> --
> 1.7.9.5
>

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

* Re: [PATCH v10 7/8] DT: Add documentation for exynos4-is 'flashes' property
  2015-06-08  9:02 ` [PATCH v10 7/8] DT: Add documentation for exynos4-is 'flashes' property Jacek Anaszewski
@ 2015-06-10 18:18   ` Bryan Wu
  0 siblings, 0 replies; 24+ messages in thread
From: Bryan Wu @ 2015-06-10 18:18 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: Linux LED Subsystem, linux-media, Kyungmin Park, Pavel Machek,
	rpurdie, Sakari Ailus, Sylwester Nawrocki, devicetree

On Mon, Jun 8, 2015 at 2:02 AM, Jacek Anaszewski
<j.anaszewski@samsung.com> wrote:
> This patch adds a description of 'samsung,camera-flashes'
> property to the samsung-fimc.txt.
>

Please go ahead with my Ack
Acked-by: Bryan Wu <cooloney@gmail.com>

Thanks,
-Bryan

> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> Cc: Sylwester Nawrocki <s.nawrocki@samsung.com>
> Cc: devicetree@vger.kernel.org
> ---
>  .../devicetree/bindings/media/samsung-fimc.txt     |   10 ++++++++++
>  1 file changed, 10 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt
> index 922d6f8..0554cad 100644
> --- a/Documentation/devicetree/bindings/media/samsung-fimc.txt
> +++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt
> @@ -40,6 +40,14 @@ should be inactive. For the "active-a" state the camera port A must be activated
>  and the port B deactivated and for the state "active-b" it should be the other
>  way around.
>
> +Optional properties:
> +
> +- samsung,camera-flashes - Array of pairs of phandles to the camera sensor
> +       devices and flash LEDs respectively. The pairs must reflect the board
> +       configuration, i.e. a sensor has to be able to strobe a flash LED by
> +       hardware. Flash LED is represented by a child node of a flash LED
> +       device (see Documentation/devicetree/bindings/leds/common.txt).
> +
>  The 'camera' node must include at least one 'fimc' child node.
>
>
> @@ -166,6 +174,8 @@ Example:
>                 clock-output-names = "cam_a_clkout", "cam_b_clkout";
>                 pinctrl-names = "default";
>                 pinctrl-0 = <&cam_port_a_clk_active>;
> +               samsung,camera-flashes = <&rear_camera &rear_flash>,
> +                                        <&front_camera &front_flash>;
>                 status = "okay";
>                 #address-cells = <1>;
>                 #size-cells = <1>;
> --
> 1.7.9.5
>

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

* Re: [PATCH v10 8/8] exynos4-is: Add support for v4l2-flash subdevs
  2015-06-08  9:02 ` [PATCH v10 8/8] exynos4-is: Add support for v4l2-flash subdevs Jacek Anaszewski
@ 2015-06-10 18:18   ` Bryan Wu
  0 siblings, 0 replies; 24+ messages in thread
From: Bryan Wu @ 2015-06-10 18:18 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: Linux LED Subsystem, linux-media, Kyungmin Park, Pavel Machek,
	rpurdie, Sakari Ailus, Sylwester Nawrocki

On Mon, Jun 8, 2015 at 2:02 AM, Jacek Anaszewski
<j.anaszewski@samsung.com> wrote:
> This patch adds support for external v4l2-flash devices.
> The support includes parsing "camera-flashes" DT property
> and asynchronous sub-device registration.
>

Please go ahead with my Ack
Acked-by: Bryan Wu <cooloney@gmail.com>

Thanks,
-Bryan

> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> Cc: Sylwester Nawrocki <s.nawrocki@samsung.com>
> ---
>  drivers/media/platform/exynos4-is/media-dev.c |   75 ++++++++++++++++++++++++-
>  drivers/media/platform/exynos4-is/media-dev.h |    4 ++
>  2 files changed, 77 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
> index e3d7b70..e387fd2 100644
> --- a/drivers/media/platform/exynos4-is/media-dev.c
> +++ b/drivers/media/platform/exynos4-is/media-dev.c
> @@ -455,6 +455,63 @@ rpm_put:
>         return ret;
>  }
>
> +static int fimc_md_register_flash_entities(struct fimc_md *fmd)
> +{
> +       struct device_node *parent = fmd->pdev->dev.of_node, *np_sensor,
> +               *np_flash;
> +       struct v4l2_async_notifier *notifier = &fmd->subdev_notifier;
> +       struct device *dev = &fmd->pdev->dev;
> +       struct v4l2_async_subdev *asd;
> +       int i, j, num_flashes = 0, num_elems;
> +
> +       num_elems = of_property_count_u32_elems(parent,
> +                                               "samsung,camera-flashes");
> +       /* samsung,camera-flashes property is optional */
> +       if (num_elems < 0)
> +               return 0;
> +
> +       /* samsung,camera-flashes array must have even number of elements */
> +       if ((num_elems & 1) || (num_elems > FIMC_MAX_SENSORS * 2))
> +               return -EINVAL;
> +
> +       for (i = 0; i < num_elems; i += 2) {
> +               /*
> +                * The pair of camera sensor and flash LED phandles reflects
> +                * the physical connection on the board, which allows for the
> +                * camera sensor to strobe the flash by raising a hardware pin.
> +                * This property just describes the association.
> +                */
> +               np_sensor = of_parse_phandle(parent,
> +                                            "samsung,camera-flashes", i);
> +
> +               for (j = 0; j < fmd->num_sensors; j++)
> +                       if (fmd->async_subdevs.sensors[j].match.of.node ==
> +                           np_sensor)
> +                               break;
> +
> +               of_node_put(np_sensor);
> +
> +               /*
> +                * If the camera sensor phandle isn't known to the media device
> +                * controller, then raise a warning only.
> +                */
> +               if (j == fmd->num_sensors)
> +                       dev_warn(dev, "sensor verification failed for a flash\n");
> +
> +               np_flash = of_parse_phandle(parent, "samsung,camera-flashes",
> +                                               i + 1);
> +
> +               asd = &fmd->async_subdevs.flashes[num_flashes++];
> +               asd->match_type = V4L2_ASYNC_MATCH_OF;
> +               asd->match.of.node = np_flash;
> +               notifier->subdevs[notifier->num_subdevs++] = asd;
> +
> +               of_node_put(np_flash);
> +       }
> +
> +       return 0;
> +}
> +
>  static int __of_get_csis_id(struct device_node *np)
>  {
>         u32 reg = 0;
> @@ -1280,6 +1337,15 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
>         struct fimc_sensor_info *si = NULL;
>         int i;
>
> +       /*
> +        * Flash sub-devices are controlled independently of ISP, and thus
> +        * verify only that the sub-device being matched was previously
> +        * registered.
> +        */
> +       for (i = 0; i < ARRAY_SIZE(async_subdevs->flashes); i++)
> +               if (&async_subdevs->flashes[i] == asd)
> +                       return 0;
> +
>         /* Find platform data for this sensor subdev */
>         for (i = 0; i < ARRAY_SIZE(async_subdevs->sensors); i++)
>                 if (fmd->sensor[i].asd == asd)
> @@ -1326,13 +1392,18 @@ static int fimc_md_register_async_entities(struct fimc_md *fmd)
>  {
>         struct device *dev = fmd->media_dev.dev;
>         struct v4l2_async_notifier *notifier = &fmd->subdev_notifier;
> +       int ret;
>
> -       notifier->subdevs = devm_kcalloc(dev, FIMC_MAX_SENSORS,
> +       notifier->subdevs = devm_kcalloc(dev, FIMC_MAX_ASYNC_ENTITIES,
>                                         sizeof(*notifier->subdevs), GFP_KERNEL);
>         if (!notifier->subdevs)
>                 return -ENOMEM;
>
> -       return fimc_md_register_sensor_entities(fmd);
> +       ret = fimc_md_register_sensor_entities(fmd);
> +       if (ret)
> +               return -EINVAL;
> +
> +       return fimc_md_register_flash_entities(fmd);
>  }
>
>  static int fimc_md_probe(struct platform_device *pdev)
> diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
> index ff6d020..be9205c 100644
> --- a/drivers/media/platform/exynos4-is/media-dev.h
> +++ b/drivers/media/platform/exynos4-is/media-dev.h
> @@ -33,7 +33,10 @@
>  #define PINCTRL_STATE_IDLE     "idle"
>
>  #define FIMC_MAX_SENSORS       4
> +#define FIMC_MAX_FLASHES       4
> +#define FIMC_MAX_ASYNC_ENTITIES        (FIMC_MAX_SENSORS + FIMC_MAX_FLASHES)
>  #define FIMC_MAX_CAMCLKS       2
> +
>  #define DEFAULT_SENSOR_CLK_FREQ        24000000U
>
>  /* LCD/ISP Writeback clocks (PIXELASYNCMx) */
> @@ -95,6 +98,7 @@ struct fimc_sensor_info {
>
>  struct fimc_async_subdevs {
>         struct v4l2_async_subdev sensors[FIMC_MAX_SENSORS];
> +       struct v4l2_async_subdev flashes[FIMC_MAX_FLASHES];
>  };
>
>  struct cam_clk {
> --
> 1.7.9.5
>

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

* Re: [PATCH v10 1/8] Documentation: leds: Add description of v4l2-flash sub-device
  2015-06-08  9:02 ` [PATCH v10 1/8] Documentation: leds: Add description of v4l2-flash sub-device Jacek Anaszewski
@ 2015-06-10 18:21   ` Bryan Wu
  0 siblings, 0 replies; 24+ messages in thread
From: Bryan Wu @ 2015-06-10 18:21 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: Linux LED Subsystem, linux-media, Kyungmin Park, Pavel Machek,
	rpurdie, Sakari Ailus, Sylwester Nawrocki

On Mon, Jun 8, 2015 at 2:02 AM, Jacek Anaszewski
<j.anaszewski@samsung.com> wrote:
> This patch extends LED Flash class documention by
> the description of interactions with v4l2-flash sub-device.
>

Merged into my -devel branch and it won't be merged into 4.2.0 merge
window but wait for one more cycle, since now it's quite late in 4.1.0
cycle.

Thanks,
-Bryan

> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> Cc: Richard Purdie <rpurdie@rpsys.net>
> ---
>  Documentation/leds/leds-class-flash.txt |   51 +++++++++++++++++++++++++++++++
>  1 file changed, 51 insertions(+)
>
> diff --git a/Documentation/leds/leds-class-flash.txt b/Documentation/leds/leds-class-flash.txt
> index 19bb673..8da3c6f 100644
> --- a/Documentation/leds/leds-class-flash.txt
> +++ b/Documentation/leds/leds-class-flash.txt
> @@ -20,3 +20,54 @@ Following sysfs attributes are exposed for controlling flash LED devices:
>         - max_flash_timeout
>         - flash_strobe
>         - flash_fault
> +
> +
> +V4L2 flash wrapper for flash LEDs
> +=================================
> +
> +A LED subsystem driver can be controlled also from the level of VideoForLinux2
> +subsystem. In order to enable this CONFIG_V4L2_FLASH_LED_CLASS symbol has to
> +be defined in the kernel config.
> +
> +The driver must call the v4l2_flash_init function to get registered in the
> +V4L2 subsystem. The function takes six arguments:
> +- dev       : flash device, e.g. an I2C device
> +- of_node   : of_node of the LED, may be NULL if the same as device's
> +- fled_cdev : LED flash class device to wrap
> +- iled_cdev : LED flash class device representing indicator LED associated with
> +             fled_cdev, may be NULL
> +- ops : V4L2 specific ops
> +       * external_strobe_set - defines the source of the flash LED strobe -
> +               V4L2_CID_FLASH_STROBE control or external source, typically
> +               a sensor, which makes it possible to synchronise the flash
> +               strobe start with exposure start,
> +       * intensity_to_led_brightness and led_brightness_to_intensity - perform
> +               enum led_brightness <-> V4L2 intensity conversion in a device
> +               specific manner - they can be used for devices with non-linear
> +               LED current scale.
> +- config : configuration for V4L2 Flash sub-device
> +       * dev_name - the name of the media entity, unique in the system,
> +       * flash_faults - bitmask of flash faults that the LED flash class
> +               device can report; corresponding LED_FAULT* bit definitions are
> +               available in <linux/led-class-flash.h>,
> +       * torch_intensity - constraints for the LED in TORCH mode
> +               in microamperes,
> +       * indicator_intensity - constraints for the indicator LED
> +               in microamperes,
> +       * has_external_strobe - determines whether the flash strobe source
> +               can be switched to external,
> +
> +On remove the v4l2_flash_release function has to be called, which takes one
> +argument - struct v4l2_flash pointer returned previously by v4l2_flash_init.
> +This function can be safely called with NULL or error pointer argument.
> +
> +Please refer to drivers/leds/leds-max77693.c for an exemplary usage of the
> +v4l2 flash wrapper.
> +
> +Once the V4L2 sub-device is registered by the driver which created the Media
> +controller device, the sub-device node acts just as a node of a native V4L2
> +flash API device would. The calls are simply routed to the LED flash API.
> +
> +Opening the V4L2 flash sub-device makes the LED subsystem sysfs interface
> +unavailable. The interface is re-enabled after the V4L2 flash sub-device
> +is closed.
> --
> 1.7.9.5
>

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

* Re: [PATCH v10 2/8] media: Add registration helpers for V4L2 flash sub-devices
  2015-06-10 18:12       ` Bryan Wu
@ 2015-06-10 18:29         ` Pavel Machek
  2015-06-10 21:34         ` Sakari Ailus
  1 sibling, 0 replies; 24+ messages in thread
From: Pavel Machek @ 2015-06-10 18:29 UTC (permalink / raw)
  To: Bryan Wu
  Cc: Jacek Anaszewski, Linux LED Subsystem, linux-media,
	Kyungmin Park, rpurdie, Sakari Ailus, Sylwester Nawrocki,
	Hans Verkuil

On Wed 2015-06-10 11:12:50, Bryan Wu wrote:
> On Wed, Jun 10, 2015 at 11:01 AM, Bryan Wu <cooloney@gmail.com> wrote:
> > On Wed, Jun 10, 2015 at 10:57 AM, Bryan Wu <cooloney@gmail.com> wrote:
> >> On Mon, Jun 8, 2015 at 2:02 AM, Jacek Anaszewski
> >> <j.anaszewski@samsung.com> wrote:
> >>> This patch adds helper functions for registering/unregistering
> >>> LED Flash class devices as V4L2 sub-devices. 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.
> >>>
> >>
> >> Please go ahead with my Ack
> >>
> >> Acked-by: Bryan Wu <cooloney@gmail.com>
> >>
> >
> > I found the rest of LED patches depend on this one. What about merging
> > this through my tree?
> >
> > -Bryan
> >
> >
> 
> Merged into my -devel branch and it won't be merged into 4.2.0 merge
> window but wait for one more cycle, since now it's quite late in 4.1.0
> cycle.

Actually... as this code will not be used (so it can't break
anything), it would make some sense to merge it to 4.2.

Thanks,
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [PATCH v10 2/8] media: Add registration helpers for V4L2 flash sub-devices
  2015-06-10 18:12       ` Bryan Wu
  2015-06-10 18:29         ` Pavel Machek
@ 2015-06-10 21:34         ` Sakari Ailus
  2015-06-10 21:39           ` Bryan Wu
  1 sibling, 1 reply; 24+ messages in thread
From: Sakari Ailus @ 2015-06-10 21:34 UTC (permalink / raw)
  To: Bryan Wu
  Cc: Jacek Anaszewski, Linux LED Subsystem, linux-media,
	Kyungmin Park, Pavel Machek, rpurdie, Sylwester Nawrocki,
	Hans Verkuil, mchehab

Hi Bryan,

On Wed, Jun 10, 2015 at 11:12:50AM -0700, Bryan Wu wrote:
> On Wed, Jun 10, 2015 at 11:01 AM, Bryan Wu <cooloney@gmail.com> wrote:
> > On Wed, Jun 10, 2015 at 10:57 AM, Bryan Wu <cooloney@gmail.com> wrote:
> >> On Mon, Jun 8, 2015 at 2:02 AM, Jacek Anaszewski
> >> <j.anaszewski@samsung.com> wrote:
> >>> This patch adds helper functions for registering/unregistering
> >>> LED Flash class devices as V4L2 sub-devices. 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.
> >>>
> >>
> >> Please go ahead with my Ack
> >>
> >> Acked-by: Bryan Wu <cooloney@gmail.com>
> >>
> >
> > I found the rest of LED patches depend on this one. What about merging
> > this through my tree?
> >
> > -Bryan
> >
> >
> 
> Merged into my -devel branch and it won't be merged into 4.2.0 merge
> window but wait for one more cycle, since now it's quite late in 4.1.0
> cycle.

Thanks!!

I briefly discussed this with Mauro (cc'd), this should be fine indeed.

-- 
Kind regards,

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

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

* Re: [PATCH v10 2/8] media: Add registration helpers for V4L2 flash sub-devices
  2015-06-10 21:34         ` Sakari Ailus
@ 2015-06-10 21:39           ` Bryan Wu
  2015-06-10 21:55             ` Sakari Ailus
  0 siblings, 1 reply; 24+ messages in thread
From: Bryan Wu @ 2015-06-10 21:39 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Jacek Anaszewski, Linux LED Subsystem, linux-media,
	Kyungmin Park, Pavel Machek, rpurdie, Sylwester Nawrocki,
	Hans Verkuil, mchehab

On Wed, Jun 10, 2015 at 2:34 PM, Sakari Ailus <sakari.ailus@iki.fi> wrote:
> Hi Bryan,
>
> On Wed, Jun 10, 2015 at 11:12:50AM -0700, Bryan Wu wrote:
>> On Wed, Jun 10, 2015 at 11:01 AM, Bryan Wu <cooloney@gmail.com> wrote:
>> > On Wed, Jun 10, 2015 at 10:57 AM, Bryan Wu <cooloney@gmail.com> wrote:
>> >> On Mon, Jun 8, 2015 at 2:02 AM, Jacek Anaszewski
>> >> <j.anaszewski@samsung.com> wrote:
>> >>> This patch adds helper functions for registering/unregistering
>> >>> LED Flash class devices as V4L2 sub-devices. 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.
>> >>>
>> >>
>> >> Please go ahead with my Ack
>> >>
>> >> Acked-by: Bryan Wu <cooloney@gmail.com>
>> >>
>> >
>> > I found the rest of LED patches depend on this one. What about merging
>> > this through my tree?
>> >
>> > -Bryan
>> >
>> >
>>
>> Merged into my -devel branch and it won't be merged into 4.2.0 merge
>> window but wait for one more cycle, since now it's quite late in 4.1.0
>> cycle.
>
> Thanks!!
>
> I briefly discussed this with Mauro (cc'd), this should be fine indeed.
>

I just got an email reporting a building error.

tree:   git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds.git
devel
head:   c7551d847a2336c299dff27b33ff48913fb11ee3
commit: badd9dba592c55f7a7b1f2b59ecdf0345ca56f01 [23/26] media: Add
registration helpers for V4L2 flash sub-devices
config: i386-allyesconfig (attached as .config)
reproduce:
  git checkout badd9dba592c55f7a7b1f2b59ecdf0345ca56f01
  # save the attached .config to linux build tree
  make ARCH=i386

All error/warnings (new ones prefixed by >>):

   drivers/media/v4l2-core/v4l2-flash-led-class.c: In function
'v4l2_flash_init':

drivers/media/v4l2-core/v4l2-flash-led-class.c:646:4: error: 'struct
v4l2_subdev' has no member named 'of_node'

     sd->of_node = of_node;
       ^
   drivers/media/v4l2-core/v4l2-flash-led-class.c:662:8: error:
'struct v4l2_subdev' has no member named 'of_node'
     if (sd->of_node)
           ^
   drivers/media/v4l2-core/v4l2-flash-led-class.c:663:17: error:
'struct v4l2_subdev' has no member named 'of_node'
      of_node_get(sd->of_node);
                    ^
   drivers/media/v4l2-core/v4l2-flash-led-class.c: In function
'v4l2_flash_release':
   drivers/media/v4l2-core/v4l2-flash-led-class.c:696:8: error:
'struct v4l2_subdev' has no member named 'of_node'
     if (sd->of_node)
           ^
   drivers/media/v4l2-core/v4l2-flash-led-class.c:697:17: error:
'struct v4l2_subdev' has no member named 'of_node'
      of_node_put(sd->of_node);
                    ^

vim +646 drivers/media/v4l2-core/v4l2-flash-led-class.c

   640
   641 sd = &v4l2_flash->sd;
   642 v4l2_flash->fled_cdev = fled_cdev;
   643 v4l2_flash->iled_cdev = iled_cdev;
   644 v4l2_flash->ops = ops;
   645 sd->dev = dev;
 > 646 sd->of_node = of_node;
   647 v4l2_subdev_init(sd, &v4l2_flash_subdev_ops);
   648 sd->internal_ops = &v4l2_flash_subdev_internal_ops;
   649 sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;

---
0-DAY kernel test infrastructure                Open Source Technology Center
http://lists.01.org/mailman/listinfo/kbuild                 Intel Corporation

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

* Re: [PATCH v10 2/8] media: Add registration helpers for V4L2 flash sub-devices
  2015-06-10 21:39           ` Bryan Wu
@ 2015-06-10 21:55             ` Sakari Ailus
  0 siblings, 0 replies; 24+ messages in thread
From: Sakari Ailus @ 2015-06-10 21:55 UTC (permalink / raw)
  To: Bryan Wu
  Cc: Jacek Anaszewski, Linux LED Subsystem, linux-media,
	Kyungmin Park, Pavel Machek, rpurdie, Sylwester Nawrocki,
	Hans Verkuil, mchehab

Hi Bryan,

On Wed, Jun 10, 2015 at 02:39:09PM -0700, Bryan Wu wrote:
> On Wed, Jun 10, 2015 at 2:34 PM, Sakari Ailus <sakari.ailus@iki.fi> wrote:
> > Hi Bryan,
> >
> > On Wed, Jun 10, 2015 at 11:12:50AM -0700, Bryan Wu wrote:
> >> On Wed, Jun 10, 2015 at 11:01 AM, Bryan Wu <cooloney@gmail.com> wrote:
> >> > On Wed, Jun 10, 2015 at 10:57 AM, Bryan Wu <cooloney@gmail.com> wrote:
> >> >> On Mon, Jun 8, 2015 at 2:02 AM, Jacek Anaszewski
> >> >> <j.anaszewski@samsung.com> wrote:
> >> >>> This patch adds helper functions for registering/unregistering
> >> >>> LED Flash class devices as V4L2 sub-devices. 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.
> >> >>>
> >> >>
> >> >> Please go ahead with my Ack
> >> >>
> >> >> Acked-by: Bryan Wu <cooloney@gmail.com>
> >> >>
> >> >
> >> > I found the rest of LED patches depend on this one. What about merging
> >> > this through my tree?
> >> >
> >> > -Bryan
> >> >
> >> >
> >>
> >> Merged into my -devel branch and it won't be merged into 4.2.0 merge
> >> window but wait for one more cycle, since now it's quite late in 4.1.0
> >> cycle.
> >
> > Thanks!!
> >
> > I briefly discussed this with Mauro (cc'd), this should be fine indeed.
> >
> 
> I just got an email reporting a building error.
> 
> tree:   git://git.kernel.org/pub/scm/linux/kernel/git/cooloney/linux-leds.git
> devel
> head:   c7551d847a2336c299dff27b33ff48913fb11ee3
> commit: badd9dba592c55f7a7b1f2b59ecdf0345ca56f01 [23/26] media: Add
> registration helpers for V4L2 flash sub-devices
> config: i386-allyesconfig (attached as .config)
> reproduce:
>   git checkout badd9dba592c55f7a7b1f2b59ecdf0345ca56f01
>   # save the attached .config to linux build tree
>   make ARCH=i386
> 
> All error/warnings (new ones prefixed by >>):
> 
>    drivers/media/v4l2-core/v4l2-flash-led-class.c: In function
> 'v4l2_flash_init':
> 
> drivers/media/v4l2-core/v4l2-flash-led-class.c:646:4: error: 'struct
> v4l2_subdev' has no member named 'of_node'
> 
>      sd->of_node = of_node;
>        ^
>    drivers/media/v4l2-core/v4l2-flash-led-class.c:662:8: error:
> 'struct v4l2_subdev' has no member named 'of_node'
>      if (sd->of_node)
>            ^
>    drivers/media/v4l2-core/v4l2-flash-led-class.c:663:17: error:
> 'struct v4l2_subdev' has no member named 'of_node'
>       of_node_get(sd->of_node);
>                     ^
>    drivers/media/v4l2-core/v4l2-flash-led-class.c: In function
> 'v4l2_flash_release':
>    drivers/media/v4l2-core/v4l2-flash-led-class.c:696:8: error:
> 'struct v4l2_subdev' has no member named 'of_node'
>      if (sd->of_node)
>            ^
>    drivers/media/v4l2-core/v4l2-flash-led-class.c:697:17: error:
> 'struct v4l2_subdev' has no member named 'of_node'
>       of_node_put(sd->of_node);
>                     ^
> 
> vim +646 drivers/media/v4l2-core/v4l2-flash-led-class.c

Oh dear. I briefly forgot the dependency to the patch Jacek also referred
to:

<URL:http://www.spinics.net/lists/linux-media/msg90846.html>

That's v1.2 I just sent in order to address Laurent's concern. I'd like to
get an ack from him on that though, but he probably won't be able to give
one until tomorrow evening Finnish time.

-- 
Kind regards,

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

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

end of thread, other threads:[~2015-06-10 21:56 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-06-08  9:02 [PATCH v10 0/8] LED / flash API integration Jacek Anaszewski
2015-06-08  9:02 ` [PATCH v10 1/8] Documentation: leds: Add description of v4l2-flash sub-device Jacek Anaszewski
2015-06-10 18:21   ` Bryan Wu
2015-06-08  9:02 ` [PATCH v10 2/8] media: Add registration helpers for V4L2 flash sub-devices Jacek Anaszewski
2015-06-10 17:57   ` Bryan Wu
2015-06-10 18:01     ` Bryan Wu
2015-06-10 18:12       ` Bryan Wu
2015-06-10 18:29         ` Pavel Machek
2015-06-10 21:34         ` Sakari Ailus
2015-06-10 21:39           ` Bryan Wu
2015-06-10 21:55             ` Sakari Ailus
2015-06-08  9:02 ` [PATCH v10 3/8] leds: max77693: add support for V4L2 Flash sub-device Jacek Anaszewski
2015-06-10 18:12   ` Bryan Wu
2015-06-08  9:02 ` [PATCH v10 4/8] DT: aat1290: Document handling external strobe sources Jacek Anaszewski
2015-06-10 18:12   ` Bryan Wu
2015-06-08  9:02 ` [PATCH v10 5/8] leds: aat1290: add support for V4L2 Flash sub-device Jacek Anaszewski
2015-06-10 18:12   ` Bryan Wu
2015-06-08  9:02 ` [PATCH v10 6/8] exynos4-is: Improve the mechanism of async subdevs verification Jacek Anaszewski
2015-06-10 18:17   ` Bryan Wu
2015-06-08  9:02 ` [PATCH v10 7/8] DT: Add documentation for exynos4-is 'flashes' property Jacek Anaszewski
2015-06-10 18:18   ` Bryan Wu
2015-06-08  9:02 ` [PATCH v10 8/8] exynos4-is: Add support for v4l2-flash subdevs Jacek Anaszewski
2015-06-10 18:18   ` Bryan Wu
2015-06-09  8:59 ` [PATCH v10 0/8] LED / flash API integration 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).