All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v1 00/11] LED / flash API integration
@ 2015-03-20 15:03 Jacek Anaszewski
  2015-03-20 15:03 ` [PATCH v1 01/11] leds: Add support for max77693 mfd flash cell Jacek Anaszewski
                   ` (9 more replies)
  0 siblings, 10 replies; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-20 15:03 UTC (permalink / raw)
  To: linux-leds, linux-media, devicetree
  Cc: kyungmin.park, pavel, cooloney, rpurdie, sakari.ailus,
	s.nawrocki, Jacek Anaszewski

This is a first non-RFC version of LED / flash API integration
series [1]. It is based on linux-next_20150320.

======================
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


Jacek Anaszewski (11):
  leds: Add support for max77693 mfd flash cell
  DT: Add documentation for the mfd Maxim max77693
  leds: Add driver for AAT1290 current regulator
  of: Add Skyworks Solutions, Inc. vendor prefix
  DT: Add documentation for the Skyworks AAT1290
  exynos4-is: Add support for v4l2-flash subdevs
  media: Add registration helpers for V4L2 flash sub-devices
  Documentation: leds: Add description of v4l2-flash sub-device
  DT: Add documentation for exynos4-is 'flashes' property
  leds: max77693: add support for V4L2 Flash sub-device
  leds: aat1290: add support for V4L2 Flash sub-device

 .../devicetree/bindings/leds/leds-aat1290.txt      |   70 ++
 .../devicetree/bindings/media/samsung-fimc.txt     |    8 +
 Documentation/devicetree/bindings/mfd/max77693.txt |   61 ++
 .../devicetree/bindings/vendor-prefixes.txt        |    1 +
 Documentation/leds/leds-class-flash.txt            |   13 +
 drivers/leds/Kconfig                               |   19 +
 drivers/leds/Makefile                              |    2 +
 drivers/leds/leds-aat1290.c                        |  564 ++++++++++
 drivers/leds/leds-max77693.c                       | 1136 ++++++++++++++++++++
 drivers/media/platform/exynos4-is/media-dev.c      |   36 +-
 drivers/media/platform/exynos4-is/media-dev.h      |   13 +-
 drivers/media/v4l2-core/Kconfig                    |   12 +
 drivers/media/v4l2-core/Makefile                   |    2 +
 drivers/media/v4l2-core/v4l2-flash.c               |  607 +++++++++++
 include/media/v4l2-flash.h                         |  145 +++
 15 files changed, 2686 insertions(+), 3 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-aat1290.txt
 create mode 100644 drivers/leds/leds-aat1290.c
 create mode 100644 drivers/leds/leds-max77693.c
 create mode 100644 drivers/media/v4l2-core/v4l2-flash.c
 create mode 100644 include/media/v4l2-flash.h

-- 
1.7.9.5

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

* [PATCH v1 01/11] leds: Add support for max77693 mfd flash cell
  2015-03-20 15:03 [PATCH v1 00/11] LED / flash API integration Jacek Anaszewski
@ 2015-03-20 15:03 ` Jacek Anaszewski
  2015-03-21 22:44   ` Sakari Ailus
  2015-03-20 15:03 ` [PATCH v1 02/11] DT: Add documentation for the mfd Maxim max77693 Jacek Anaszewski
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-20 15:03 UTC (permalink / raw)
  To: linux-leds, linux-media, devicetree
  Cc: kyungmin.park, pavel, cooloney, rpurdie, sakari.ailus,
	s.nawrocki, Jacek Anaszewski, Andrzej Hajda, Lee Jones,
	Chanwoo Choi

This patch adds led-flash support to Maxim max77693 chipset.
A device can be exposed to user space through LED subsystem
sysfs interface. Device supports up to two leds which can
work in flash and torch mode. The leds can be triggered
externally or by software.

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Bryan Wu <cooloney@gmail.com>
Cc: Richard Purdie <rpurdie@rpsys.net>
Cc: Lee Jones <lee.jones@linaro.org>
Cc: Chanwoo Choi <cw00.choi@samsung.com>
---
 drivers/leds/Kconfig         |   10 +
 drivers/leds/Makefile        |    1 +
 drivers/leds/leds-max77693.c | 1003 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1014 insertions(+)
 create mode 100644 drivers/leds/leds-max77693.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 966b960..f9fbeb5 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -467,6 +467,16 @@ config LEDS_TCA6507
 	  LED driver chips accessed via the I2C bus.
 	  Driver support brightness control and hardware-assisted blinking.
 
+config LEDS_MAX77693
+	tristate "LED support for MAX77693 Flash"
+	depends on LEDS_CLASS_FLASH
+	depends on MFD_MAX77693
+	depends on OF
+	help
+	  This option enables support for the flash part of the MAX77693
+	  multifunction device. It has build in control for two leds in flash
+	  and torch mode.
+
 config LEDS_MAX8997
 	tristate "LED support for MAX8997 PMIC"
 	depends on LEDS_CLASS && MFD_MAX8997
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index bf46093..9413fdb 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_LEDS_MC13783)		+= leds-mc13783.o
 obj-$(CONFIG_LEDS_NS2)			+= leds-ns2.o
 obj-$(CONFIG_LEDS_NETXBIG)		+= leds-netxbig.o
 obj-$(CONFIG_LEDS_ASIC3)		+= leds-asic3.o
+obj-$(CONFIG_LEDS_MAX77693)		+= leds-max77693.o
 obj-$(CONFIG_LEDS_MAX8997)		+= leds-max8997.o
 obj-$(CONFIG_LEDS_LM355x)		+= leds-lm355x.o
 obj-$(CONFIG_LEDS_BLINKM)		+= leds-blinkm.o
diff --git a/drivers/leds/leds-max77693.c b/drivers/leds/leds-max77693.c
new file mode 100644
index 0000000..7386d69
--- /dev/null
+++ b/drivers/leds/leds-max77693.c
@@ -0,0 +1,1003 @@
+/*
+ * LED Flash class driver for the flash cell of max77693 mfd.
+ *
+ *	Copyright (C) 2015, Samsung Electronics Co., Ltd.
+ *
+ *	Authors: Jacek Anaszewski <j.anaszewski@samsung.com>
+ *		 Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <asm/div64.h>
+#include <linux/led-class-flash.h>
+#include <linux/mfd/max77693.h>
+#include <linux/mfd/max77693-private.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#define MODE_OFF		0
+#define MODE_FLASH(a)		(1 << (a))
+#define MODE_TORCH(a)		(1 << (2 + (a)))
+#define MODE_FLASH_EXTERNAL(a)	(1 << (4 + (a)))
+
+#define MODE_FLASH_MASK		(MODE_FLASH(FLED1) | MODE_FLASH(FLED2) | \
+				 MODE_FLASH_EXTERNAL(FLED1) | \
+				 MODE_FLASH_EXTERNAL(FLED2))
+#define MODE_TORCH_MASK		(MODE_TORCH(FLED1) | MODE_TORCH(FLED2))
+
+#define FLED1_IOUT		(1 << 0)
+#define FLED2_IOUT		(1 << 1)
+
+#define MAX77693_LED_JOINT_NAME	"max77693-led"
+#define MAX77693_LED1_NAME	"max77693-led.0"
+#define MAX77693_LED2_NAME	"max77693-led.1"
+
+enum max77693_fled {
+	FLED1,
+	FLED2,
+};
+
+enum max77693_led_mode {
+	FLASH,
+	TORCH,
+};
+
+struct max77693_led_config_data {
+	const char *label[2];
+	u32 iout_torch_max[2];
+	u32 iout_flash_max[2];
+	u32 flash_timeout[2];
+	u32 num_leds;
+	u32 boost_mode;
+	u32 boost_vout;
+	u32 low_vsys;
+	u32 trigger_type;
+};
+
+struct max77693_sub_led {
+	/* related FLED output identifier */
+	int fled_id;
+	/* related LED Flash class device */
+	struct led_classdev_flash fled_cdev;
+	/* assures led-triggers compatibility */
+	struct work_struct work_brightness_set;
+
+	/* brightness cache */
+	unsigned int torch_brightness;
+	/* flash timeout cache */
+	unsigned int flash_timeout;
+	/* flash faults that may have occurred */
+	u32 flash_faults;
+};
+
+struct max77693_led_device {
+	/* parent mfd regmap */
+	struct regmap *regmap;
+	/* platform device data */
+	struct platform_device *pdev;
+	/* secures access to the device */
+	struct mutex lock;
+
+	/* sub led data */
+	struct max77693_sub_led sub_leds[2];
+
+	/* maximum torch current values for FLED outputs */
+	u32 iout_torch_max[2];
+	/* maximum flash current values for FLED outputs */
+	u32 iout_flash_max[2];
+	/* flash trigger type */
+	u32 trigger_type;
+
+	/* current flash timeout cache */
+	unsigned int current_flash_timeout;
+	/* ITORCH register cache */
+	u8 torch_iout_reg;
+	/* mode of fled outputs */
+	unsigned int mode_flags;
+	/* recently strobed fled */
+	int strobing_sub_led_id;
+	/* bitmask of fled outputs use state (bit 0. - FLED1, bit 1. - FLED2) */
+	u8 fled_mask;
+	/* FLED modes that can be set */
+	u8 allowed_modes;
+
+	/* arrangement of current outputs */
+	bool iout_joint;
+};
+
+struct max77693_led_settings {
+	struct led_flash_setting torch_brightness;
+	struct led_flash_setting flash_brightness;
+	struct led_flash_setting flash_timeout;
+};
+
+static u8 max77693_led_iout_to_reg(u32 ua)
+{
+	if (ua < FLASH_IOUT_MIN)
+		ua = FLASH_IOUT_MIN;
+	return (ua - FLASH_IOUT_MIN) / FLASH_IOUT_STEP;
+}
+
+static u8 max77693_flash_timeout_to_reg(u32 us)
+{
+	return (us - FLASH_TIMEOUT_MIN) / FLASH_TIMEOUT_STEP;
+}
+
+static inline struct max77693_sub_led *flcdev_to_sub_led(
+					struct led_classdev_flash *fled_cdev)
+{
+	return container_of(fled_cdev, struct max77693_sub_led, fled_cdev);
+}
+
+static inline struct max77693_led_device *sub_led_to_led(
+					struct max77693_sub_led *sub_led)
+{
+	return container_of(sub_led, struct max77693_led_device,
+				sub_leds[sub_led->fled_id]);
+}
+
+static inline u8 max77693_led_vsys_to_reg(u32 mv)
+{
+	return ((mv - MAX_FLASH1_VSYS_MIN) / MAX_FLASH1_VSYS_STEP) << 2;
+}
+
+static inline u8 max77693_led_vout_to_reg(u32 mv)
+{
+	return (mv - FLASH_VOUT_MIN) / FLASH_VOUT_STEP + FLASH_VOUT_RMIN;
+}
+
+static inline bool max77693_fled_used(struct max77693_led_device *led,
+					 int fled_id)
+{
+	u8 fled_bit = (fled_id == FLED1) ? FLED1_IOUT : FLED2_IOUT;
+
+	return led->fled_mask & fled_bit;
+}
+
+static int max77693_set_mode_reg(struct max77693_led_device *led, u8 mode)
+{
+	struct regmap *rmap = led->regmap;
+	int ret, v = 0, i;
+
+	for (i = FLED1; i <= FLED2; ++i) {
+		if (mode & MODE_TORCH(i))
+			v |= FLASH_EN_ON << TORCH_EN_SHIFT(i);
+
+		if (mode & MODE_FLASH(i)) {
+			v |= FLASH_EN_ON << FLASH_EN_SHIFT(i);
+		} else if (mode & MODE_FLASH_EXTERNAL(i)) {
+			v |= FLASH_EN_FLASH << FLASH_EN_SHIFT(i);
+			/*
+			 * Enable hw triggering also for torch mode, as some
+			 * camera sensors use torch led to fathom ambient light
+			 * conditions before strobing the flash.
+			 */
+			v |= FLASH_EN_TORCH << TORCH_EN_SHIFT(i);
+		}
+	}
+
+	/* Reset the register only prior setting flash modes */
+	if (mode & ~(MODE_TORCH(FLED1) | MODE_TORCH(FLED2))) {
+		ret = regmap_write(rmap, MAX77693_LED_REG_FLASH_EN, 0);
+		if (ret < 0)
+			return ret;
+	}
+
+	return regmap_write(rmap, MAX77693_LED_REG_FLASH_EN, v);
+}
+
+static int max77693_add_mode(struct max77693_led_device *led, u8 mode)
+{
+	int i, ret;
+
+	mode &= led->allowed_modes;
+
+	/*
+	 * Torch mode once enabled remains active until turned off. If the FLED2
+	 * output isn't to be disabled check if the torch mode to be set isn't
+	 * already activated and avoid re-setting it.
+	 */
+	if ((!(mode ^ led->mode_flags)) & MODE_TORCH(FLED2)) {
+		for (i = FLED1; i <= FLED2; ++i)
+			if ((mode & led->mode_flags & MODE_TORCH(i)))
+				return 0;
+	}
+
+	if (led->iout_joint)
+		/* Span the mode on FLED2 for joint iouts case */
+		mode |= (mode << 1);
+
+	/*
+	 * FLASH_EXTERNAL mode activates FLASHEN and TORCHEN pins in the device.
+	 * The related register bits fields interfere with SW triggerred modes,
+	 * thus clear them to ensure proper device configuration.
+	 */
+	for (i = FLED1; i <= FLED2; ++i)
+		if (mode & MODE_FLASH_EXTERNAL(i))
+			led->mode_flags &= (~MODE_TORCH(i) & ~MODE_FLASH(i));
+
+	led->mode_flags |= mode;
+	led->mode_flags &= led->allowed_modes;
+
+	ret = max77693_set_mode_reg(led, led->mode_flags);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Clear flash mode flag after setting the mode to avoid spurious flash
+	 * strobing on each subsequent torch mode setting.
+	 */
+	if (mode & MODE_FLASH_MASK)
+		led->mode_flags &= ~mode;
+
+	return ret;
+}
+
+static int max77693_clear_mode(struct max77693_led_device *led,
+				u8 mode)
+{
+	if (led->iout_joint)
+		/* Clear mode also on FLED2 for joint iouts case */
+		mode |= (mode << 1);
+
+	led->mode_flags &= ~mode;
+
+	return max77693_set_mode_reg(led, led->mode_flags);
+}
+
+static void max77693_add_allowed_modes(struct max77693_led_device *led,
+				int fled_id, enum max77693_led_mode mode)
+{
+	if (mode == FLASH)
+		led->allowed_modes |= (MODE_FLASH(fled_id) |
+				       MODE_FLASH_EXTERNAL(fled_id));
+	else
+		led->allowed_modes |= MODE_TORCH(fled_id);
+}
+
+static void max77693_distribute_currents(struct max77693_led_device *led,
+				int fled_id, enum max77693_led_mode mode,
+				u32 micro_amp, u32 iout_max[2], u32 iout[2])
+{
+	if (!led->iout_joint) {
+		iout[fled_id] = micro_amp;
+		max77693_add_allowed_modes(led, fled_id, mode);
+		return;
+	}
+
+	iout[FLED1] = min(micro_amp, iout_max[FLED1]);
+	iout[FLED2] = micro_amp - iout[FLED1];
+
+	if (mode == FLASH)
+		led->allowed_modes &= ~MODE_FLASH_MASK;
+	else
+		led->allowed_modes &= ~MODE_TORCH_MASK;
+
+	max77693_add_allowed_modes(led, FLED1, mode);
+
+	if (iout[FLED2])
+		max77693_add_allowed_modes(led, FLED2, mode);
+}
+
+static int max77693_set_torch_current(struct max77693_led_device *led,
+				int fled_id, u32 micro_amp)
+{
+	struct regmap *rmap = led->regmap;
+	u8 iout1_reg = 0, iout2_reg = 0;
+	u32 iout[2];
+
+	max77693_distribute_currents(led, fled_id, TORCH, micro_amp,
+					led->iout_torch_max, iout);
+
+	if (fled_id == FLED1 || led->iout_joint) {
+		iout1_reg = max77693_led_iout_to_reg(iout[FLED1]);
+		led->torch_iout_reg &= TORCH_IOUT_MASK(TORCH_IOUT2_SHIFT);
+	}
+	if (fled_id == FLED2 || led->iout_joint) {
+		iout2_reg = max77693_led_iout_to_reg(iout[FLED2]);
+		led->torch_iout_reg &= TORCH_IOUT_MASK(TORCH_IOUT1_SHIFT);
+	}
+
+	led->torch_iout_reg |= ((iout1_reg << TORCH_IOUT1_SHIFT) |
+				(iout2_reg << TORCH_IOUT2_SHIFT));
+
+	return regmap_write(rmap, MAX77693_LED_REG_ITORCH,
+						led->torch_iout_reg);
+}
+
+static int max77693_set_flash_current(struct max77693_led_device *led,
+					int fled_id,
+					u32 micro_amp)
+{
+	struct regmap *rmap = led->regmap;
+	u8 iout1_reg, iout2_reg;
+	u32 iout[2];
+	int ret = -EINVAL;
+
+	max77693_distribute_currents(led, fled_id, FLASH, micro_amp,
+					led->iout_flash_max, iout);
+
+	if (fled_id == FLED1 || led->iout_joint) {
+		iout1_reg = max77693_led_iout_to_reg(iout[FLED1]);
+		ret = regmap_write(rmap, MAX77693_LED_REG_IFLASH1,
+							iout1_reg);
+		if (ret < 0)
+			return ret;
+	}
+	if (fled_id == FLED2 || led->iout_joint) {
+		iout2_reg = max77693_led_iout_to_reg(iout[FLED2]);
+		ret = regmap_write(rmap, MAX77693_LED_REG_IFLASH2,
+							iout2_reg);
+	}
+
+	return ret;
+}
+
+static int max77693_set_timeout(struct max77693_led_device *led, u32 microsec)
+{
+	struct regmap *rmap = led->regmap;
+	u8 v;
+	int ret;
+
+	v = max77693_flash_timeout_to_reg(microsec);
+
+	if (led->trigger_type == MAX77693_LED_TRIG_TYPE_LEVEL)
+		v |= FLASH_TMR_LEVEL;
+
+	ret = regmap_write(rmap, MAX77693_LED_REG_FLASH_TIMER, v);
+	if (ret < 0)
+		return ret;
+
+	led->current_flash_timeout = microsec;
+
+	return 0;
+}
+
+static int max77693_get_strobe_status(struct max77693_led_device *led,
+					bool *state)
+{
+	struct regmap *rmap = led->regmap;
+	unsigned int v;
+	int ret;
+
+	ret = regmap_read(rmap, MAX77693_LED_REG_FLASH_STATUS, &v);
+	if (ret < 0)
+		return ret;
+
+	*state = v & FLASH_STATUS_FLASH_ON;
+
+	return ret;
+}
+
+static int max77693_get_flash_faults(struct max77693_sub_led *sub_led)
+{
+	struct max77693_led_device *led = sub_led_to_led(sub_led);
+	struct regmap *rmap = led->regmap;
+	unsigned int v;
+	u8 fault_open_mask, fault_short_mask;
+	int ret;
+
+	sub_led->flash_faults = 0;
+
+	if (led->iout_joint) {
+		fault_open_mask = FLASH_INT_FLED1_OPEN | FLASH_INT_FLED2_OPEN;
+		fault_short_mask = FLASH_INT_FLED1_SHORT |
+							FLASH_INT_FLED2_SHORT;
+	} else {
+		fault_open_mask = (sub_led->fled_id == FLED1) ?
+						FLASH_INT_FLED1_OPEN :
+						FLASH_INT_FLED2_OPEN;
+		fault_short_mask = (sub_led->fled_id == FLED1) ?
+						FLASH_INT_FLED1_SHORT :
+						FLASH_INT_FLED2_SHORT;
+	}
+
+	ret = regmap_read(rmap, MAX77693_LED_REG_FLASH_INT, &v);
+	if (ret < 0)
+		return ret;
+
+	if (v & fault_open_mask)
+		sub_led->flash_faults |= LED_FAULT_OVER_VOLTAGE;
+	if (v & fault_short_mask)
+		sub_led->flash_faults |= LED_FAULT_SHORT_CIRCUIT;
+	if (v & FLASH_INT_OVER_CURRENT)
+		sub_led->flash_faults |= LED_FAULT_OVER_CURRENT;
+
+	return 0;
+}
+
+static int max77693_setup(struct max77693_led_device *led,
+			 struct max77693_led_config_data *cfg)
+{
+	struct regmap *rmap = led->regmap;
+	int i, first_led, last_led, ret;
+	u32 max_flash_curr[2];
+	u8 v;
+
+	/*
+	 * Initialize only flash current. Torch current doesn't
+	 * require initialization as ITORCH register is written with
+	 * new value each time brightness_set op is called.
+	 */
+	if (led->iout_joint) {
+		first_led = FLED1;
+		last_led = FLED1;
+		max_flash_curr[FLED1] = cfg->iout_flash_max[FLED1] +
+					cfg->iout_flash_max[FLED2];
+	} else {
+		first_led = max77693_fled_used(led, FLED1) ? FLED1 : FLED2;
+		last_led = max77693_fled_used(led, FLED2) ? FLED2 : FLED1;
+		max_flash_curr[FLED1] = cfg->iout_flash_max[FLED1];
+		max_flash_curr[FLED2] = cfg->iout_flash_max[FLED2];
+	}
+
+	for (i = first_led; i <= last_led; ++i) {
+		ret = max77693_set_flash_current(led, i,
+					max_flash_curr[i]);
+		if (ret < 0)
+			return ret;
+	}
+
+	v = TORCH_TMR_NO_TIMER | MAX77693_LED_TRIG_TYPE_LEVEL;
+	ret = regmap_write(rmap, MAX77693_LED_REG_ITORCHTIMER, v);
+	if (ret < 0)
+		return ret;
+
+	if (cfg->low_vsys > 0)
+		v = max77693_led_vsys_to_reg(cfg->low_vsys) |
+						MAX_FLASH1_MAX_FL_EN;
+	else
+		v = 0;
+
+	ret = regmap_write(rmap, MAX77693_LED_REG_MAX_FLASH1, v);
+	if (ret < 0)
+		return ret;
+	ret = regmap_write(rmap, MAX77693_LED_REG_MAX_FLASH2, 0);
+	if (ret < 0)
+		return ret;
+
+	if (cfg->boost_mode == MAX77693_LED_BOOST_FIXED)
+		v = FLASH_BOOST_FIXED;
+	else
+		v = cfg->boost_mode | cfg->boost_mode << 1;
+
+	if (max77693_fled_used(led, FLED1) && max77693_fled_used(led, FLED2))
+		v |= FLASH_BOOST_LEDNUM_2;
+
+	ret = regmap_write(rmap, MAX77693_LED_REG_VOUT_CNTL, v);
+	if (ret < 0)
+		return ret;
+
+	v = max77693_led_vout_to_reg(cfg->boost_vout);
+	ret = regmap_write(rmap, MAX77693_LED_REG_VOUT_FLASH1, v);
+	if (ret < 0)
+		return ret;
+
+	return max77693_set_mode_reg(led, MODE_OFF);
+}
+
+static int __max77693_led_brightness_set(struct max77693_led_device *led,
+					int fled_id, enum led_brightness value)
+{
+	int ret;
+
+	mutex_lock(&led->lock);
+
+	if (value == 0) {
+		ret = max77693_clear_mode(led, MODE_TORCH(fled_id));
+		if (ret < 0)
+			dev_dbg(&led->pdev->dev,
+				"Failed to clear torch mode (%d)\n",
+				ret);
+		goto unlock;
+	}
+
+	ret = max77693_set_torch_current(led, fled_id, value * TORCH_IOUT_STEP);
+	if (ret < 0) {
+		dev_dbg(&led->pdev->dev,
+			"Failed to set torch current (%d)\n",
+			ret);
+		goto unlock;
+	}
+
+	ret = max77693_add_mode(led, MODE_TORCH(fled_id));
+	if (ret < 0)
+		dev_dbg(&led->pdev->dev,
+			"Failed to set torch mode (%d)\n",
+			ret);
+unlock:
+	mutex_unlock(&led->lock);
+	return ret;
+}
+
+static void max77693_led_brightness_set_work(
+					struct work_struct *work)
+{
+	struct max77693_sub_led *sub_led =
+			container_of(work, struct max77693_sub_led,
+					work_brightness_set);
+	struct max77693_led_device *led = sub_led_to_led(sub_led);
+
+	__max77693_led_brightness_set(led, sub_led->fled_id,
+				sub_led->torch_brightness);
+}
+
+/* LED subsystem callbacks */
+
+static int max77693_led_brightness_set_sync(
+				struct led_classdev *led_cdev,
+				enum led_brightness value)
+{
+	struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
+	struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev);
+	struct max77693_led_device *led = sub_led_to_led(sub_led);
+
+	return __max77693_led_brightness_set(led, sub_led->fled_id, value);
+}
+
+static void max77693_led_brightness_set(
+				struct led_classdev *led_cdev,
+				enum led_brightness value)
+{
+	struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
+	struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev);
+
+	sub_led->torch_brightness = value;
+	schedule_work(&sub_led->work_brightness_set);
+}
+
+static int max77693_led_flash_brightness_set(
+				struct led_classdev_flash *fled_cdev,
+				u32 brightness)
+{
+	struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev);
+	struct max77693_led_device *led = sub_led_to_led(sub_led);
+	int ret;
+
+	mutex_lock(&led->lock);
+	ret = max77693_set_flash_current(led, sub_led->fled_id, brightness);
+	mutex_unlock(&led->lock);
+
+	return ret;
+}
+
+static int max77693_led_flash_strobe_set(
+				struct led_classdev_flash *fled_cdev,
+				bool state)
+{
+	struct max77693_sub_led *sub_led = flcdev_to_sub_led(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 (!state) {
+		ret = max77693_clear_mode(led, MODE_FLASH(fled_id));
+		goto unlock;
+	}
+
+	if (sub_led->flash_timeout != led->current_flash_timeout) {
+		ret = max77693_set_timeout(led, sub_led->flash_timeout);
+		if (ret < 0)
+			goto unlock;
+	}
+
+	led->strobing_sub_led_id = fled_id;
+
+	ret = max77693_add_mode(led, MODE_FLASH(fled_id));
+	if (ret < 0)
+		goto unlock;
+
+	ret = max77693_get_flash_faults(sub_led);
+
+unlock:
+	mutex_unlock(&led->lock);
+	return ret;
+}
+
+static int max77693_led_flash_fault_get(
+				struct led_classdev_flash *fled_cdev,
+				u32 *fault)
+{
+	struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev);
+
+	*fault = sub_led->flash_faults;
+
+	return 0;
+}
+
+static int max77693_led_flash_strobe_get(
+				struct led_classdev_flash *fled_cdev,
+				bool *state)
+{
+	struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev);
+	struct max77693_led_device *led = sub_led_to_led(sub_led);
+	int ret;
+
+	if (!state)
+		return -EINVAL;
+
+	mutex_lock(&led->lock);
+
+	ret = max77693_get_strobe_status(led, state);
+
+	*state = !!(*state && (led->strobing_sub_led_id == sub_led->fled_id));
+
+	mutex_unlock(&led->lock);
+
+	return ret;
+}
+
+static int max77693_led_flash_timeout_set(
+				struct led_classdev_flash *fled_cdev,
+				u32 timeout)
+{
+	struct max77693_sub_led *sub_led = flcdev_to_sub_led(fled_cdev);
+	struct max77693_led_device *led = sub_led_to_led(sub_led);
+
+	mutex_lock(&led->lock);
+	sub_led->flash_timeout = timeout;
+	mutex_unlock(&led->lock);
+
+	return 0;
+}
+
+static int max77693_led_parse_dt(struct max77693_led_device *led,
+				struct max77693_led_config_data *cfg)
+{
+	struct device *dev = &led->pdev->dev;
+	struct max77693_sub_led *sub_leds = led->sub_leds;
+	struct device_node *node = dev->of_node, *child_node;
+	struct property *prop;
+	u32 led_sources[2];
+	int i, fled_id;
+
+	of_property_read_u32(node, "maxim,trigger-type", &cfg->trigger_type);
+	of_property_read_u32(node, "maxim,boost-mode", &cfg->boost_mode);
+	of_property_read_u32(node, "maxim,boost-mvout", &cfg->boost_vout);
+	of_property_read_u32(node, "maxim,mvsys-min", &cfg->low_vsys);
+
+	for_each_available_child_of_node(node, child_node) {
+		prop = of_find_property(child_node, "led-sources", NULL);
+		if (prop) {
+			const __be32 *srcs = NULL;
+
+			for (i = 0; i < ARRAY_SIZE(led_sources); ++i) {
+				srcs = of_prop_next_u32(prop, srcs,
+							&led_sources[i]);
+				if (!srcs)
+					break;
+			}
+		} else {
+			dev_err(dev,
+				"\"led-sources\" DT property not found.\n");
+			return -EINVAL;
+		}
+
+		if (i == 2) {
+			fled_id = FLED1;
+			led->fled_mask = FLED1_IOUT | FLED2_IOUT;
+		} else if (led_sources[0] == FLED1) {
+			fled_id = FLED1;
+			led->fled_mask |= FLED1_IOUT;
+		} else if (led_sources[0] == FLED2) {
+			fled_id = FLED2;
+			led->fled_mask |= FLED2_IOUT;
+		} else {
+			dev_err(dev,
+				"Wrong \"led-sources\" DT property value.\n");
+			return -EINVAL;
+		}
+
+		sub_leds[fled_id].fled_id = fled_id;
+
+		of_property_read_string(child_node, "label",
+					(const char **) &cfg->label[fled_id]);
+
+		of_property_read_u32(child_node, "max-microamp",
+						&cfg->iout_torch_max[fled_id]);
+		of_property_read_u32(child_node, "flash-max-microamp",
+						&cfg->iout_flash_max[fled_id]);
+		of_property_read_u32(child_node, "flash-timeout-us",
+						&cfg->flash_timeout[fled_id]);
+
+		if (++cfg->num_leds == 2 ||
+		    (max77693_fled_used(led, FLED1) &&
+		     max77693_fled_used(led, FLED2)))
+			break;
+	}
+
+	if (cfg->num_leds == 0) {
+		dev_err(dev, "No DT child node found for connected LED(s).\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void clamp_align(u32 *v, u32 min, u32 max, u32 step)
+{
+	*v = clamp_val(*v, min, max);
+	if (step > 1)
+		*v = (*v - min) / step * step + min;
+}
+
+static void max77693_led_validate_configuration(struct max77693_led_device *led,
+					struct max77693_led_config_data *cfg)
+{
+	int i;
+
+	if (cfg->num_leds == 1 &&
+	    max77693_fled_used(led, FLED1) && max77693_fled_used(led, FLED2))
+		led->iout_joint = true;
+
+	cfg->boost_mode = clamp_val(cfg->boost_mode, MAX77693_LED_BOOST_NONE,
+			    MAX77693_LED_BOOST_FIXED);
+
+	/* Boost must be enabled if both current outputs are used */
+	if ((cfg->boost_mode == MAX77693_LED_BOOST_NONE) && led->iout_joint)
+		cfg->boost_mode = MAX77693_LED_BOOST_FIXED;
+
+	/* Split max current settings to both outputs in case of joint leds */
+	if (led->iout_joint) {
+		cfg->iout_torch_max[FLED1] /= 2;
+		cfg->iout_torch_max[FLED2] = cfg->iout_torch_max[FLED1];
+		cfg->iout_flash_max[FLED1] /= 2;
+		cfg->iout_flash_max[FLED2] = cfg->iout_flash_max[FLED1];
+	}
+
+	for (i = FLED1; i <= FLED2; ++i) {
+		if (max77693_fled_used(led, i)) {
+			clamp_align(&cfg->iout_torch_max[i], TORCH_IOUT_MIN,
+					TORCH_IOUT_MAX, TORCH_IOUT_STEP);
+			clamp_align(&cfg->iout_flash_max[i], FLASH_IOUT_MIN,
+					cfg->boost_mode ? FLASH_IOUT_MAX_2LEDS :
+							FLASH_IOUT_MAX_1LED,
+					FLASH_IOUT_STEP);
+		} else {
+			cfg->iout_torch_max[i] = cfg->iout_flash_max[i] = 0;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(cfg->flash_timeout); ++i)
+		clamp_align(&cfg->flash_timeout[i], FLASH_TIMEOUT_MIN,
+				FLASH_TIMEOUT_MAX, FLASH_TIMEOUT_STEP);
+
+	cfg->trigger_type = clamp_val(cfg->trigger_type,
+					MAX77693_LED_TRIG_TYPE_EDGE,
+					MAX77693_LED_TRIG_TYPE_LEVEL);
+
+	clamp_align(&cfg->boost_vout, FLASH_VOUT_MIN, FLASH_VOUT_MAX,
+							FLASH_VOUT_STEP);
+
+	if (cfg->low_vsys)
+		clamp_align(&cfg->low_vsys, MAX_FLASH1_VSYS_MIN,
+				MAX_FLASH1_VSYS_MAX, MAX_FLASH1_VSYS_STEP);
+}
+
+static int max77693_led_get_configuration(struct max77693_led_device *led,
+				struct max77693_led_config_data *cfg)
+{
+	int ret;
+
+	ret = max77693_led_parse_dt(led, cfg);
+	if (ret < 0)
+		return ret;
+
+	max77693_led_validate_configuration(led, cfg);
+
+	memcpy(led->iout_torch_max, cfg->iout_torch_max,
+				sizeof(led->iout_torch_max));
+	memcpy(led->iout_flash_max, cfg->iout_flash_max,
+				sizeof(led->iout_flash_max));
+	led->trigger_type = cfg->trigger_type;
+
+	return 0;
+}
+
+static const struct led_flash_ops flash_ops = {
+	.flash_brightness_set	= max77693_led_flash_brightness_set,
+	.strobe_set		= max77693_led_flash_strobe_set,
+	.strobe_get		= max77693_led_flash_strobe_get,
+	.timeout_set		= max77693_led_flash_timeout_set,
+	.fault_get		= max77693_led_flash_fault_get,
+};
+
+static void max77693_init_flash_settings(struct max77693_led_device *led,
+					 int fled_id,
+					 struct max77693_led_config_data *cfg,
+					 struct max77693_led_settings *s)
+{
+	struct led_flash_setting *setting;
+
+	/* Init torch intensity setting */
+	setting = &s->torch_brightness;
+	setting->min = TORCH_IOUT_MIN;
+	setting->max = cfg->iout_torch_max[fled_id];
+	setting->max = led->iout_joint ?
+		cfg->iout_torch_max[FLED1] + cfg->iout_torch_max[FLED2] :
+		cfg->iout_torch_max[fled_id];
+	setting->step = TORCH_IOUT_STEP;
+	setting->val = setting->max;
+
+	/* Init flash intensity setting */
+	setting = &s->flash_brightness;
+	setting->min = FLASH_IOUT_MIN;
+	setting->max = led->iout_joint ?
+		cfg->iout_flash_max[FLED1] + cfg->iout_flash_max[FLED2] :
+		cfg->iout_flash_max[fled_id];
+	setting->step = FLASH_IOUT_STEP;
+	setting->val = setting->max;
+
+	/* Init flash timeout setting */
+	setting = &s->flash_timeout;
+	setting->min = FLASH_TIMEOUT_MIN;
+	setting->max = cfg->flash_timeout[fled_id];
+	setting->step = FLASH_TIMEOUT_STEP;
+	setting->val = setting->max;
+}
+
+static const char *max77693_get_led_name(struct max77693_led_device *led,
+					 int fled_id)
+{
+	if (led->iout_joint)
+		return MAX77693_LED_JOINT_NAME;
+	else
+		return  (fled_id == FLED1) ? MAX77693_LED1_NAME :
+					     MAX77693_LED2_NAME;
+}
+
+static void max77693_init_fled_cdev(struct max77693_led_device *led,
+				int fled_id,
+				struct max77693_led_config_data *cfg)
+{
+	struct led_classdev_flash *fled_cdev;
+	struct led_classdev *led_cdev;
+	struct max77693_sub_led *sub_led = &led->sub_leds[fled_id];
+	struct max77693_led_settings settings;
+
+	/* Initialize flash settings */
+	max77693_init_flash_settings(led, fled_id, cfg, &settings);
+
+	/* Initialize LED Flash class device */
+	fled_cdev = &sub_led->fled_cdev;
+	fled_cdev->ops = &flash_ops;
+	led_cdev = &fled_cdev->led_cdev;
+
+	if (cfg->label[fled_id])
+		led_cdev->name = cfg->label[fled_id];
+	else
+		led_cdev->name = max77693_get_led_name(led, fled_id),
+
+	led_cdev->brightness_set = max77693_led_brightness_set;
+	led_cdev->brightness_set_sync = max77693_led_brightness_set_sync;
+	INIT_WORK(&sub_led->work_brightness_set,
+			max77693_led_brightness_set_work);
+
+	led_cdev->max_brightness = settings.torch_brightness.val /
+					TORCH_IOUT_STEP;
+	led_cdev->flags |= LED_DEV_CAP_FLASH;
+
+	fled_cdev->brightness = settings.flash_brightness;
+	fled_cdev->timeout = settings.flash_timeout;
+	sub_led->flash_timeout = fled_cdev->timeout.val;
+}
+
+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 max77693_led_config_data cfg = {};
+	int init_fled_cdev[2], i, ret;
+
+	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	led->pdev = pdev;
+	led->regmap = iodev->regmap;
+	led->allowed_modes = MODE_FLASH_MASK;
+	sub_leds = led->sub_leds;
+
+	platform_set_drvdata(pdev, led);
+	ret = max77693_led_get_configuration(led, &cfg);
+	if (ret < 0)
+		return ret;
+
+	ret = max77693_setup(led, &cfg);
+	if (ret < 0)
+		return ret;
+
+	init_fled_cdev[FLED1] =
+			led->iout_joint || max77693_fled_used(led, FLED1);
+	init_fled_cdev[FLED2] =
+			!led->iout_joint && max77693_fled_used(led, FLED2);
+
+	/* Initialize LED Flash class device(s) */
+	for (i = FLED1; i <= FLED2; ++i)
+		if (init_fled_cdev[i])
+			max77693_init_fled_cdev(led, i, &cfg);
+	mutex_init(&led->lock);
+
+	/* Register LED Flash class device(s) */
+	for (i = FLED1; i <= FLED2; ++i) {
+		if (!init_fled_cdev[i])
+			continue;
+
+		ret = led_classdev_flash_register(dev, &sub_leds[i].fled_cdev);
+		if (ret < 0) {
+			/*
+			 * At this moment FLED1 might have been already
+			 * registered and it needs to be released.
+			 */
+			if (i == FLED2)
+				goto err_register_led2;
+			else
+				goto err_register_led1;
+		}
+	}
+
+	return 0;
+
+err_register_led2:
+	/* It is possible than only FLED2 was to be registered */
+	if (!init_fled_cdev[FLED1])
+		goto err_register_led1;
+	led_classdev_flash_unregister(&sub_leds[FLED1].fled_cdev);
+err_register_led1:
+	mutex_destroy(&led->lock);
+
+	return ret;
+}
+
+static int max77693_led_remove(struct platform_device *pdev)
+{
+	struct max77693_led_device *led = platform_get_drvdata(pdev);
+	struct max77693_sub_led *sub_leds = led->sub_leds;
+
+	if (led->iout_joint || max77693_fled_used(led, FLED1)) {
+		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)) {
+		led_classdev_flash_unregister(&sub_leds[FLED2].fled_cdev);
+		cancel_work_sync(&sub_leds[FLED2].work_brightness_set);
+	}
+
+	mutex_destroy(&led->lock);
+
+	return 0;
+}
+
+static const struct of_device_id max77693_led_dt_match[] = {
+	{.compatible = "maxim,max77693-led"},
+	{},
+};
+
+static struct platform_driver max77693_led_driver = {
+	.probe		= max77693_led_probe,
+	.remove		= max77693_led_remove,
+	.driver		= {
+		.name	= "max77693-led",
+		.owner	= THIS_MODULE,
+		.of_match_table = max77693_led_dt_match,
+	},
+};
+
+module_platform_driver(max77693_led_driver);
+
+MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
+MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
+MODULE_DESCRIPTION("Maxim MAX77693 led flash driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.9.5

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

* [PATCH v1 02/11] DT: Add documentation for the mfd Maxim max77693
  2015-03-20 15:03 [PATCH v1 00/11] LED / flash API integration Jacek Anaszewski
  2015-03-20 15:03 ` [PATCH v1 01/11] leds: Add support for max77693 mfd flash cell Jacek Anaszewski
@ 2015-03-20 15:03 ` Jacek Anaszewski
  2015-03-21 22:49   ` Sakari Ailus
  2015-03-26 14:06   ` Lee Jones
  2015-03-20 15:03 ` [PATCH v1 03/11] leds: Add driver for AAT1290 current regulator Jacek Anaszewski
                   ` (7 subsequent siblings)
  9 siblings, 2 replies; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-20 15:03 UTC (permalink / raw)
  To: linux-leds, linux-media, devicetree
  Cc: kyungmin.park, pavel, cooloney, rpurdie, sakari.ailus,
	s.nawrocki, Jacek Anaszewski, Andrzej Hajda, Lee Jones,
	Chanwoo Choi

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

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Lee Jones <lee.jones@linaro.org>
Cc: Chanwoo Choi <cw00.choi@samsung.com>
Cc: Bryan Wu <cooloney@gmail.com>
Cc: Richard Purdie <rpurdie@rpsys.net>
---
 Documentation/devicetree/bindings/mfd/max77693.txt |   61 ++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/Documentation/devicetree/bindings/mfd/max77693.txt b/Documentation/devicetree/bindings/mfd/max77693.txt
index 38e6440..15c546e 100644
--- a/Documentation/devicetree/bindings/mfd/max77693.txt
+++ b/Documentation/devicetree/bindings/mfd/max77693.txt
@@ -76,7 +76,53 @@ Optional properties:
     Valid values: 4300000, 4700000, 4800000, 4900000
     Default: 4300000
 
+- led : the LED submodule device node
+
+There are two LED outputs available - FLED1 and FLED2. Each of them can
+control a separate LED or they can be connected together to double
+the maximum current for a single connected LED. One LED is represented
+by one child node.
+
+Required properties:
+- compatible : Must be "maxim,max77693-led".
+
+Optional properties:
+- maxim,trigger-type : Flash trigger type.
+	Possible trigger types:
+		LEDS_TRIG_TYPE_EDGE (0) - Rising edge of the signal triggers
+			the flash,
+		LEDS_TRIG_TYPE_LEVEL (1) - Strobe pulse length controls duration
+			of the flash.
+- maxim,boost-mode :
+	In boost mode the device can produce up to 1.2A of total current
+	on both outputs. The maximum current on each output is reduced
+	to 625mA then. If not enabled explicitly, boost setting defaults to
+	LEDS_BOOST_FIXED in case both current sources are used.
+	Possible values:
+		LEDS_BOOST_OFF (0) - no boost,
+		LEDS_BOOST_ADAPTIVE (1) - adaptive mode,
+		LEDS_BOOST_FIXED (2) - fixed mode.
+- maxim,boost-mvout : Output voltage of the boost module in millivolts.
+- maxim,mvsys-min : Low input voltage level in millivolts. Flash is not fired
+	if chip estimates that system voltage could drop below this level due
+	to flash power consumption.
+
+Required properties of the LED child node:
+- led-sources : see Documentation/devicetree/bindings/leds/common.txt;
+		device current output identifiers: 0 - FLED1, 1 - FLED2
+
+Optional properties of the LED child node:
+- label : see Documentation/devicetree/bindings/leds/common.txt
+- max-microamp : see Documentation/devicetree/bindings/leds/common.txt
+		Range: 15625 - 250000
+- flash-max-microamp : see Documentation/devicetree/bindings/leds/common.txt
+		Range: 15625 - 1000000
+- flash-timeout-us : see Documentation/devicetree/bindings/leds/common.txt
+		Range: 62500 - 1000000
+
 Example:
+#include <dt-bindings/leds/common.h>
+
 	max77693@66 {
 		compatible = "maxim,max77693";
 		reg = <0x66>;
@@ -117,5 +163,20 @@ Example:
 			maxim,thermal-regulation-celsius = <75>;
 			maxim,battery-overcurrent-microamp = <3000000>;
 			maxim,charge-input-threshold-microvolt = <4300000>;
+
+		led {
+			compatible = "maxim,max77693-led";
+			maxim,trigger-type = <LEDS_TRIG_TYPE_LEVEL>;
+			maxim,boost-mode = <LEDS_BOOST_FIXED>;
+			maxim,boost-mvout = <5000>;
+			maxim,mvsys-min = <2400>;
+
+			camera_flash: flash-led {
+				label = "max77693-flash";
+				led-sources = <0>, <1>;
+				max-microamp = <500000>;
+				flash-max-microamp = <1250000>;
+				flash-timeout-us = <1000000>;
+			};
 		};
 	};
-- 
1.7.9.5

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

* [PATCH v1 03/11] leds: Add driver for AAT1290 current regulator
  2015-03-20 15:03 [PATCH v1 00/11] LED / flash API integration Jacek Anaszewski
  2015-03-20 15:03 ` [PATCH v1 01/11] leds: Add support for max77693 mfd flash cell Jacek Anaszewski
  2015-03-20 15:03 ` [PATCH v1 02/11] DT: Add documentation for the mfd Maxim max77693 Jacek Anaszewski
@ 2015-03-20 15:03 ` Jacek Anaszewski
  2015-03-21 22:55   ` Sakari Ailus
  2015-03-20 15:03 ` [PATCH v1 04/11] of: Add Skyworks Solutions, Inc. vendor prefix Jacek Anaszewski
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-20 15:03 UTC (permalink / raw)
  To: linux-leds, linux-media, devicetree
  Cc: kyungmin.park, pavel, cooloney, rpurdie, sakari.ailus,
	s.nawrocki, Jacek Anaszewski

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

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Bryan Wu <cooloney@gmail.com>
Cc: Richard Purdie <rpurdie@rpsys.net>
---
 drivers/leds/Kconfig        |    8 +
 drivers/leds/Makefile       |    1 +
 drivers/leds/leds-aat1290.c |  382 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 391 insertions(+)
 create mode 100644 drivers/leds/leds-aat1290.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index f9fbeb5..c3b5b027 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -42,6 +42,14 @@ config LEDS_88PM860X
 	  This option enables support for on-chip LED drivers found on Marvell
 	  Semiconductor 88PM8606 PMIC.
 
+config LEDS_AAT1290
+	tristate "LED support for the AAT1290"
+	depends on LEDS_CLASS_FLASH
+	depends on GPIOLIB
+	depends on OF
+	help
+	 This option enables support for the LEDs on the AAT1290.
+
 config LEDS_LM3530
 	tristate "LCD Backlight driver for LM3530"
 	depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 9413fdb..0b3fd0e 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_LEDS_TRIGGERS)		+= led-triggers.o
 
 # LED Platform Drivers
 obj-$(CONFIG_LEDS_88PM860X)		+= leds-88pm860x.o
+obj-$(CONFIG_LEDS_AAT1290)		+= leds-aat1290.o
 obj-$(CONFIG_LEDS_BD2802)		+= leds-bd2802.o
 obj-$(CONFIG_LEDS_LOCOMO)		+= leds-locomo.o
 obj-$(CONFIG_LEDS_LM3530)		+= leds-lm3530.o
diff --git a/drivers/leds/leds-aat1290.c b/drivers/leds/leds-aat1290.c
new file mode 100644
index 0000000..456f9a9d
--- /dev/null
+++ b/drivers/leds/leds-aat1290.c
@@ -0,0 +1,382 @@
+/*
+ *	LED Flash class driver for the AAT1290
+ *	1.5A Step-Up Current Regulator for Flash LEDs
+ *
+ *	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/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/led-class-flash.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#define AAT1290_MOVIE_MODE_CURRENT_ADDR	17
+#define AAT1290_MAX_MM_CURR_PERCENT_0	16
+#define AAT1290_MAX_MM_CURR_PERCENT_100	1
+
+#define AAT1290_FLASH_SAFETY_TIMER_ADDR	18
+
+#define AAT1290_MOVIE_MODE_CONFIG_ADDR	19
+#define AAT1290_MOVIE_MODE_OFF		1
+#define AAT1290_MOVIE_MODE_ON		3
+
+#define AAT1290_MM_CURRENT_RATIO_ADDR	20
+#define AAT1290_MM_TO_FL_1_92		1
+
+#define AAT1290_MM_TO_FL_RATIO		1000 / 1920
+#define AAT1290_MAX_MM_CURRENT(fl_max)	(fl_max * AAT1290_MM_TO_FL_RATIO)
+
+#define AAT1290_LATCH_TIME_MIN_US	500
+#define AAT1290_LATCH_TIME_MAX_US	1000
+#define AAT1290_EN_SET_TICK_TIME_US	1
+#define AAT1290_FLEN_OFF_DELAY_TIME_US	10
+#define AAT1290_FLASH_TM_NUM_LEVELS	16
+#define AAT1290_MM_CURRENT_SCALE_SIZE	15
+
+#define AAT1290_NAME			"aat1290"
+
+
+struct aat1290_led_settings {
+	struct led_flash_setting torch_brightness;
+	struct led_flash_setting flash_timeout;
+};
+
+struct aat1290_led {
+	/* platform device data */
+	struct platform_device *pdev;
+	/* secures access to the device */
+	struct mutex lock;
+
+	/* related LED Flash class device */
+	struct led_classdev_flash fled_cdev;
+
+	/* FLEN pin */
+	struct gpio_desc *gpio_fl_en;
+	/* EN|SET pin  */
+	struct gpio_desc *gpio_en_set;
+
+	/* maximum flash timeout */
+	u32 max_flash_tm;
+	/* maximum LED current in flash mode */
+	u32 max_flash_current;
+	/* device mode */
+	bool movie_mode;
+
+	/* brightness cache */
+	unsigned int torch_brightness;
+	/* assures led-triggers compatibility */
+	struct work_struct work_brightness_set;
+};
+
+static struct aat1290_led *fled_cdev_to_led(
+				struct led_classdev_flash *fled_cdev)
+{
+	return container_of(fled_cdev, struct aat1290_led, fled_cdev);
+}
+
+static void aat1290_as2cwire_write(struct aat1290_led *led, int addr, int value)
+{
+	int i;
+
+	gpiod_direction_output(led->gpio_fl_en, 0);
+	gpiod_direction_output(led->gpio_en_set, 0);
+
+	udelay(AAT1290_FLEN_OFF_DELAY_TIME_US);
+
+	/* write address */
+	for (i = 0; i < addr; ++i) {
+		udelay(AAT1290_EN_SET_TICK_TIME_US);
+		gpiod_direction_output(led->gpio_en_set, 0);
+		udelay(AAT1290_EN_SET_TICK_TIME_US);
+		gpiod_direction_output(led->gpio_en_set, 1);
+	}
+
+	usleep_range(AAT1290_LATCH_TIME_MIN_US, AAT1290_LATCH_TIME_MAX_US);
+
+	/* write data */
+	for (i = 0; i < value; ++i) {
+		udelay(AAT1290_EN_SET_TICK_TIME_US);
+		gpiod_direction_output(led->gpio_en_set, 0);
+		udelay(AAT1290_EN_SET_TICK_TIME_US);
+		gpiod_direction_output(led->gpio_en_set, 1);
+	}
+
+	usleep_range(AAT1290_LATCH_TIME_MIN_US, AAT1290_LATCH_TIME_MAX_US);
+}
+
+static void aat1290_set_flash_safety_timer(struct aat1290_led *led,
+					unsigned int micro_sec)
+{
+	struct led_classdev_flash *fled_cdev = &led->fled_cdev;
+	struct led_flash_setting *flash_tm = &fled_cdev->timeout;
+	int flash_tm_reg = AAT1290_FLASH_TM_NUM_LEVELS -
+				(micro_sec / flash_tm->step) + 1;
+
+	aat1290_as2cwire_write(led, AAT1290_FLASH_SAFETY_TIMER_ADDR,
+							flash_tm_reg);
+}
+
+static void aat1290_brightness_set(struct aat1290_led *led,
+					enum led_brightness brightness)
+{
+	mutex_lock(&led->lock);
+
+	if (brightness == 0) {
+		gpiod_direction_output(led->gpio_fl_en, 0);
+		gpiod_direction_output(led->gpio_en_set, 0);
+		led->movie_mode = false;
+		goto unlock;
+	}
+
+	if (!led->movie_mode) {
+		aat1290_as2cwire_write(led, AAT1290_MM_CURRENT_RATIO_ADDR,
+					AAT1290_MM_TO_FL_1_92);
+		led->movie_mode = true;
+	}
+
+	aat1290_as2cwire_write(led, AAT1290_MOVIE_MODE_CURRENT_ADDR,
+				AAT1290_MAX_MM_CURR_PERCENT_0 - brightness);
+	aat1290_as2cwire_write(led, AAT1290_MOVIE_MODE_CONFIG_ADDR,
+				AAT1290_MOVIE_MODE_ON);
+unlock:
+	mutex_unlock(&led->lock);
+}
+
+/* LED subsystem callbacks */
+
+static void aat1290_brightness_set_work(struct work_struct *work)
+{
+	struct aat1290_led *led =
+		container_of(work, struct aat1290_led, work_brightness_set);
+
+	aat1290_brightness_set(led, led->torch_brightness);
+}
+
+static void aat1290_led_brightness_set(struct led_classdev *led_cdev,
+					enum led_brightness brightness)
+{
+	struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
+	struct aat1290_led *led = fled_cdev_to_led(fled_cdev);
+
+	led->torch_brightness = brightness;
+	schedule_work(&led->work_brightness_set);
+}
+
+static int aat1290_led_brightness_set_sync(struct led_classdev *led_cdev,
+					enum led_brightness brightness)
+{
+	struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
+	struct aat1290_led *led = fled_cdev_to_led(fled_cdev);
+
+	aat1290_brightness_set(led, brightness);
+
+	return 0;
+}
+
+static int aat1290_led_flash_strobe_set(struct led_classdev_flash *fled_cdev,
+					 bool state)
+
+{
+	struct aat1290_led *led = fled_cdev_to_led(fled_cdev);
+	struct led_classdev *led_cdev = &fled_cdev->led_cdev;
+	struct led_flash_setting *timeout = &fled_cdev->timeout;
+
+	mutex_lock(&led->lock);
+
+	if (state == 0) {
+		gpiod_direction_output(led->gpio_fl_en, 0);
+		gpiod_direction_output(led->gpio_en_set, 0);
+		goto done;
+	}
+
+	aat1290_set_flash_safety_timer(led, timeout->val);
+
+	gpiod_direction_output(led->gpio_fl_en, 1);
+
+done:
+	/*
+	 * To reenter movie mode after a flash event the part must be cycled
+	 * off and back on to reset the movie mode and reprogrammed via the
+	 * AS2Cwire. Therefore the brightness and movie_mode properties needs
+	 * to be updated here to reflect the actual state.
+	 */
+	led_cdev->brightness = 0;
+	led->movie_mode = false;
+
+	mutex_unlock(&led->lock);
+
+	return 0;
+}
+
+static int aat1290_led_flash_timeout_set(struct led_classdev_flash *fled_cdev,
+						u32 timeout)
+{
+	/*
+	 * Don't do anything - flash timeout is cached in the led-class-flash
+	 * core and will be applied in the strobe_set op, as writing the
+	 * safety timer register spuriously turns the torch mode on.
+	 */
+
+	return 0;
+}
+
+static int aat1290_led_parse_dt(struct aat1290_led *led)
+{
+	struct led_classdev *led_cdev = &led->fled_cdev.led_cdev;
+	struct device *dev = &led->pdev->dev;
+	struct device_node *child_node;
+	int ret = 0;
+
+	led->gpio_fl_en = devm_gpiod_get(dev, "flen");
+	if (IS_ERR(led->gpio_fl_en)) {
+		ret = PTR_ERR(led->gpio_fl_en);
+		dev_err(dev, "Unable to claim gpio \"flen\".\n");
+		return ret;
+	}
+
+	led->gpio_en_set = devm_gpiod_get(dev, "enset");
+	if (IS_ERR(led->gpio_en_set)) {
+		ret = PTR_ERR(led->gpio_en_set);
+		dev_err(dev, "Unable to claim gpio \"enset\".\n");
+		return ret;
+	}
+
+	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");
+		return -EINVAL;
+	}
+
+	of_property_read_string(child_node, "label",
+				(const char **) &led_cdev->name);
+
+	ret = of_property_read_u32(child_node, "flash-max-microamp",
+				&led->max_flash_current);
+	if (ret < 0) {
+		dev_err(dev,
+			"Error reading \"flash-max-microamp\" DT property\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(child_node, "flash-timeout-us",
+				&led->max_flash_tm);
+	if (ret < 0) {
+		dev_err(dev,
+			"Error reading \"flash-timeout-us\" DT property\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static void aat1290_init_flash_settings(struct aat1290_led *led,
+					 struct aat1290_led_settings *s)
+{
+	struct led_flash_setting *setting;
+
+	/* Init flash timeout setting */
+	setting = &s->flash_timeout;
+	setting->min = led->max_flash_tm / AAT1290_FLASH_TM_NUM_LEVELS;
+	setting->max = setting->min * AAT1290_FLASH_TM_NUM_LEVELS;
+	setting->step = setting->min;
+	setting->val = setting->max;
+}
+
+static const struct led_flash_ops flash_ops = {
+	.strobe_set = aat1290_led_flash_strobe_set,
+	.timeout_set = aat1290_led_flash_timeout_set,
+};
+
+static int aat1290_led_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct aat1290_led *led;
+	struct led_classdev *led_cdev;
+	struct led_classdev_flash *fled_cdev;
+	struct aat1290_led_settings settings;
+	int ret;
+
+	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	led->pdev = pdev;
+	platform_set_drvdata(pdev, led);
+
+	fled_cdev = &led->fled_cdev;
+	led_cdev = &fled_cdev->led_cdev;
+
+	ret = aat1290_led_parse_dt(led);
+	if (ret < 0)
+		return ret;
+
+	if (!led_cdev->name)
+		led_cdev->name = AAT1290_NAME;
+
+	/* Init flash settings */
+	aat1290_init_flash_settings(led, &settings);
+
+	fled_cdev->timeout = settings.flash_timeout;
+	fled_cdev->ops = &flash_ops;
+
+	/* Init LED class */
+	led_cdev->brightness_set = aat1290_led_brightness_set;
+	led_cdev->brightness_set_sync = aat1290_led_brightness_set_sync;
+	led_cdev->max_brightness = AAT1290_MM_CURRENT_SCALE_SIZE;
+	led_cdev->flags |= LED_DEV_CAP_FLASH;
+
+	INIT_WORK(&led->work_brightness_set, aat1290_brightness_set_work);
+
+	/* Register in the LED subsystem. */
+	ret = led_classdev_flash_register(&pdev->dev, fled_cdev);
+	if (ret < 0)
+		return ret;
+
+	mutex_init(&led->lock);
+
+	return 0;
+}
+
+static int aat1290_led_remove(struct platform_device *pdev)
+{
+	struct aat1290_led *led = platform_get_drvdata(pdev);
+
+	led_classdev_flash_unregister(&led->fled_cdev);
+	cancel_work_sync(&led->work_brightness_set);
+
+	mutex_destroy(&led->lock);
+
+	return 0;
+}
+
+static const struct of_device_id aat1290_led_dt_match[] = {
+	{.compatible = "skyworks,aat1290"},
+	{},
+};
+
+static struct platform_driver aat1290_led_driver = {
+	.probe		= aat1290_led_probe,
+	.remove		= aat1290_led_remove,
+	.driver		= {
+		.name	= "aat1290",
+		.owner	= THIS_MODULE,
+		.of_match_table = aat1290_led_dt_match,
+	},
+};
+
+module_platform_driver(aat1290_led_driver);
+
+MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
+MODULE_DESCRIPTION("Skyworks Current Regulator for Flash LEDs");
+MODULE_LICENSE("GPL v2");
-- 
1.7.9.5

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

* [PATCH v1 04/11] of: Add Skyworks Solutions, Inc. vendor prefix
  2015-03-20 15:03 [PATCH v1 00/11] LED / flash API integration Jacek Anaszewski
                   ` (2 preceding siblings ...)
  2015-03-20 15:03 ` [PATCH v1 03/11] leds: Add driver for AAT1290 current regulator Jacek Anaszewski
@ 2015-03-20 15:03 ` Jacek Anaszewski
  2015-03-20 15:03 ` [PATCH v1 05/11] DT: Add documentation for the Skyworks AAT1290 Jacek Anaszewski
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-20 15:03 UTC (permalink / raw)
  To: linux-leds, linux-media, devicetree
  Cc: kyungmin.park, pavel, cooloney, rpurdie, sakari.ailus,
	s.nawrocki, Jacek Anaszewski

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

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 .../devicetree/bindings/vendor-prefixes.txt        |    1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index a9eeaa0..c87bd87 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -161,6 +161,7 @@ ricoh	Ricoh Co. Ltd.
 rockchip	Fuzhou Rockchip Electronics Co., Ltd
 samsung	Samsung Semiconductor
 sandisk	Sandisk Corporation
+skyworks	Skyworks Solutions, Inc.
 sbs	Smart Battery System
 schindler	Schindler
 seagate	Seagate Technology PLC
-- 
1.7.9.5

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

* [PATCH v1 05/11] DT: Add documentation for the Skyworks AAT1290
  2015-03-20 15:03 [PATCH v1 00/11] LED / flash API integration Jacek Anaszewski
                   ` (3 preceding siblings ...)
  2015-03-20 15:03 ` [PATCH v1 04/11] of: Add Skyworks Solutions, Inc. vendor prefix Jacek Anaszewski
@ 2015-03-20 15:03 ` Jacek Anaszewski
  2015-03-20 15:03 ` [PATCH v1 07/11] media: Add registration helpers for V4L2 flash sub-devices Jacek Anaszewski
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-20 15:03 UTC (permalink / raw)
  To: linux-leds, linux-media, devicetree
  Cc: kyungmin.park, pavel, cooloney, rpurdie, sakari.ailus,
	s.nawrocki, Jacek Anaszewski

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

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

diff --git a/Documentation/devicetree/bindings/leds/leds-aat1290.txt b/Documentation/devicetree/bindings/leds/leds-aat1290.txt
new file mode 100644
index 0000000..c3df4e9
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-aat1290.txt
@@ -0,0 +1,70 @@
+* Skyworks Solutions, Inc. AAT1290 Current Regulator for Flash LEDs
+
+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. In order to add a capability of selecting the strobe signal source
+(e.g. GPIO or ISP) there is an additional switch required, independent of the
+flash chip. The switch is controlled with pin control.
+
+Required properties:
+
+- compatible : Must be "skyworks,aat1290".
+- 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.
+
+Required properties of the LED child node:
+- flash-max-microamp : Maximum intensity in microamperes of the flash LED -
+		       it can be calculated using following formula:
+		       I = 1A * 162kohm / Rset.
+- flash-timeout-us : Maximum flash timeout in microseconds -
+		     it can be calculated using following formula:
+		     T = 8.82 * 10^9 * Ct.
+
+Optional properties of the LED child node:
+- label : see Documentation/devicetree/bindings/leds/common.txt
+
+Example (by Ct = 220nF, Rset = 160kohm and exynos4412-trats2 board with
+a switch that allows for routing strobe signal either from host or from ISP):
+
+#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";
+		flash-max-microamp = <1012500>;
+		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] 39+ messages in thread

* [PATCH v1 06/11] exynos4-is: Add support for v4l2-flash subdevs
  2015-03-20 15:03 [PATCH v1 00/11] LED / flash API integration Jacek Anaszewski
@ 2015-03-20 15:03     ` Jacek Anaszewski
  2015-03-20 15:03 ` [PATCH v1 02/11] DT: Add documentation for the mfd Maxim max77693 Jacek Anaszewski
                       ` (8 subsequent siblings)
  9 siblings, 0 replies; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-20 15:03 UTC (permalink / raw)
  To: linux-leds-u79uwXL29TY76Z2rM5mHXA,
	linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ, pavel-+ZI9xUNit7I,
	cooloney-Re5JQEeQqe8AvxtiuMwx3w, rpurdie-Fm38FmjxZ/leoWH0uzbU5w,
	sakari.ailus-X3B1VOXEql0, s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ,
	Jacek Anaszewski

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

Signed-off-by: Jacek Anaszewski <j.anaszewski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Acked-by: Kyungmin Park <kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Cc: Sylwester Nawrocki <s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
---
 drivers/media/platform/exynos4-is/media-dev.c |   36 +++++++++++++++++++++++--
 drivers/media/platform/exynos4-is/media-dev.h |   13 ++++++++-
 2 files changed, 46 insertions(+), 3 deletions(-)

diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index f315ef9..8dd0e5d 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -451,6 +451,25 @@ rpm_put:
 	return ret;
 }
 
+static void fimc_md_register_flash_entities(struct fimc_md *fmd)
+{
+	struct device_node *parent = fmd->pdev->dev.of_node;
+	struct device_node *np;
+	int i = 0;
+
+	do {
+		np = of_parse_phandle(parent, "flashes", i);
+		if (np) {
+			fmd->flash[fmd->num_flashes].asd.match_type =
+							V4L2_ASYNC_MATCH_OF;
+			fmd->flash[fmd->num_flashes].asd.match.of.node = np;
+			fmd->num_flashes++;
+			fmd->async_subdevs[fmd->num_sensors + i] =
+						&fmd->flash[i].asd;
+		}
+	} while (np && (++i < FIMC_MAX_FLASHES));
+}
+
 static int __of_get_csis_id(struct device_node *np)
 {
 	u32 reg = 0;
@@ -1275,6 +1294,15 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
 	struct fimc_sensor_info *si = NULL;
 	int i;
 
+	/* Register flash subdev if detected any */
+	for (i = 0; i < ARRAY_SIZE(fmd->flash); i++) {
+		if (fmd->flash[i].asd.match.of.node == subdev->dev->of_node) {
+			fmd->flash[i].subdev = subdev;
+			fmd->num_flashes++;
+			return 0;
+		}
+	}
+
 	/* Find platform data for this sensor subdev */
 	for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++)
 		if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node)
@@ -1385,6 +1413,8 @@ static int fimc_md_probe(struct platform_device *pdev)
 		goto err_m_ent;
 	}
 
+	fimc_md_register_flash_entities(fmd);
+
 	mutex_unlock(&fmd->media_dev.graph_mutex);
 
 	ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
@@ -1401,12 +1431,14 @@ static int fimc_md_probe(struct platform_device *pdev)
 		goto err_attr;
 	}
 
-	if (fmd->num_sensors > 0) {
+	if (fmd->num_sensors > 0 || fmd->num_flashes > 0) {
 		fmd->subdev_notifier.subdevs = fmd->async_subdevs;
-		fmd->subdev_notifier.num_subdevs = fmd->num_sensors;
+		fmd->subdev_notifier.num_subdevs = fmd->num_sensors +
+							fmd->num_flashes;
 		fmd->subdev_notifier.bound = subdev_notifier_bound;
 		fmd->subdev_notifier.complete = subdev_notifier_complete;
 		fmd->num_sensors = 0;
+		fmd->num_flashes = 0;
 
 		ret = v4l2_async_notifier_register(&fmd->v4l2_dev,
 						&fmd->subdev_notifier);
diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
index 0321454..feff9c8 100644
--- a/drivers/media/platform/exynos4-is/media-dev.h
+++ b/drivers/media/platform/exynos4-is/media-dev.h
@@ -34,6 +34,8 @@
 
 #define FIMC_MAX_SENSORS	4
 #define FIMC_MAX_CAMCLKS	2
+#define FIMC_MAX_FLASHES	2
+#define FIMC_MAX_ASYNC_SUBDEVS (FIMC_MAX_SENSORS + FIMC_MAX_FLASHES)
 #define DEFAULT_SENSOR_CLK_FREQ	24000000U
 
 /* LCD/ISP Writeback clocks (PIXELASYNCMx) */
@@ -93,6 +95,11 @@ struct fimc_sensor_info {
 	struct fimc_dev *host;
 };
 
+struct fimc_flash_info {
+	struct v4l2_subdev *subdev;
+	struct v4l2_async_subdev asd;
+};
+
 struct cam_clk {
 	struct clk_hw hw;
 	struct fimc_md *fmd;
@@ -104,6 +111,8 @@ struct cam_clk {
  * @csis: MIPI CSIS subdevs data
  * @sensor: array of registered sensor subdevs
  * @num_sensors: actual number of registered sensors
+ * @flash: array of registered flash subdevs
+ * @num_flashes: actual number of registered flashes
  * @camclk: external sensor clock information
  * @fimc: array of registered fimc devices
  * @fimc_is: fimc-is data structure
@@ -123,6 +132,8 @@ struct fimc_md {
 	struct fimc_csis_info csis[CSIS_MAX_ENTITIES];
 	struct fimc_sensor_info sensor[FIMC_MAX_SENSORS];
 	int num_sensors;
+	struct fimc_flash_info flash[FIMC_MAX_FLASHES];
+	int num_flashes;
 	struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS];
 	struct clk *wbclk[FIMC_MAX_WBCLKS];
 	struct fimc_lite *fimc_lite[FIMC_LITE_MAX_DEVS];
@@ -149,7 +160,7 @@ struct fimc_md {
 	} clk_provider;
 
 	struct v4l2_async_notifier subdev_notifier;
-	struct v4l2_async_subdev *async_subdevs[FIMC_MAX_SENSORS];
+	struct v4l2_async_subdev *async_subdevs[FIMC_MAX_ASYNC_SUBDEVS];
 
 	bool user_subdev_api;
 	spinlock_t slock;
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH v1 06/11] exynos4-is: Add support for v4l2-flash subdevs
@ 2015-03-20 15:03     ` Jacek Anaszewski
  0 siblings, 0 replies; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-20 15:03 UTC (permalink / raw)
  To: linux-leds, linux-media, devicetree
  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 "flashes" DT property
and asynchronous subdevice 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 |   36 +++++++++++++++++++++++--
 drivers/media/platform/exynos4-is/media-dev.h |   13 ++++++++-
 2 files changed, 46 insertions(+), 3 deletions(-)

diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index f315ef9..8dd0e5d 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -451,6 +451,25 @@ rpm_put:
 	return ret;
 }
 
+static void fimc_md_register_flash_entities(struct fimc_md *fmd)
+{
+	struct device_node *parent = fmd->pdev->dev.of_node;
+	struct device_node *np;
+	int i = 0;
+
+	do {
+		np = of_parse_phandle(parent, "flashes", i);
+		if (np) {
+			fmd->flash[fmd->num_flashes].asd.match_type =
+							V4L2_ASYNC_MATCH_OF;
+			fmd->flash[fmd->num_flashes].asd.match.of.node = np;
+			fmd->num_flashes++;
+			fmd->async_subdevs[fmd->num_sensors + i] =
+						&fmd->flash[i].asd;
+		}
+	} while (np && (++i < FIMC_MAX_FLASHES));
+}
+
 static int __of_get_csis_id(struct device_node *np)
 {
 	u32 reg = 0;
@@ -1275,6 +1294,15 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
 	struct fimc_sensor_info *si = NULL;
 	int i;
 
+	/* Register flash subdev if detected any */
+	for (i = 0; i < ARRAY_SIZE(fmd->flash); i++) {
+		if (fmd->flash[i].asd.match.of.node == subdev->dev->of_node) {
+			fmd->flash[i].subdev = subdev;
+			fmd->num_flashes++;
+			return 0;
+		}
+	}
+
 	/* Find platform data for this sensor subdev */
 	for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++)
 		if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node)
@@ -1385,6 +1413,8 @@ static int fimc_md_probe(struct platform_device *pdev)
 		goto err_m_ent;
 	}
 
+	fimc_md_register_flash_entities(fmd);
+
 	mutex_unlock(&fmd->media_dev.graph_mutex);
 
 	ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
@@ -1401,12 +1431,14 @@ static int fimc_md_probe(struct platform_device *pdev)
 		goto err_attr;
 	}
 
-	if (fmd->num_sensors > 0) {
+	if (fmd->num_sensors > 0 || fmd->num_flashes > 0) {
 		fmd->subdev_notifier.subdevs = fmd->async_subdevs;
-		fmd->subdev_notifier.num_subdevs = fmd->num_sensors;
+		fmd->subdev_notifier.num_subdevs = fmd->num_sensors +
+							fmd->num_flashes;
 		fmd->subdev_notifier.bound = subdev_notifier_bound;
 		fmd->subdev_notifier.complete = subdev_notifier_complete;
 		fmd->num_sensors = 0;
+		fmd->num_flashes = 0;
 
 		ret = v4l2_async_notifier_register(&fmd->v4l2_dev,
 						&fmd->subdev_notifier);
diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
index 0321454..feff9c8 100644
--- a/drivers/media/platform/exynos4-is/media-dev.h
+++ b/drivers/media/platform/exynos4-is/media-dev.h
@@ -34,6 +34,8 @@
 
 #define FIMC_MAX_SENSORS	4
 #define FIMC_MAX_CAMCLKS	2
+#define FIMC_MAX_FLASHES	2
+#define FIMC_MAX_ASYNC_SUBDEVS (FIMC_MAX_SENSORS + FIMC_MAX_FLASHES)
 #define DEFAULT_SENSOR_CLK_FREQ	24000000U
 
 /* LCD/ISP Writeback clocks (PIXELASYNCMx) */
@@ -93,6 +95,11 @@ struct fimc_sensor_info {
 	struct fimc_dev *host;
 };
 
+struct fimc_flash_info {
+	struct v4l2_subdev *subdev;
+	struct v4l2_async_subdev asd;
+};
+
 struct cam_clk {
 	struct clk_hw hw;
 	struct fimc_md *fmd;
@@ -104,6 +111,8 @@ struct cam_clk {
  * @csis: MIPI CSIS subdevs data
  * @sensor: array of registered sensor subdevs
  * @num_sensors: actual number of registered sensors
+ * @flash: array of registered flash subdevs
+ * @num_flashes: actual number of registered flashes
  * @camclk: external sensor clock information
  * @fimc: array of registered fimc devices
  * @fimc_is: fimc-is data structure
@@ -123,6 +132,8 @@ struct fimc_md {
 	struct fimc_csis_info csis[CSIS_MAX_ENTITIES];
 	struct fimc_sensor_info sensor[FIMC_MAX_SENSORS];
 	int num_sensors;
+	struct fimc_flash_info flash[FIMC_MAX_FLASHES];
+	int num_flashes;
 	struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS];
 	struct clk *wbclk[FIMC_MAX_WBCLKS];
 	struct fimc_lite *fimc_lite[FIMC_LITE_MAX_DEVS];
@@ -149,7 +160,7 @@ struct fimc_md {
 	} clk_provider;
 
 	struct v4l2_async_notifier subdev_notifier;
-	struct v4l2_async_subdev *async_subdevs[FIMC_MAX_SENSORS];
+	struct v4l2_async_subdev *async_subdevs[FIMC_MAX_ASYNC_SUBDEVS];
 
 	bool user_subdev_api;
 	spinlock_t slock;
-- 
1.7.9.5


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

* [PATCH v1 07/11] media: Add registration helpers for V4L2 flash sub-devices
  2015-03-20 15:03 [PATCH v1 00/11] LED / flash API integration Jacek Anaszewski
                   ` (4 preceding siblings ...)
  2015-03-20 15:03 ` [PATCH v1 05/11] DT: Add documentation for the Skyworks AAT1290 Jacek Anaszewski
@ 2015-03-20 15:03 ` Jacek Anaszewski
  2015-03-22  0:22   ` Sakari Ailus
  2015-03-25  0:40   ` Sakari Ailus
       [not found] ` <1426863811-12516-1-git-send-email-j.anaszewski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
                   ` (3 subsequent siblings)
  9 siblings, 2 replies; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-20 15:03 UTC (permalink / raw)
  To: linux-leds, linux-media, devicetree
  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      |   12 +
 drivers/media/v4l2-core/Makefile     |    2 +
 drivers/media/v4l2-core/v4l2-flash.c |  607 ++++++++++++++++++++++++++++++++++
 include/media/v4l2-flash.h           |  145 ++++++++
 4 files changed, 766 insertions(+)
 create mode 100644 drivers/media/v4l2-core/v4l2-flash.c
 create mode 100644 include/media/v4l2-flash.h

diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
index ba7e21a..0659598 100644
--- a/drivers/media/v4l2-core/Kconfig
+++ b/drivers/media/v4l2-core/Kconfig
@@ -44,6 +44,18 @@ config V4L2_MEM2MEM_DEV
         tristate
         depends on VIDEOBUF2_CORE
 
+# Used by LED subsystem flash drivers
+config V4L2_FLASH_LED_CLASS
+	tristate "Enable support for Flash sub-devices"
+	depends on VIDEO_V4L2_SUBDEV_API
+	depends on LEDS_CLASS_FLASH
+	depends on OF
+	---help---
+	  Say Y here to enable support for Flash sub-devices, which allow
+	  to control LED class devices with use of V4L2 Flash controls.
+
+	  When in doubt, say N.
+
 # Used by drivers that need Videobuf modules
 config VIDEOBUF_GEN
 	tristate
diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
index 63d29f2..44e858c 100644
--- a/drivers/media/v4l2-core/Makefile
+++ b/drivers/media/v4l2-core/Makefile
@@ -22,6 +22,8 @@ obj-$(CONFIG_VIDEO_TUNER) += tuner.o
 
 obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
 
+obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash.o
+
 obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
 obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
 obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o
diff --git a/drivers/media/v4l2-core/v4l2-flash.c b/drivers/media/v4l2-core/v4l2-flash.c
new file mode 100644
index 0000000..804c2e4
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-flash.c
@@ -0,0 +1,607 @@
+/*
+ * 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.h>
+#include "../../leds/leds.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)
+
+static enum led_brightness __intensity_to_led_brightness(
+					struct v4l2_ctrl *ctrl,
+					s32 intensity)
+{
+	s64 intensity64 = intensity - ctrl->minimum;
+
+	do_div(intensity64, 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->id != V4L2_CID_FLASH_INDICATOR_INTENSITY)
+		++intensity64;
+
+	return intensity64;
+}
+
+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] &&
+	    ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
+		return;
+
+	led_set_brightness(&v4l2_flash->fled_cdev->led_cdev, brightness);
+}
+
+static int v4l2_flash_update_led_brightness(struct v4l2_flash *v4l2_flash,
+					struct v4l2_ctrl *ctrl)
+{
+	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;
+	int ret;
+
+	/*
+	 * 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 (ctrl == ctrls[TORCH_INTENSITY] &&
+	    ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
+		return 0;
+
+	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 */
+		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 */
+		return led_set_flash_timeout(fled_cdev, c->val);
+	case V4L2_CID_FLASH_INTENSITY:
+		/* no conversion is needed */
+		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 fill_ctrl_init_data(struct v4l2_flash *v4l2_flash,
+			  struct v4l2_flash_ctrl_config *flash_ctrl_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_ctrl_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_ctrl_cfg->flash_faults;
+		ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
+				  V4L2_CTRL_FLAG_READ_ONLY;
+	}
+
+	/* Init INDICATOR_INTENSITY ctrl data */
+	if (flash_ctrl_cfg->indicator_led) {
+		ctrl_init_data[INDICATOR_INTENSITY].cid =
+					V4L2_CID_FLASH_INDICATOR_INTENSITY;
+		ctrl_init_data[INDICATOR_INTENSITY].config =
+						flash_ctrl_cfg->intensity;
+		ctrl_cfg = &ctrl_init_data[INDICATOR_INTENSITY].config;
+		ctrl_cfg->id = V4L2_CID_FLASH_INDICATOR_INTENSITY;
+		ctrl_cfg->min = 0;
+		ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
+
+		/* Indicator LED can have only faults and intensity controls. */
+		return;
+	}
+
+	/* 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_init_data[TORCH_INTENSITY].config = flash_ctrl_cfg->intensity;
+	ctrl_cfg = &ctrl_init_data[TORCH_INTENSITY].config;
+	ctrl_cfg->id = V4L2_CID_FLASH_TORCH_INTENSITY;
+	ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
+
+	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_ctrl_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_init_data[FLASH_TIMEOUT].config =
+					flash_ctrl_cfg->flash_timeout;
+		ctrl_cfg = &ctrl_init_data[FLASH_TIMEOUT].config;
+		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_init_data[FLASH_INTENSITY].config =
+					flash_ctrl_cfg->flash_intensity;
+		ctrl_cfg = &ctrl_init_data[FLASH_INTENSITY].config;
+		ctrl_cfg->id = V4L2_CID_FLASH_INTENSITY;
+		ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
+	}
+}
+
+static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash,
+				struct v4l2_flash_ctrl_config *flash_ctrl_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;
+
+	/* 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_ctrl_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;
+
+	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);
+
+	if (ctrls[INDICATOR_INTENSITY])
+		v4l2_flash_set_led_brightness(v4l2_flash,
+						ctrls[INDICATOR_INTENSITY]);
+	else
+		v4l2_flash_set_led_brightness(v4l2_flash,
+						ctrls[TORCH_INTENSITY]);
+
+	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;
+	int ret = 0;
+
+	mutex_lock(&led_cdev->led_access);
+
+	if (!v4l2_fh_is_singular(&fh->vfh)) {
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	led_sysfs_disable(led_cdev);
+	led_trigger_remove(led_cdev);
+
+	ret = __sync_device_with_v4l2_controls(v4l2_flash);
+
+unlock:
+	mutex_unlock(&led_cdev->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;
+	int ret = 0;
+
+	mutex_lock(&led_cdev->led_access);
+
+	if (v4l2_flash->ctrls[STROBE_SOURCE])
+		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);
+
+	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 led_classdev_flash *fled_cdev,
+				   const struct v4l2_flash_ops *ops,
+				   struct v4l2_flash_ctrl_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->ops = ops;
+	sd->dev = led_cdev->dev;
+	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)
+		return ERR_PTR(ret);
+
+	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);
+	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 = &v4l2_flash->sd;
+	struct led_classdev *led_cdev = &v4l2_flash->fled_cdev->led_cdev;
+
+	v4l2_async_unregister_subdev(sd);
+	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.h b/include/media/v4l2-flash.h
new file mode 100644
index 0000000..cc0138d
--- /dev/null
+++ b/include/media/v4l2-flash.h
@@ -0,0 +1,145 @@
+/*
+ * 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;
+
+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,
+};
+
+/*
+ * 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_ctrl_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_ctrl_config - V4L2 Flash controls initialization data
+ * @dev_name:			human readable device name
+ * @intensity:			constraints for the led in a non-flash mode
+ * @flash_intensity:		V4L2_CID_FLASH_INTENSITY constraints
+ * @flash_timeout:		V4L2_CID_FLASH_TIMEOUT constraints
+ * @flash_faults:		possible flash faults
+ * @has_external_strobe:	external strobe capability
+ * @indicator_led:		signifies that a led is of indicator type
+ */
+struct v4l2_flash_ctrl_config {
+	char dev_name[32];
+	struct v4l2_ctrl_config intensity;
+	struct v4l2_ctrl_config flash_intensity;
+	struct v4l2_ctrl_config flash_timeout;
+	u32 flash_faults;
+	unsigned int has_external_strobe:1;
+	unsigned int indicator_led:1;
+};
+
+/**
+ * struct v4l2_flash - Flash sub-device context
+ * @fled_cdev:		LED Flash class device controlled by this sub-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;
+	const struct v4l2_flash_ops *ops;
+
+	struct v4l2_subdev sd;
+	struct v4l2_ctrl_handler hdl;
+	struct v4l2_ctrl *ctrls[STROBE_SOURCE + 1];
+};
+
+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
+ * @fled_cdev:	the LED Flash class device to wrap
+ * @flash_ops:	V4L2 Flash device ops
+ * @config:	initialization data for V4L2 Flash controls
+ *
+ * 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 led_classdev_flash *fled_cdev,
+				   const struct v4l2_flash_ops *ops,
+				   struct v4l2_flash_ctrl_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
+#define v4l2_flash_init(fled_cdev, ops, config) (NULL)
+#define v4l2_flash_release(v4l2_flash)
+#endif /* CONFIG_V4L2_FLASH_LED_CLASS */
+
+#endif /* _V4L2_FLASH_H */
-- 
1.7.9.5

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

* [PATCH v1 08/11] Documentation: leds: Add description of v4l2-flash sub-device
  2015-03-20 15:03 [PATCH v1 00/11] LED / flash API integration Jacek Anaszewski
@ 2015-03-20 15:03     ` Jacek Anaszewski
  2015-03-20 15:03 ` [PATCH v1 02/11] DT: Add documentation for the mfd Maxim max77693 Jacek Anaszewski
                       ` (8 subsequent siblings)
  9 siblings, 0 replies; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-20 15:03 UTC (permalink / raw)
  To: linux-leds-u79uwXL29TY76Z2rM5mHXA,
	linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ, pavel-+ZI9xUNit7I,
	cooloney-Re5JQEeQqe8AvxtiuMwx3w, rpurdie-Fm38FmjxZ/leoWH0uzbU5w,
	sakari.ailus-X3B1VOXEql0, s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ,
	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-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Acked-by: Kyungmin Park <kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Acked-by: Sakari Ailus <sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
Cc: Bryan Wu <cooloney-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Cc: Richard Purdie <rpurdie-Fm38FmjxZ/leoWH0uzbU5w@public.gmane.org>
---
 Documentation/leds/leds-class-flash.txt |   13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/Documentation/leds/leds-class-flash.txt b/Documentation/leds/leds-class-flash.txt
index 19bb673..8623413 100644
--- a/Documentation/leds/leds-class-flash.txt
+++ b/Documentation/leds/leds-class-flash.txt
@@ -20,3 +20,16 @@ Following sysfs attributes are exposed for controlling flash LED devices:
 	- max_flash_timeout
 	- flash_strobe
 	- flash_fault
+
+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. On remove the
+v4l2_flash_release function has to be called (see <media/v4l2-flash.h>).
+
+After proper initialization a V4L2 Flash sub-device is created. The sub-device
+exposes a number of V4L2 controls, which allow for controlling a LED Flash class
+device with use of its internal kernel 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

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH v1 08/11] Documentation: leds: Add description of v4l2-flash sub-device
@ 2015-03-20 15:03     ` Jacek Anaszewski
  0 siblings, 0 replies; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-20 15:03 UTC (permalink / raw)
  To: linux-leds, linux-media, devicetree
  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>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Cc: Bryan Wu <cooloney@gmail.com>
Cc: Richard Purdie <rpurdie@rpsys.net>
---
 Documentation/leds/leds-class-flash.txt |   13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/Documentation/leds/leds-class-flash.txt b/Documentation/leds/leds-class-flash.txt
index 19bb673..8623413 100644
--- a/Documentation/leds/leds-class-flash.txt
+++ b/Documentation/leds/leds-class-flash.txt
@@ -20,3 +20,16 @@ Following sysfs attributes are exposed for controlling flash LED devices:
 	- max_flash_timeout
 	- flash_strobe
 	- flash_fault
+
+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. On remove the
+v4l2_flash_release function has to be called (see <media/v4l2-flash.h>).
+
+After proper initialization a V4L2 Flash sub-device is created. The sub-device
+exposes a number of V4L2 controls, which allow for controlling a LED Flash class
+device with use of its internal kernel 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] 39+ messages in thread

* [PATCH v1 09/11] DT: Add documentation for exynos4-is 'flashes' property
  2015-03-20 15:03 [PATCH v1 00/11] LED / flash API integration Jacek Anaszewski
                   ` (6 preceding siblings ...)
       [not found] ` <1426863811-12516-1-git-send-email-j.anaszewski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
@ 2015-03-20 15:03 ` Jacek Anaszewski
  2015-03-25  1:06   ` Sakari Ailus
  2015-03-20 15:03 ` [PATCH v1 10/11] leds: max77693: add support for V4L2 Flash sub-device Jacek Anaszewski
  2015-03-20 15:03 ` [PATCH v1 11/11] leds: aat1290: " Jacek Anaszewski
  9 siblings, 1 reply; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-20 15:03 UTC (permalink / raw)
  To: linux-leds, linux-media, devicetree
  Cc: kyungmin.park, pavel, cooloney, rpurdie, sakari.ailus,
	s.nawrocki, Jacek Anaszewski

This patch adds a description of '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>
---
 .../devicetree/bindings/media/samsung-fimc.txt     |    8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt
index 922d6f8..cb0e263 100644
--- a/Documentation/devicetree/bindings/media/samsung-fimc.txt
+++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt
@@ -40,6 +40,13 @@ 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:
+
+- flashes - Array of phandles to the flash LEDs that can be controlled by the
+	    sub-devices contained in this media device. 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 +173,7 @@ Example:
 		clock-output-names = "cam_a_clkout", "cam_b_clkout";
 		pinctrl-names = "default";
 		pinctrl-0 = <&cam_port_a_clk_active>;
+		flashes = <&camera_flash>, <&system_torch>;
 		status = "okay";
 		#address-cells = <1>;
 		#size-cells = <1>;
-- 
1.7.9.5

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

* [PATCH v1 10/11] leds: max77693: add support for V4L2 Flash sub-device
  2015-03-20 15:03 [PATCH v1 00/11] LED / flash API integration Jacek Anaszewski
                   ` (7 preceding siblings ...)
  2015-03-20 15:03 ` [PATCH v1 09/11] DT: Add documentation for exynos4-is 'flashes' property Jacek Anaszewski
@ 2015-03-20 15:03 ` Jacek Anaszewski
  2015-03-22  0:28   ` Sakari Ailus
  2015-03-20 15:03 ` [PATCH v1 11/11] leds: aat1290: " Jacek Anaszewski
  9 siblings, 1 reply; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-20 15:03 UTC (permalink / raw)
  To: linux-leds, linux-media, devicetree
  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 |  149 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 141 insertions(+), 8 deletions(-)

diff --git a/drivers/leds/leds-max77693.c b/drivers/leds/leds-max77693.c
index 7386d69..a12bd8c 100644
--- a/drivers/leds/leds-max77693.c
+++ b/drivers/leds/leds-max77693.c
@@ -21,6 +21,7 @@
 #include <linux/regmap.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
+#include <media/v4l2-flash.h>
 
 #define MODE_OFF		0
 #define MODE_FLASH(a)		(1 << (a))
@@ -68,6 +69,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;
@@ -651,7 +654,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;
@@ -697,6 +701,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;
 
 		of_property_read_string(child_node, "label",
@@ -784,11 +795,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;
 
@@ -855,9 +867,86 @@ static const char *max77693_get_led_name(struct max77693_led_device *led,
 					     MAX77693_LED2_NAME;
 }
 
+#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_ctrl_config(struct max77693_led_device *led,
+					int fled_id,
+					struct max77693_led_settings *s,
+					struct v4l2_flash_ctrl_config *config)
+{
+	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 *setting;
+	struct v4l2_ctrl_config *c;
+
+	snprintf(config->dev_name, sizeof(config->dev_name),
+		 "%s %d-%04x", max77693_get_led_name(led, fled_id),
+		 i2c_adapter_id(i2c->adapter), i2c->addr);
+
+	c = &config->intensity;
+	setting = &s->torch_brightness;
+	c->min = setting->min;
+	c->max = setting->max;
+	c->step = setting->step;
+	c->def = setting->val;
+
+	c = &config->flash_intensity;
+	setting = &s->flash_brightness;
+	c->min = setting->min;
+	c->max = setting->max;
+	c->step = setting->step;
+	c->def = setting->val;
+
+	c = &config->flash_timeout;
+	setting = &s->flash_timeout;
+	c->min = setting->min;
+	c->max = setting->max;
+	c->step = setting->step;
+	c->def = setting->val;
+
+	/* Init flash faults config */
+	config->flash_faults =	V4L2_FLASH_FAULT_OVER_VOLTAGE |
+				V4L2_FLASH_FAULT_SHORT_CIRCUIT |
+				V4L2_FLASH_FAULT_OVER_CURRENT;
+
+	config->has_external_strobe = true;
+}
+
+static const struct v4l2_flash_ops v4l2_flash_ops = {
+	.external_strobe_set = max77693_led_external_strobe_set,
+};
+
+#else
+#define max77693_init_v4l2_ctrl_config(led, fled_id, s, config)
+#endif
+
 static void max77693_init_fled_cdev(struct max77693_led_device *led,
 				int fled_id,
-				struct max77693_led_config_data *cfg)
+				struct max77693_led_config_data *cfg,
+				struct v4l2_flash_ctrl_config *v4l2_flash_cfg)
 {
 	struct led_classdev_flash *fled_cdev;
 	struct led_classdev *led_cdev;
@@ -866,6 +955,8 @@ static void max77693_init_fled_cdev(struct max77693_led_device *led,
 
 	/* Initialize flash settings */
 	max77693_init_flash_settings(led, fled_id, cfg, &settings);
+	/* Initialize V4L2 Flash config basing on initialized settings */
+	max77693_init_v4l2_ctrl_config(led, fled_id, &settings, v4l2_flash_cfg);
 
 	/* Initialize LED Flash class device */
 	fled_cdev = &sub_led->fled_cdev;
@@ -891,15 +982,51 @@ static void max77693_init_fled_cdev(struct max77693_led_device *led,
 	sub_led->flash_timeout = fled_cdev->timeout.val;
 }
 
+static int max77693_register_led(struct max77693_sub_led *sub_led,
+				 struct v4l2_flash_ctrl_config *v4l2_flash_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;
+	int ret;
+
+	/* Register in the LED subsystem */
+	ret = led_classdev_flash_register(dev, fled_cdev);
+	if (ret < 0)
+		return ret;
+
+	fled_cdev->led_cdev.dev->of_node = sub_node;
+
+	/* Register in the V4L2 subsystem. */
+	sub_led->v4l2_flash = v4l2_flash_init(fled_cdev, &v4l2_flash_ops,
+						v4l2_flash_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:
+	of_node_put(sub_node);
+	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] = { NULL, NULL };
 	struct max77693_led_config_data cfg = {};
+	struct v4l2_flash_ctrl_config v4l2_flash_config[2];
 	int init_fled_cdev[2], i, ret;
 
+	memset(v4l2_flash_config, 0, sizeof(v4l2_flash_config));
+
 	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
 	if (!led)
 		return -ENOMEM;
@@ -910,7 +1037,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, &cfg);
+	ret = max77693_led_get_configuration(led, &cfg, sub_nodes);
 	if (ret < 0)
 		return ret;
 
@@ -926,15 +1053,18 @@ static int max77693_led_probe(struct platform_device *pdev)
 	/* Initialize LED Flash class device(s) */
 	for (i = FLED1; i <= FLED2; ++i)
 		if (init_fled_cdev[i])
-			max77693_init_fled_cdev(led, i, &cfg);
+			max77693_init_fled_cdev(led, i, &cfg,
+						&v4l2_flash_config[i]);
 	mutex_init(&led->lock);
 
-	/* Register LED Flash class device(s) */
+	/* Register LED Flash class and related V4L2 Flash device(s) */
 	for (i = FLED1; i <= FLED2; ++i) {
 		if (!init_fled_cdev[i])
 			continue;
 
-		ret = led_classdev_flash_register(dev, &sub_leds[i].fled_cdev);
+		ret = max77693_register_led(&sub_leds[i],
+					    &v4l2_flash_config[i],
+					    sub_nodes[i]);
 		if (ret < 0) {
 			/*
 			 * At this moment FLED1 might have been already
@@ -953,6 +1083,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);
@@ -966,11 +1097,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] 39+ messages in thread

* [PATCH v1 11/11] leds: aat1290: add support for V4L2 Flash sub-device
  2015-03-20 15:03 [PATCH v1 00/11] LED / flash API integration Jacek Anaszewski
                   ` (8 preceding siblings ...)
  2015-03-20 15:03 ` [PATCH v1 10/11] leds: max77693: add support for V4L2 Flash sub-device Jacek Anaszewski
@ 2015-03-20 15:03 ` Jacek Anaszewski
  9 siblings, 0 replies; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-20 15:03 UTC (permalink / raw)
  To: linux-leds, linux-media, devicetree
  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 |  186 ++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 185 insertions(+), 2 deletions(-)

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index c3b5b027..d73f43f 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 456f9a9d..6100b6b 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.h>
 
 #define AAT1290_MOVIE_MODE_CURRENT_ADDR	17
 #define AAT1290_MAX_MM_CURR_PERCENT_0	16
@@ -46,6 +48,11 @@
 
 #define AAT1290_NAME			"aat1290"
 
+#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
+/* Number of AAT1290 devices in the system. */
+static unsigned int dev_count;
+#endif
+
 
 struct aat1290_led_settings {
 	struct led_flash_setting torch_brightness;
@@ -60,11 +67,17 @@ struct aat1290_led {
 
 	/* related 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;
 	/* EN|SET pin  */
 	struct gpio_desc *gpio_en_set;
+#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
+	/* movie mode current scale */
+	int *mm_current_scale;
+#endif
 
 	/* maximum flash timeout */
 	u32 max_flash_tm;
@@ -231,11 +244,16 @@ static int aat1290_led_flash_timeout_set(struct led_classdev_flash *fled_cdev,
 	return 0;
 }
 
-static int aat1290_led_parse_dt(struct aat1290_led *led)
+static int aat1290_led_parse_dt(struct aat1290_led *led,
+			struct device_node **sub_node,
+			struct v4l2_flash_ctrl_config *v4l2_flash_config)
 {
 	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");
@@ -252,6 +270,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)) {
+		v4l2_flash_config->has_external_strobe = false;
+		dev_info(dev,
+			 "No support for external strobe detected.\n");
+	} else {
+		v4l2_flash_config->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");
@@ -277,6 +306,8 @@ static int aat1290_led_parse_dt(struct aat1290_led *led)
 		return ret;
 	}
 
+	*sub_node = child_node;
+
 	return ret;
 }
 
@@ -285,6 +316,15 @@ static void aat1290_init_flash_settings(struct aat1290_led *led,
 {
 	struct led_flash_setting *setting;
 
+#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
+	/* Init flash intensity setting */
+	setting = &s->torch_brightness;
+	setting->min = led->mm_current_scale[0];
+	setting->max = led->mm_current_scale[AAT1290_MM_CURRENT_SCALE_SIZE - 1];
+	setting->step = 1;
+	setting->val = setting->max;
+#endif
+
 	/* Init flash timeout setting */
 	setting = &s->flash_timeout;
 	setting->min = led->max_flash_tm / AAT1290_FLASH_TM_NUM_LEVELS;
@@ -293,6 +333,113 @@ static void aat1290_init_flash_settings(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;
+}
+
+int init_mm_current_scale(struct aat1290_led *led)
+{
+	int max_mm_current_percent[] = { 20, 22, 25, 28, 32, 36, 40, 45, 50, 56,
+						63, 71, 79, 89, 100 };
+	int i, max_mm_current = AAT1290_MAX_MM_CURRENT(led->max_flash_current);
+
+	led->mm_current_scale = devm_kzalloc(&led->pdev->dev,
+						sizeof(max_mm_current_percent),
+						GFP_KERNEL);
+	if (!led->mm_current_scale)
+		return -ENOMEM;
+
+	for (i = 0; i < AAT1290_MM_CURRENT_SCALE_SIZE; ++i)
+		led->mm_current_scale[i] = max_mm_current *
+					  max_mm_current_percent[i] / 100;
+
+	return 0;
+}
+
+static void aat1290_init_v4l2_ctrl_config(struct aat1290_led *led,
+					struct aat1290_led_settings *s,
+					struct v4l2_flash_ctrl_config *config)
+{
+	struct led_flash_setting *setting;
+	struct v4l2_ctrl_config *c;
+	char suffix[10];
+
+	if (++dev_count > 1)
+		snprintf(suffix, sizeof(suffix), "_%d", dev_count);
+
+	snprintf(config->dev_name, sizeof(config->dev_name), "%s%s",
+		 AAT1290_NAME, dev_count > 1 ? suffix : "");
+
+	c = &config->intensity;
+	setting = &s->torch_brightness;
+	c->min = setting->min;
+	c->max = setting->max;
+	c->step = setting->step;
+	c->def = setting->val;
+
+	c = &config->flash_timeout;
+	setting = &s->flash_timeout;
+	c->min = setting->min;
+	c->max = setting->max;
+	c->step = setting->step;
+	c->def = setting->val;
+}
+
+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
+#define aat1290_init_v4l2_ctrl_config(led, s, config)
+#define init_mm_current_scale(led) (0)
+#endif
+
 static const struct led_flash_ops flash_ops = {
 	.strobe_set = aat1290_led_flash_strobe_set,
 	.timeout_set = aat1290_led_flash_timeout_set,
@@ -301,10 +448,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_settings settings;
+	struct v4l2_flash_ctrl_config v4l2_flash_config = {};
 	int ret;
 
 	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
@@ -317,16 +466,27 @@ static int aat1290_led_probe(struct platform_device *pdev)
 	fled_cdev = &led->fled_cdev;
 	led_cdev = &fled_cdev->led_cdev;
 
-	ret = aat1290_led_parse_dt(led);
+	ret = aat1290_led_parse_dt(led, &sub_node, &v4l2_flash_config);
 	if (ret < 0)
 		return ret;
 
 	if (!led_cdev->name)
 		led_cdev->name = AAT1290_NAME;
 
+	/*
+	 * Init non-linear movie mode current scale basing
+	 * on the max flash current from Device Tree binding.
+	 */
+	ret = init_mm_current_scale(led);
+	if (ret < 0)
+		return ret;
+
 	/* Init flash settings */
 	aat1290_init_flash_settings(led, &settings);
 
+	/* Init V4L2 Flash controls basing on initialized settings */
+	aat1290_init_v4l2_ctrl_config(led, &settings, &v4l2_flash_config);
+
 	fled_cdev->timeout = settings.flash_timeout;
 	fled_cdev->ops = &flash_ops;
 
@@ -345,18 +505,40 @@ static int aat1290_led_probe(struct platform_device *pdev)
 
 	mutex_init(&led->lock);
 
+	led_cdev->dev->of_node = sub_node;
+
+	/* Create V4L2 Flash subdev. */
+	led->v4l2_flash = v4l2_flash_init(fled_cdev,
+					  &v4l2_flash_ops,
+					  &v4l2_flash_config);
+	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);
+	mutex_destroy(&led->lock);
+
+	return ret;
 }
 
 static int aat1290_led_remove(struct platform_device *pdev)
 {
 	struct aat1290_led *led = platform_get_drvdata(pdev);
 
+	v4l2_flash_release(led->v4l2_flash);
 	led_classdev_flash_unregister(&led->fled_cdev);
 	cancel_work_sync(&led->work_brightness_set);
 
 	mutex_destroy(&led->lock);
 
+#if IS_ENABLED(CONFIG_V4L2_FLASH_LED_CLASS)
+	--dev_count;
+#endif
+
 	return 0;
 }
 
-- 
1.7.9.5

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

* Re: [PATCH v1 01/11] leds: Add support for max77693 mfd flash cell
  2015-03-20 15:03 ` [PATCH v1 01/11] leds: Add support for max77693 mfd flash cell Jacek Anaszewski
@ 2015-03-21 22:44   ` Sakari Ailus
  2015-03-23 13:22     ` Jacek Anaszewski
  0 siblings, 1 reply; 39+ messages in thread
From: Sakari Ailus @ 2015-03-21 22:44 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki, Andrzej Hajda, Lee Jones,
	Chanwoo Choi

Hi Jacek,

On Fri, Mar 20, 2015 at 04:03:21PM +0100, Jacek Anaszewski wrote:
> This patch adds led-flash support to Maxim max77693 chipset.
> A device can be exposed to user space through LED subsystem
> sysfs interface. Device supports up to two leds which can
> work in flash and torch mode. The leds can be triggered
> externally or by software.
> 
> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
> Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> Cc: Bryan Wu <cooloney@gmail.com>
> Cc: Richard Purdie <rpurdie@rpsys.net>
> Cc: Lee Jones <lee.jones@linaro.org>
> Cc: Chanwoo Choi <cw00.choi@samsung.com>

Thanks for the update once again!

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

-- 
Kind regards,

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

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

* Re: [PATCH v1 02/11] DT: Add documentation for the mfd Maxim max77693
  2015-03-20 15:03 ` [PATCH v1 02/11] DT: Add documentation for the mfd Maxim max77693 Jacek Anaszewski
@ 2015-03-21 22:49   ` Sakari Ailus
  2015-03-23  9:54     ` Jacek Anaszewski
  2015-03-26 14:06   ` Lee Jones
  1 sibling, 1 reply; 39+ messages in thread
From: Sakari Ailus @ 2015-03-21 22:49 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki, Andrzej Hajda, Lee Jones,
	Chanwoo Choi

Hi Jacek,

On Fri, Mar 20, 2015 at 04:03:22PM +0100, Jacek Anaszewski wrote:
> +Optional properties of the LED child node:
> +- label : see Documentation/devicetree/bindings/leds/common.txt

I'm still not comfortable using the label field as-is as the entity name in
the later patches, there's one important problem: it is not guaranteed to be
unique in the system.

Do you think this could be added to
Documentation/devicetree/bindings/leds/common.txt, with perhaps enforcing it
in the LED framework? Bryan, what do you think?

The alternative would be to simply ignore it in the entity name, but then
the name of the device would be different in the LED framework and Media
controller.

-- 
Kind regards,

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

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

* Re: [PATCH v1 03/11] leds: Add driver for AAT1290 current regulator
  2015-03-20 15:03 ` [PATCH v1 03/11] leds: Add driver for AAT1290 current regulator Jacek Anaszewski
@ 2015-03-21 22:55   ` Sakari Ailus
  0 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2015-03-21 22:55 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki

Hi Jacek,

On Fri, Mar 20, 2015 at 04:03:23PM +0100, Jacek Anaszewski wrote:
...
> +static int aat1290_led_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct aat1290_led *led;
> +	struct led_classdev *led_cdev;
> +	struct led_classdev_flash *fled_cdev;
> +	struct aat1290_led_settings settings;
> +	int ret;
> +
> +	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
> +	if (!led)
> +		return -ENOMEM;
> +
> +	led->pdev = pdev;
> +	platform_set_drvdata(pdev, led);
> +
> +	fled_cdev = &led->fled_cdev;
> +	led_cdev = &fled_cdev->led_cdev;
> +
> +	ret = aat1290_led_parse_dt(led);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (!led_cdev->name)
> +		led_cdev->name = AAT1290_NAME;
> +
> +	/* Init flash settings */
> +	aat1290_init_flash_settings(led, &settings);
> +
> +	fled_cdev->timeout = settings.flash_timeout;
> +	fled_cdev->ops = &flash_ops;
> +
> +	/* Init LED class */
> +	led_cdev->brightness_set = aat1290_led_brightness_set;
> +	led_cdev->brightness_set_sync = aat1290_led_brightness_set_sync;
> +	led_cdev->max_brightness = AAT1290_MM_CURRENT_SCALE_SIZE;
> +	led_cdev->flags |= LED_DEV_CAP_FLASH;
> +
> +	INIT_WORK(&led->work_brightness_set, aat1290_brightness_set_work);
> +
> +	/* Register in the LED subsystem. */
> +	ret = led_classdev_flash_register(&pdev->dev, fled_cdev);
> +	if (ret < 0)
> +		return ret;
> +
> +	mutex_init(&led->lock);

I think you must initialise the mutex before led_classdev_flash_register(),
as this exposes the device to the user. Remember mutex_destroy() in error
handling.

> +	return 0;
> +}
> +
> +static int aat1290_led_remove(struct platform_device *pdev)
> +{
> +	struct aat1290_led *led = platform_get_drvdata(pdev);
> +
> +	led_classdev_flash_unregister(&led->fled_cdev);
> +	cancel_work_sync(&led->work_brightness_set);
> +
> +	mutex_destroy(&led->lock);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id aat1290_led_dt_match[] = {
> +	{.compatible = "skyworks,aat1290"},

{ .compatible ... 1290" },

With the two issues fixed,

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

> +	{},
> +};
> +
> +static struct platform_driver aat1290_led_driver = {
> +	.probe		= aat1290_led_probe,
> +	.remove		= aat1290_led_remove,
> +	.driver		= {
> +		.name	= "aat1290",
> +		.owner	= THIS_MODULE,
> +		.of_match_table = aat1290_led_dt_match,
> +	},
> +};
> +
> +module_platform_driver(aat1290_led_driver);
> +
> +MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
> +MODULE_DESCRIPTION("Skyworks Current Regulator for Flash LEDs");
> +MODULE_LICENSE("GPL v2");

-- 
Kind regards,

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

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

* Re: [PATCH v1 07/11] media: Add registration helpers for V4L2 flash sub-devices
  2015-03-20 15:03 ` [PATCH v1 07/11] media: Add registration helpers for V4L2 flash sub-devices Jacek Anaszewski
@ 2015-03-22  0:22   ` Sakari Ailus
  2015-03-23 15:08     ` Jacek Anaszewski
  2015-03-25  0:40   ` Sakari Ailus
  1 sibling, 1 reply; 39+ messages in thread
From: Sakari Ailus @ 2015-03-22  0:22 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki, Hans Verkuil

Hi Jacek,

Thanks for the updated set. Some comments below.

On Fri, Mar 20, 2015 at 04:03:27PM +0100, Jacek Anaszewski 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.
> 
> 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      |   12 +
>  drivers/media/v4l2-core/Makefile     |    2 +
>  drivers/media/v4l2-core/v4l2-flash.c |  607 ++++++++++++++++++++++++++++++++++
>  include/media/v4l2-flash.h           |  145 ++++++++
>  4 files changed, 766 insertions(+)
>  create mode 100644 drivers/media/v4l2-core/v4l2-flash.c
>  create mode 100644 include/media/v4l2-flash.h
> 
> diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
> index ba7e21a..0659598 100644
> --- a/drivers/media/v4l2-core/Kconfig
> +++ b/drivers/media/v4l2-core/Kconfig
> @@ -44,6 +44,18 @@ config V4L2_MEM2MEM_DEV
>          tristate
>          depends on VIDEOBUF2_CORE
>  
> +# Used by LED subsystem flash drivers
> +config V4L2_FLASH_LED_CLASS
> +	tristate "Enable support for Flash sub-devices"
> +	depends on VIDEO_V4L2_SUBDEV_API
> +	depends on LEDS_CLASS_FLASH
> +	depends on OF

I think you can drop OF dependency. A no-op implementation will be used if
it's disabled.

> +	---help---
> +	  Say Y here to enable support for Flash sub-devices, which allow
> +	  to control LED class devices with use of V4L2 Flash controls.
> +
> +	  When in doubt, say N.
> +
>  # Used by drivers that need Videobuf modules
>  config VIDEOBUF_GEN
>  	tristate
> diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
> index 63d29f2..44e858c 100644
> --- a/drivers/media/v4l2-core/Makefile
> +++ b/drivers/media/v4l2-core/Makefile
> @@ -22,6 +22,8 @@ obj-$(CONFIG_VIDEO_TUNER) += tuner.o
>  
>  obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
>  
> +obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash.o
> +
>  obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
>  obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
>  obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o
> diff --git a/drivers/media/v4l2-core/v4l2-flash.c b/drivers/media/v4l2-core/v4l2-flash.c
> new file mode 100644
> index 0000000..804c2e4
> --- /dev/null
> +++ b/drivers/media/v4l2-core/v4l2-flash.c
> @@ -0,0 +1,607 @@
> +/*
> + * 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.h>
> +#include "../../leds/leds.h"

What do you need from leds.h? Shouldn't this be e.g. under include/linux
instead?

> +#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)
> +
> +static enum led_brightness __intensity_to_led_brightness(
> +					struct v4l2_ctrl *ctrl,
> +					s32 intensity)
> +{
> +	s64 intensity64 = intensity - ctrl->minimum;
> +
> +	do_div(intensity64, 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->id != V4L2_CID_FLASH_INDICATOR_INTENSITY)
> +		++intensity64;
> +
> +	return intensity64;
> +}
> +
> +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] &&
> +	    ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
> +		return;
> +
> +	led_set_brightness(&v4l2_flash->fled_cdev->led_cdev, brightness);
> +}
> +
> +static int v4l2_flash_update_led_brightness(struct v4l2_flash *v4l2_flash,
> +					struct v4l2_ctrl *ctrl)
> +{
> +	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;
> +	int ret;
> +
> +	/*
> +	 * 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 (ctrl == ctrls[TORCH_INTENSITY] &&
> +	    ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
> +		return 0;
> +
> +	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 */
> +		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 */
> +		return led_set_flash_timeout(fled_cdev, c->val);
> +	case V4L2_CID_FLASH_INTENSITY:
> +		/* no conversion is needed */
> +		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 fill_ctrl_init_data(struct v4l2_flash *v4l2_flash,
> +			  struct v4l2_flash_ctrl_config *flash_ctrl_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_ctrl_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_ctrl_cfg->flash_faults;
> +		ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
> +				  V4L2_CTRL_FLAG_READ_ONLY;
> +	}
> +
> +	/* Init INDICATOR_INTENSITY ctrl data */
> +	if (flash_ctrl_cfg->indicator_led) {
> +		ctrl_init_data[INDICATOR_INTENSITY].cid =
> +					V4L2_CID_FLASH_INDICATOR_INTENSITY;
> +		ctrl_init_data[INDICATOR_INTENSITY].config =
> +						flash_ctrl_cfg->intensity;
> +		ctrl_cfg = &ctrl_init_data[INDICATOR_INTENSITY].config;
> +		ctrl_cfg->id = V4L2_CID_FLASH_INDICATOR_INTENSITY;
> +		ctrl_cfg->min = 0;
> +		ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
> +
> +		/* Indicator LED can have only faults and intensity controls. */
> +		return;
> +	}
> +
> +	/* 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_init_data[TORCH_INTENSITY].config = flash_ctrl_cfg->intensity;
> +	ctrl_cfg = &ctrl_init_data[TORCH_INTENSITY].config;
> +	ctrl_cfg->id = V4L2_CID_FLASH_TORCH_INTENSITY;
> +	ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
> +
> +	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_ctrl_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_init_data[FLASH_TIMEOUT].config =
> +					flash_ctrl_cfg->flash_timeout;
> +		ctrl_cfg = &ctrl_init_data[FLASH_TIMEOUT].config;
> +		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_init_data[FLASH_INTENSITY].config =
> +					flash_ctrl_cfg->flash_intensity;
> +		ctrl_cfg = &ctrl_init_data[FLASH_INTENSITY].config;
> +		ctrl_cfg->id = V4L2_CID_FLASH_INTENSITY;
> +		ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
> +	}
> +}
> +
> +static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash,
> +				struct v4l2_flash_ctrl_config *flash_ctrl_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;
> +
> +	/* 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_ctrl_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;
> +
> +	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);
> +
> +	if (ctrls[INDICATOR_INTENSITY])
> +		v4l2_flash_set_led_brightness(v4l2_flash,
> +						ctrls[INDICATOR_INTENSITY]);
> +	else
> +		v4l2_flash_set_led_brightness(v4l2_flash,
> +						ctrls[TORCH_INTENSITY]);
> +
> +	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;
> +	int ret = 0;

No need to initialise ret.

> +
> +	mutex_lock(&led_cdev->led_access);
> +
> +	if (!v4l2_fh_is_singular(&fh->vfh)) {

I'm not sure about this --- no current flash driver AFAIK provides exclusive
access.

> +		ret = -EBUSY;
> +		goto unlock;
> +	}
> +
> +	led_sysfs_disable(led_cdev);
> +	led_trigger_remove(led_cdev);
> +
> +	ret = __sync_device_with_v4l2_controls(v4l2_flash);
> +
> +unlock:
> +	mutex_unlock(&led_cdev->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;
> +	int ret = 0;

ret is redundant.

> +
> +	mutex_lock(&led_cdev->led_access);
> +
> +	if (v4l2_flash->ctrls[STROBE_SOURCE])
> +		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);
> +
> +	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 led_classdev_flash *fled_cdev,
> +				   const struct v4l2_flash_ops *ops,
> +				   struct v4l2_flash_ctrl_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->ops = ops;
> +	sd->dev = led_cdev->dev;
> +	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)

You're missing media_entity_cleanup() here. How about adding a new label
below for that?

> +		return ERR_PTR(ret);
> +
> +	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);
> +	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 = &v4l2_flash->sd;
> +	struct led_classdev *led_cdev = &v4l2_flash->fled_cdev->led_cdev;
> +
> +	v4l2_async_unregister_subdev(sd);
> +	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.h b/include/media/v4l2-flash.h
> new file mode 100644
> index 0000000..cc0138d
> --- /dev/null
> +++ b/include/media/v4l2-flash.h
> @@ -0,0 +1,145 @@
> +/*
> + * 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;
> +
> +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,
> +};
> +
> +/*
> + * 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_ctrl_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_ctrl_config - V4L2 Flash controls initialization data

This is a bit more than just about controls. How about dropping "_ctrl" from
the name?

> + * @dev_name:			human readable device name

Could you use "the name of the media entity, unique in the system" instead,
please?

> + * @intensity:			constraints for the led in a non-flash mode
> + * @flash_intensity:		V4L2_CID_FLASH_INTENSITY constraints
> + * @flash_timeout:		V4L2_CID_FLASH_TIMEOUT constraints
> + * @flash_faults:		possible flash faults

Please refer to documentation 

> + * @has_external_strobe:	external strobe capability
> + * @indicator_led:		signifies that a led is of indicator type
> + */
> +struct v4l2_flash_ctrl_config {
> +	char dev_name[32];
> +	struct v4l2_ctrl_config intensity;
> +	struct v4l2_ctrl_config flash_intensity;
> +	struct v4l2_ctrl_config flash_timeout;

I think I may have suggested using v4l2_ctrl_config here, but currently the
drivers need to copy the values to v4l2_ctrl_config struct, instead of using
struct led_flash_setting which they have to use anyway.

What would you think it'd be feasible to use led_flash_setting here as well?

> +	u32 flash_faults;
> +	unsigned int has_external_strobe:1;
> +	unsigned int indicator_led:1;
> +};
> +
> +/**
> + * struct v4l2_flash - Flash sub-device context
> + * @fled_cdev:		LED Flash class device controlled by this sub-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;
> +	const struct v4l2_flash_ops *ops;
> +
> +	struct v4l2_subdev sd;
> +	struct v4l2_ctrl_handler hdl;
> +	struct v4l2_ctrl *ctrls[STROBE_SOURCE + 1];
> +};
> +
> +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
> + * @fled_cdev:	the LED Flash class device to wrap
> + * @flash_ops:	V4L2 Flash device ops
> + * @config:	initialization data for V4L2 Flash controls
> + *
> + * 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 led_classdev_flash *fled_cdev,
> +				   const struct v4l2_flash_ops *ops,
> +				   struct v4l2_flash_ctrl_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
> +#define v4l2_flash_init(fled_cdev, ops, config) (NULL)
> +#define v4l2_flash_release(v4l2_flash)
> +#endif /* CONFIG_V4L2_FLASH_LED_CLASS */
> +
> +#endif /* _V4L2_FLASH_H */

-- 
Kind regards,

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

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

* Re: [PATCH v1 10/11] leds: max77693: add support for V4L2 Flash sub-device
  2015-03-20 15:03 ` [PATCH v1 10/11] leds: max77693: add support for V4L2 Flash sub-device Jacek Anaszewski
@ 2015-03-22  0:28   ` Sakari Ailus
  0 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2015-03-22  0:28 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki

Hi Jacek,

On Fri, Mar 20, 2015 at 04:03:30PM +0100, Jacek Anaszewski 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.
> 
> 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 |  149 +++++++++++++++++++++++++++++++++++++++---
>  1 file changed, 141 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/leds/leds-max77693.c b/drivers/leds/leds-max77693.c
> index 7386d69..a12bd8c 100644
> --- a/drivers/leds/leds-max77693.c
> +++ b/drivers/leds/leds-max77693.c
> @@ -21,6 +21,7 @@
>  #include <linux/regmap.h>
>  #include <linux/slab.h>
>  #include <linux/workqueue.h>
> +#include <media/v4l2-flash.h>
>  
>  #define MODE_OFF		0
>  #define MODE_FLASH(a)		(1 << (a))
> @@ -68,6 +69,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;
> @@ -651,7 +654,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;
> @@ -697,6 +701,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;
>  
>  		of_property_read_string(child_node, "label",
> @@ -784,11 +795,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;
>  
> @@ -855,9 +867,86 @@ static const char *max77693_get_led_name(struct max77693_led_device *led,
>  					     MAX77693_LED2_NAME;
>  }
>  
> +#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_ctrl_config(struct max77693_led_device *led,
> +					int fled_id,
> +					struct max77693_led_settings *s,
> +					struct v4l2_flash_ctrl_config *config)
> +{
> +	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 *setting;
> +	struct v4l2_ctrl_config *c;
> +
> +	snprintf(config->dev_name, sizeof(config->dev_name),
> +		 "%s %d-%04x", max77693_get_led_name(led, fled_id),
> +		 i2c_adapter_id(i2c->adapter), i2c->addr);

Looks good!

> +
> +	c = &config->intensity;
> +	setting = &s->torch_brightness;
> +	c->min = setting->min;
> +	c->max = setting->max;
> +	c->step = setting->step;
> +	c->def = setting->val;
> +
> +	c = &config->flash_intensity;
> +	setting = &s->flash_brightness;
> +	c->min = setting->min;
> +	c->max = setting->max;
> +	c->step = setting->step;
> +	c->def = setting->val;
> +
> +	c = &config->flash_timeout;
> +	setting = &s->flash_timeout;
> +	c->min = setting->min;
> +	c->max = setting->max;
> +	c->step = setting->step;
> +	c->def = setting->val;

You could create a function for this. Up to you. But more importantly,
please see my comment on patch 7 regarding the use of v4l2_ctrl_config
first.

> +	/* Init flash faults config */
> +	config->flash_faults =	V4L2_FLASH_FAULT_OVER_VOLTAGE |
> +				V4L2_FLASH_FAULT_SHORT_CIRCUIT |
> +				V4L2_FLASH_FAULT_OVER_CURRENT;
> +
> +	config->has_external_strobe = true;
> +}
> +
> +static const struct v4l2_flash_ops v4l2_flash_ops = {
> +	.external_strobe_set = max77693_led_external_strobe_set,
> +};
> +
> +#else
> +#define max77693_init_v4l2_ctrl_config(led, fled_id, s, config)
> +#endif
> +
>  static void max77693_init_fled_cdev(struct max77693_led_device *led,
>  				int fled_id,
> -				struct max77693_led_config_data *cfg)
> +				struct max77693_led_config_data *cfg,
> +				struct v4l2_flash_ctrl_config *v4l2_flash_cfg)
>  {
>  	struct led_classdev_flash *fled_cdev;
>  	struct led_classdev *led_cdev;
> @@ -866,6 +955,8 @@ static void max77693_init_fled_cdev(struct max77693_led_device *led,
>  
>  	/* Initialize flash settings */
>  	max77693_init_flash_settings(led, fled_id, cfg, &settings);
> +	/* Initialize V4L2 Flash config basing on initialized settings */
> +	max77693_init_v4l2_ctrl_config(led, fled_id, &settings, v4l2_flash_cfg);
>  
>  	/* Initialize LED Flash class device */
>  	fled_cdev = &sub_led->fled_cdev;
> @@ -891,15 +982,51 @@ static void max77693_init_fled_cdev(struct max77693_led_device *led,
>  	sub_led->flash_timeout = fled_cdev->timeout.val;
>  }
>  
> +static int max77693_register_led(struct max77693_sub_led *sub_led,
> +				 struct v4l2_flash_ctrl_config *v4l2_flash_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;
> +	int ret;
> +
> +	/* Register in the LED subsystem */
> +	ret = led_classdev_flash_register(dev, fled_cdev);
> +	if (ret < 0)
> +		return ret;
> +
> +	fled_cdev->led_cdev.dev->of_node = sub_node;
> +
> +	/* Register in the V4L2 subsystem. */
> +	sub_led->v4l2_flash = v4l2_flash_init(fled_cdev, &v4l2_flash_ops,
> +						v4l2_flash_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:
> +	of_node_put(sub_node);

I wonder if this is where it belongs or if I miss something here.

> +	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] = { NULL, NULL };

{} would do. { 0 } is actually what you should use, but gcc sometimes choces
on that (it's a gcc bug).

>  	struct max77693_led_config_data cfg = {};
> +	struct v4l2_flash_ctrl_config v4l2_flash_config[2];
>  	int init_fled_cdev[2], i, ret;
>  
> +	memset(v4l2_flash_config, 0, sizeof(v4l2_flash_config));

You could initialise v4l2_flash_config to zero by assigning { 0 } to it in
the initialiser, as you do with other variables already.

> +
>  	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
>  	if (!led)
>  		return -ENOMEM;
> @@ -910,7 +1037,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, &cfg);
> +	ret = max77693_led_get_configuration(led, &cfg, sub_nodes);
>  	if (ret < 0)
>  		return ret;
>  
> @@ -926,15 +1053,18 @@ static int max77693_led_probe(struct platform_device *pdev)
>  	/* Initialize LED Flash class device(s) */
>  	for (i = FLED1; i <= FLED2; ++i)
>  		if (init_fled_cdev[i])
> -			max77693_init_fled_cdev(led, i, &cfg);
> +			max77693_init_fled_cdev(led, i, &cfg,
> +						&v4l2_flash_config[i]);
>  	mutex_init(&led->lock);
>  
> -	/* Register LED Flash class device(s) */
> +	/* Register LED Flash class and related V4L2 Flash device(s) */
>  	for (i = FLED1; i <= FLED2; ++i) {
>  		if (!init_fled_cdev[i])
>  			continue;
>  
> -		ret = led_classdev_flash_register(dev, &sub_leds[i].fled_cdev);
> +		ret = max77693_register_led(&sub_leds[i],
> +					    &v4l2_flash_config[i],
> +					    sub_nodes[i]);
>  		if (ret < 0) {
>  			/*
>  			 * At this moment FLED1 might have been already
> @@ -953,6 +1083,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);
> @@ -966,11 +1097,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);
>  	}

-- 
Kind regards,

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

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

* Re: [PATCH v1 06/11] exynos4-is: Add support for v4l2-flash subdevs
  2015-03-20 15:03     ` Jacek Anaszewski
@ 2015-03-22  1:21         ` Sakari Ailus
  -1 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2015-03-22  1:21 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds-u79uwXL29TY76Z2rM5mHXA,
	linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ, pavel-+ZI9xUNit7I,
	cooloney-Re5JQEeQqe8AvxtiuMwx3w, rpurdie-Fm38FmjxZ/leoWH0uzbU5w,
	s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ

Hi Jacek,

Some comments below. Please also get an ack from Sylwester! :-)

On Fri, Mar 20, 2015 at 04:03:26PM +0100, Jacek Anaszewski wrote:
> This patch adds support for external v4l2-flash devices.
> The support includes parsing "flashes" DT property
> and asynchronous subdevice registration.
> 
> Signed-off-by: Jacek Anaszewski <j.anaszewski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> Acked-by: Kyungmin Park <kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> Cc: Sylwester Nawrocki <s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> ---
>  drivers/media/platform/exynos4-is/media-dev.c |   36 +++++++++++++++++++++++--
>  drivers/media/platform/exynos4-is/media-dev.h |   13 ++++++++-
>  2 files changed, 46 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
> index f315ef9..8dd0e5d 100644
> --- a/drivers/media/platform/exynos4-is/media-dev.c
> +++ b/drivers/media/platform/exynos4-is/media-dev.c
> @@ -451,6 +451,25 @@ rpm_put:
>  	return ret;
>  }
>  
> +static void fimc_md_register_flash_entities(struct fimc_md *fmd)
> +{
> +	struct device_node *parent = fmd->pdev->dev.of_node;
> +	struct device_node *np;
> +	int i = 0;
> +
> +	do {
> +		np = of_parse_phandle(parent, "flashes", i);
> +		if (np) {

if (!np)
	break;

And you can remove checking np another time in the loop condition.

> +			fmd->flash[fmd->num_flashes].asd.match_type =
> +							V4L2_ASYNC_MATCH_OF;
> +			fmd->flash[fmd->num_flashes].asd.match.of.node = np;
> +			fmd->num_flashes++;
> +			fmd->async_subdevs[fmd->num_sensors + i] =
> +						&fmd->flash[i].asd;

Have all the sensors been already registered by this point?

> +		}
> +	} while (np && (++i < FIMC_MAX_FLASHES));

How about instead:

fmd->num_flashes < FIMC_MAX_FLASHES

And drop i. Also move incrementing num_flashes as last in the if.

> +}
> +
>  static int __of_get_csis_id(struct device_node *np)
>  {
>  	u32 reg = 0;
> @@ -1275,6 +1294,15 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
>  	struct fimc_sensor_info *si = NULL;
>  	int i;
>  
> +	/* Register flash subdev if detected any */
> +	for (i = 0; i < ARRAY_SIZE(fmd->flash); i++) {
> +		if (fmd->flash[i].asd.match.of.node == subdev->dev->of_node) {
> +			fmd->flash[i].subdev = subdev;
> +			fmd->num_flashes++;
> +			return 0;
> +		}
> +	}
> +
>  	/* Find platform data for this sensor subdev */
>  	for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++)
>  		if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node)
> @@ -1385,6 +1413,8 @@ static int fimc_md_probe(struct platform_device *pdev)
>  		goto err_m_ent;
>  	}
>  
> +	fimc_md_register_flash_entities(fmd);
> +
>  	mutex_unlock(&fmd->media_dev.graph_mutex);
>  
>  	ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
> @@ -1401,12 +1431,14 @@ static int fimc_md_probe(struct platform_device *pdev)
>  		goto err_attr;
>  	}
>  
> -	if (fmd->num_sensors > 0) {
> +	if (fmd->num_sensors > 0 || fmd->num_flashes > 0) {
>  		fmd->subdev_notifier.subdevs = fmd->async_subdevs;
> -		fmd->subdev_notifier.num_subdevs = fmd->num_sensors;
> +		fmd->subdev_notifier.num_subdevs = fmd->num_sensors +
> +							fmd->num_flashes;
>  		fmd->subdev_notifier.bound = subdev_notifier_bound;
>  		fmd->subdev_notifier.complete = subdev_notifier_complete;
>  		fmd->num_sensors = 0;
> +		fmd->num_flashes = 0;
>  
>  		ret = v4l2_async_notifier_register(&fmd->v4l2_dev,
>  						&fmd->subdev_notifier);
> diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
> index 0321454..feff9c8 100644
> --- a/drivers/media/platform/exynos4-is/media-dev.h
> +++ b/drivers/media/platform/exynos4-is/media-dev.h
> @@ -34,6 +34,8 @@
>  
>  #define FIMC_MAX_SENSORS	4
>  #define FIMC_MAX_CAMCLKS	2
> +#define FIMC_MAX_FLASHES	2
> +#define FIMC_MAX_ASYNC_SUBDEVS (FIMC_MAX_SENSORS + FIMC_MAX_FLASHES)
>  #define DEFAULT_SENSOR_CLK_FREQ	24000000U
>  
>  /* LCD/ISP Writeback clocks (PIXELASYNCMx) */
> @@ -93,6 +95,11 @@ struct fimc_sensor_info {
>  	struct fimc_dev *host;
>  };
>  
> +struct fimc_flash_info {
> +	struct v4l2_subdev *subdev;
> +	struct v4l2_async_subdev asd;
> +};
> +
>  struct cam_clk {
>  	struct clk_hw hw;
>  	struct fimc_md *fmd;
> @@ -104,6 +111,8 @@ struct cam_clk {
>   * @csis: MIPI CSIS subdevs data
>   * @sensor: array of registered sensor subdevs
>   * @num_sensors: actual number of registered sensors
> + * @flash: array of registered flash subdevs
> + * @num_flashes: actual number of registered flashes
>   * @camclk: external sensor clock information
>   * @fimc: array of registered fimc devices
>   * @fimc_is: fimc-is data structure
> @@ -123,6 +132,8 @@ struct fimc_md {
>  	struct fimc_csis_info csis[CSIS_MAX_ENTITIES];
>  	struct fimc_sensor_info sensor[FIMC_MAX_SENSORS];
>  	int num_sensors;
> +	struct fimc_flash_info flash[FIMC_MAX_FLASHES];
> +	int num_flashes;
>  	struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS];
>  	struct clk *wbclk[FIMC_MAX_WBCLKS];
>  	struct fimc_lite *fimc_lite[FIMC_LITE_MAX_DEVS];
> @@ -149,7 +160,7 @@ struct fimc_md {
>  	} clk_provider;
>  
>  	struct v4l2_async_notifier subdev_notifier;
> -	struct v4l2_async_subdev *async_subdevs[FIMC_MAX_SENSORS];
> +	struct v4l2_async_subdev *async_subdevs[FIMC_MAX_ASYNC_SUBDEVS];
>  
>  	bool user_subdev_api;
>  	spinlock_t slock;

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus-X3B1VOXEql0@public.gmane.org	XMPP: sailus-PCDdDYkjdNMDXYZnReoRVg@public.gmane.org
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH v1 06/11] exynos4-is: Add support for v4l2-flash subdevs
@ 2015-03-22  1:21         ` Sakari Ailus
  0 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2015-03-22  1:21 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki

Hi Jacek,

Some comments below. Please also get an ack from Sylwester! :-)

On Fri, Mar 20, 2015 at 04:03:26PM +0100, Jacek Anaszewski wrote:
> This patch adds support for external v4l2-flash devices.
> The support includes parsing "flashes" DT property
> and asynchronous subdevice 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 |   36 +++++++++++++++++++++++--
>  drivers/media/platform/exynos4-is/media-dev.h |   13 ++++++++-
>  2 files changed, 46 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
> index f315ef9..8dd0e5d 100644
> --- a/drivers/media/platform/exynos4-is/media-dev.c
> +++ b/drivers/media/platform/exynos4-is/media-dev.c
> @@ -451,6 +451,25 @@ rpm_put:
>  	return ret;
>  }
>  
> +static void fimc_md_register_flash_entities(struct fimc_md *fmd)
> +{
> +	struct device_node *parent = fmd->pdev->dev.of_node;
> +	struct device_node *np;
> +	int i = 0;
> +
> +	do {
> +		np = of_parse_phandle(parent, "flashes", i);
> +		if (np) {

if (!np)
	break;

And you can remove checking np another time in the loop condition.

> +			fmd->flash[fmd->num_flashes].asd.match_type =
> +							V4L2_ASYNC_MATCH_OF;
> +			fmd->flash[fmd->num_flashes].asd.match.of.node = np;
> +			fmd->num_flashes++;
> +			fmd->async_subdevs[fmd->num_sensors + i] =
> +						&fmd->flash[i].asd;

Have all the sensors been already registered by this point?

> +		}
> +	} while (np && (++i < FIMC_MAX_FLASHES));

How about instead:

fmd->num_flashes < FIMC_MAX_FLASHES

And drop i. Also move incrementing num_flashes as last in the if.

> +}
> +
>  static int __of_get_csis_id(struct device_node *np)
>  {
>  	u32 reg = 0;
> @@ -1275,6 +1294,15 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
>  	struct fimc_sensor_info *si = NULL;
>  	int i;
>  
> +	/* Register flash subdev if detected any */
> +	for (i = 0; i < ARRAY_SIZE(fmd->flash); i++) {
> +		if (fmd->flash[i].asd.match.of.node == subdev->dev->of_node) {
> +			fmd->flash[i].subdev = subdev;
> +			fmd->num_flashes++;
> +			return 0;
> +		}
> +	}
> +
>  	/* Find platform data for this sensor subdev */
>  	for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++)
>  		if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node)
> @@ -1385,6 +1413,8 @@ static int fimc_md_probe(struct platform_device *pdev)
>  		goto err_m_ent;
>  	}
>  
> +	fimc_md_register_flash_entities(fmd);
> +
>  	mutex_unlock(&fmd->media_dev.graph_mutex);
>  
>  	ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
> @@ -1401,12 +1431,14 @@ static int fimc_md_probe(struct platform_device *pdev)
>  		goto err_attr;
>  	}
>  
> -	if (fmd->num_sensors > 0) {
> +	if (fmd->num_sensors > 0 || fmd->num_flashes > 0) {
>  		fmd->subdev_notifier.subdevs = fmd->async_subdevs;
> -		fmd->subdev_notifier.num_subdevs = fmd->num_sensors;
> +		fmd->subdev_notifier.num_subdevs = fmd->num_sensors +
> +							fmd->num_flashes;
>  		fmd->subdev_notifier.bound = subdev_notifier_bound;
>  		fmd->subdev_notifier.complete = subdev_notifier_complete;
>  		fmd->num_sensors = 0;
> +		fmd->num_flashes = 0;
>  
>  		ret = v4l2_async_notifier_register(&fmd->v4l2_dev,
>  						&fmd->subdev_notifier);
> diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
> index 0321454..feff9c8 100644
> --- a/drivers/media/platform/exynos4-is/media-dev.h
> +++ b/drivers/media/platform/exynos4-is/media-dev.h
> @@ -34,6 +34,8 @@
>  
>  #define FIMC_MAX_SENSORS	4
>  #define FIMC_MAX_CAMCLKS	2
> +#define FIMC_MAX_FLASHES	2
> +#define FIMC_MAX_ASYNC_SUBDEVS (FIMC_MAX_SENSORS + FIMC_MAX_FLASHES)
>  #define DEFAULT_SENSOR_CLK_FREQ	24000000U
>  
>  /* LCD/ISP Writeback clocks (PIXELASYNCMx) */
> @@ -93,6 +95,11 @@ struct fimc_sensor_info {
>  	struct fimc_dev *host;
>  };
>  
> +struct fimc_flash_info {
> +	struct v4l2_subdev *subdev;
> +	struct v4l2_async_subdev asd;
> +};
> +
>  struct cam_clk {
>  	struct clk_hw hw;
>  	struct fimc_md *fmd;
> @@ -104,6 +111,8 @@ struct cam_clk {
>   * @csis: MIPI CSIS subdevs data
>   * @sensor: array of registered sensor subdevs
>   * @num_sensors: actual number of registered sensors
> + * @flash: array of registered flash subdevs
> + * @num_flashes: actual number of registered flashes
>   * @camclk: external sensor clock information
>   * @fimc: array of registered fimc devices
>   * @fimc_is: fimc-is data structure
> @@ -123,6 +132,8 @@ struct fimc_md {
>  	struct fimc_csis_info csis[CSIS_MAX_ENTITIES];
>  	struct fimc_sensor_info sensor[FIMC_MAX_SENSORS];
>  	int num_sensors;
> +	struct fimc_flash_info flash[FIMC_MAX_FLASHES];
> +	int num_flashes;
>  	struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS];
>  	struct clk *wbclk[FIMC_MAX_WBCLKS];
>  	struct fimc_lite *fimc_lite[FIMC_LITE_MAX_DEVS];
> @@ -149,7 +160,7 @@ struct fimc_md {
>  	} clk_provider;
>  
>  	struct v4l2_async_notifier subdev_notifier;
> -	struct v4l2_async_subdev *async_subdevs[FIMC_MAX_SENSORS];
> +	struct v4l2_async_subdev *async_subdevs[FIMC_MAX_ASYNC_SUBDEVS];
>  
>  	bool user_subdev_api;
>  	spinlock_t slock;

-- 
Kind regards,

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

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

* Re: [PATCH v1 02/11] DT: Add documentation for the mfd Maxim max77693
  2015-03-21 22:49   ` Sakari Ailus
@ 2015-03-23  9:54     ` Jacek Anaszewski
  2015-03-25  0:55       ` Sakari Ailus
  0 siblings, 1 reply; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-23  9:54 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki, Andrzej Hajda, Lee Jones,
	Chanwoo Choi

Hi Sakari,

On 03/21/2015 11:49 PM, Sakari Ailus wrote:
> Hi Jacek,
>
> On Fri, Mar 20, 2015 at 04:03:22PM +0100, Jacek Anaszewski wrote:
>> +Optional properties of the LED child node:
>> +- label : see Documentation/devicetree/bindings/leds/common.txt
>
> I'm still not comfortable using the label field as-is as the entity name in
> the later patches, there's one important problem: it is not guaranteed to be
> unique in the system.

I don't use it as-is in my patches. For max77603-led the i2c adapter id
and client address is added to it, and for aat1290 there is '_n' suffix
added. Nonetheless I didn't notice that the patch [1] was already
merged. It checks if a LED class device with given name isn't already
registered and adds a '_n" suffix if there was any. If it was exported
I could use it in the leds-aat1290 driver and avoid depending on the
static variable.

Whereas for I2C devices the problem doesn't exist (it is guaranteed that
no more than one I2C client with an address can be present on the
same bus), for devices driven through GPIOs we haven't stable unique
identifier.

I thought that we agreed on #v4l about adding numerical postfixes
in case of such devices.

> Do you think this could be added to
> Documentation/devicetree/bindings/leds/common.txt, with perhaps enforcing it
> in the LED framework? Bryan, what do you think?

The patch [1] seems to address the issue.

> The alternative would be to simply ignore it in the entity name, but then
> the name of the device would be different in the LED framework and Media
> controller.
>

This is the case currently - the names are different. The post fixes
are added only to media entity name. Perhaps they should be unified.


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

-- 
Best Regards,
Jacek Anaszewski

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

* Re: [PATCH v1 01/11] leds: Add support for max77693 mfd flash cell
  2015-03-21 22:44   ` Sakari Ailus
@ 2015-03-23 13:22     ` Jacek Anaszewski
  0 siblings, 0 replies; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-23 13:22 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki, Andrzej Hajda, Lee Jones,
	Chanwoo Choi

Hi Sakari,

Thanks for the review.

On 03/21/2015 11:44 PM, Sakari Ailus wrote:
> Hi Jacek,
>
> On Fri, Mar 20, 2015 at 04:03:21PM +0100, Jacek Anaszewski wrote:
>> This patch adds led-flash support to Maxim max77693 chipset.
>> A device can be exposed to user space through LED subsystem
>> sysfs interface. Device supports up to two leds which can
>> work in flash and torch mode. The leds can be triggered
>> externally or by software.
>>
>> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
>> Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
>> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
>> Cc: Bryan Wu <cooloney@gmail.com>
>> Cc: Richard Purdie <rpurdie@rpsys.net>
>> Cc: Lee Jones <lee.jones@linaro.org>
>> Cc: Chanwoo Choi <cw00.choi@samsung.com>
>
> Thanks for the update once again!
>
> Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
>

There will be one more version of this patch due to some changes
around flash settings and v4l2-flash config initialization, requested
in the review of v4l2-flash helpers related patches.

-- 
Best Regards,
Jacek Anaszewski

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

* Re: [PATCH v1 07/11] media: Add registration helpers for V4L2 flash sub-devices
  2015-03-22  0:22   ` Sakari Ailus
@ 2015-03-23 15:08     ` Jacek Anaszewski
  2015-03-23 22:35       ` Sakari Ailus
  0 siblings, 1 reply; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-23 15:08 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki, Hans Verkuil

Hi Sakari,

Thanks for the review.

On 03/22/2015 01:22 AM, Sakari Ailus wrote:
> Hi Jacek,
>
> Thanks for the updated set. Some comments below.
>
> On Fri, Mar 20, 2015 at 04:03:27PM +0100, Jacek Anaszewski 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.
>>
>> 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      |   12 +
>>   drivers/media/v4l2-core/Makefile     |    2 +
>>   drivers/media/v4l2-core/v4l2-flash.c |  607 ++++++++++++++++++++++++++++++++++
>>   include/media/v4l2-flash.h           |  145 ++++++++
>>   4 files changed, 766 insertions(+)
>>   create mode 100644 drivers/media/v4l2-core/v4l2-flash.c
>>   create mode 100644 include/media/v4l2-flash.h
>>
>> diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
>> index ba7e21a..0659598 100644
>> --- a/drivers/media/v4l2-core/Kconfig
>> +++ b/drivers/media/v4l2-core/Kconfig
>> @@ -44,6 +44,18 @@ config V4L2_MEM2MEM_DEV
>>           tristate
>>           depends on VIDEOBUF2_CORE
>>
>> +# Used by LED subsystem flash drivers
>> +config V4L2_FLASH_LED_CLASS
>> +	tristate "Enable support for Flash sub-devices"
>> +	depends on VIDEO_V4L2_SUBDEV_API
>> +	depends on LEDS_CLASS_FLASH
>> +	depends on OF
>
> I think you can drop OF dependency. A no-op implementation will be used if
> it's disabled.

The sub-devices are matched by a sub-LED's device_node. This is the only
way of matching the v4l2-flash sub-devs. Without OF a v4l2-flash
sub-device will not get registered at all.

What do you mean by "no-op implementation"?

>> +	---help---
>> +	  Say Y here to enable support for Flash sub-devices, which allow
>> +	  to control LED class devices with use of V4L2 Flash controls.
>> +
>> +	  When in doubt, say N.
>> +
>>   # Used by drivers that need Videobuf modules
>>   config VIDEOBUF_GEN
>>   	tristate
>> diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
>> index 63d29f2..44e858c 100644
>> --- a/drivers/media/v4l2-core/Makefile
>> +++ b/drivers/media/v4l2-core/Makefile
>> @@ -22,6 +22,8 @@ obj-$(CONFIG_VIDEO_TUNER) += tuner.o
>>
>>   obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
>>
>> +obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash.o
>> +
>>   obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
>>   obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
>>   obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o
>> diff --git a/drivers/media/v4l2-core/v4l2-flash.c b/drivers/media/v4l2-core/v4l2-flash.c
>> new file mode 100644
>> index 0000000..804c2e4
>> --- /dev/null
>> +++ b/drivers/media/v4l2-core/v4l2-flash.c
>> @@ -0,0 +1,607 @@
>> +/*
>> + * 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.h>
>> +#include "../../leds/leds.h"
>
> What do you need from leds.h? Shouldn't this be e.g. under include/linux
> instead?
>
>> +#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)
>> +
>> +static enum led_brightness __intensity_to_led_brightness(
>> +					struct v4l2_ctrl *ctrl,
>> +					s32 intensity)
>> +{
>> +	s64 intensity64 = intensity - ctrl->minimum;
>> +
>> +	do_div(intensity64, 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->id != V4L2_CID_FLASH_INDICATOR_INTENSITY)
>> +		++intensity64;
>> +
>> +	return intensity64;
>> +}
>> +
>> +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] &&
>> +	    ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
>> +		return;
>> +
>> +	led_set_brightness(&v4l2_flash->fled_cdev->led_cdev, brightness);
>> +}
>> +
>> +static int v4l2_flash_update_led_brightness(struct v4l2_flash *v4l2_flash,
>> +					struct v4l2_ctrl *ctrl)
>> +{
>> +	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;
>> +	int ret;
>> +
>> +	/*
>> +	 * 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 (ctrl == ctrls[TORCH_INTENSITY] &&
>> +	    ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
>> +		return 0;
>> +
>> +	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 */
>> +		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 */
>> +		return led_set_flash_timeout(fled_cdev, c->val);
>> +	case V4L2_CID_FLASH_INTENSITY:
>> +		/* no conversion is needed */
>> +		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 fill_ctrl_init_data(struct v4l2_flash *v4l2_flash,
>> +			  struct v4l2_flash_ctrl_config *flash_ctrl_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_ctrl_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_ctrl_cfg->flash_faults;
>> +		ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
>> +				  V4L2_CTRL_FLAG_READ_ONLY;
>> +	}
>> +
>> +	/* Init INDICATOR_INTENSITY ctrl data */
>> +	if (flash_ctrl_cfg->indicator_led) {
>> +		ctrl_init_data[INDICATOR_INTENSITY].cid =
>> +					V4L2_CID_FLASH_INDICATOR_INTENSITY;
>> +		ctrl_init_data[INDICATOR_INTENSITY].config =
>> +						flash_ctrl_cfg->intensity;
>> +		ctrl_cfg = &ctrl_init_data[INDICATOR_INTENSITY].config;
>> +		ctrl_cfg->id = V4L2_CID_FLASH_INDICATOR_INTENSITY;
>> +		ctrl_cfg->min = 0;
>> +		ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
>> +
>> +		/* Indicator LED can have only faults and intensity controls. */
>> +		return;
>> +	}
>> +
>> +	/* 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_init_data[TORCH_INTENSITY].config = flash_ctrl_cfg->intensity;
>> +	ctrl_cfg = &ctrl_init_data[TORCH_INTENSITY].config;
>> +	ctrl_cfg->id = V4L2_CID_FLASH_TORCH_INTENSITY;
>> +	ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
>> +
>> +	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_ctrl_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_init_data[FLASH_TIMEOUT].config =
>> +					flash_ctrl_cfg->flash_timeout;
>> +		ctrl_cfg = &ctrl_init_data[FLASH_TIMEOUT].config;
>> +		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_init_data[FLASH_INTENSITY].config =
>> +					flash_ctrl_cfg->flash_intensity;
>> +		ctrl_cfg = &ctrl_init_data[FLASH_INTENSITY].config;
>> +		ctrl_cfg->id = V4L2_CID_FLASH_INTENSITY;
>> +		ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
>> +	}
>> +}
>> +
>> +static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash,
>> +				struct v4l2_flash_ctrl_config *flash_ctrl_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;
>> +
>> +	/* 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_ctrl_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;
>> +
>> +	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);
>> +
>> +	if (ctrls[INDICATOR_INTENSITY])
>> +		v4l2_flash_set_led_brightness(v4l2_flash,
>> +						ctrls[INDICATOR_INTENSITY]);
>> +	else
>> +		v4l2_flash_set_led_brightness(v4l2_flash,
>> +						ctrls[TORCH_INTENSITY]);
>> +
>> +	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;
>> +	int ret = 0;
>
> No need to initialise ret.
>
>> +
>> +	mutex_lock(&led_cdev->led_access);
>> +
>> +	if (!v4l2_fh_is_singular(&fh->vfh)) {
>
> I'm not sure about this --- no current flash driver AFAIK provides exclusive
> access.

v4l2_flash_open has a side effect in the form of writing device
registers with v4l2-flash controls state. Generally it should not
affect e.g. the flash strobe if it took place in the same moment.

What should be added, if we are to allow multiple users, is the
reference counter to be able to detect that last user is closing
a sub-dev and only then set the strobe source to software.

>> +		ret = -EBUSY;
>> +		goto unlock;
>> +	}
>> +
>> +	led_sysfs_disable(led_cdev);
>> +	led_trigger_remove(led_cdev);
>> +
>> +	ret = __sync_device_with_v4l2_controls(v4l2_flash);
>> +
>> +unlock:
>> +	mutex_unlock(&led_cdev->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;
>> +	int ret = 0;
>
> ret is redundant.

I think that I forgot to assign the v4l2_ctrl_s_ctrl result to it.

>> +
>> +	mutex_lock(&led_cdev->led_access);
>> +
>> +	if (v4l2_flash->ctrls[STROBE_SOURCE])
>> +		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);
>> +
>> +	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 led_classdev_flash *fled_cdev,
>> +				   const struct v4l2_flash_ops *ops,
>> +				   struct v4l2_flash_ctrl_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->ops = ops;
>> +	sd->dev = led_cdev->dev;
>> +	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)
>
> You're missing media_entity_cleanup() here. How about adding a new label
> below for that?

Right, thanks.

>> +		return ERR_PTR(ret);
>> +
>> +	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);
>> +	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 = &v4l2_flash->sd;
>> +	struct led_classdev *led_cdev = &v4l2_flash->fled_cdev->led_cdev;
>> +
>> +	v4l2_async_unregister_subdev(sd);
>> +	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.h b/include/media/v4l2-flash.h
>> new file mode 100644
>> index 0000000..cc0138d
>> --- /dev/null
>> +++ b/include/media/v4l2-flash.h
>> @@ -0,0 +1,145 @@
>> +/*
>> + * 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;
>> +
>> +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,
>> +};
>> +
>> +/*
>> + * 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_ctrl_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_ctrl_config - V4L2 Flash controls initialization data
>
> This is a bit more than just about controls. How about dropping "_ctrl" from
> the name?

Good point.

>> + * @dev_name:			human readable device name
>
> Could you use "the name of the media entity, unique in the system" instead,
> please?
>
>> + * @intensity:			constraints for the led in a non-flash mode
>> + * @flash_intensity:		V4L2_CID_FLASH_INTENSITY constraints
>> + * @flash_timeout:		V4L2_CID_FLASH_TIMEOUT constraints
>> + * @flash_faults:		possible flash faults
>
> Please refer to documentation

Do you want to put here whole description from documentation?
Or maybe only mention that faults can "prevent further use of some of
the flash controls"?

>
>> + * @has_external_strobe:	external strobe capability
>> + * @indicator_led:		signifies that a led is of indicator type
>> + */
>> +struct v4l2_flash_ctrl_config {
>> +	char dev_name[32];
>> +	struct v4l2_ctrl_config intensity;
>> +	struct v4l2_ctrl_config flash_intensity;
>> +	struct v4l2_ctrl_config flash_timeout;
>
> I think I may have suggested using v4l2_ctrl_config here, but currently the
> drivers need to copy the values to v4l2_ctrl_config struct, instead of using
> struct led_flash_setting which they have to use anyway.
>
> What would you think it'd be feasible to use led_flash_setting here as well?

I tried and it turned out that there was a room for even greater number
of optimizations around flash settings and config initialization.


>> +	u32 flash_faults;
>> +	unsigned int has_external_strobe:1;
>> +	unsigned int indicator_led:1;
>> +};
>> +
>> +/**
>> + * struct v4l2_flash - Flash sub-device context
>> + * @fled_cdev:		LED Flash class device controlled by this sub-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;
>> +	const struct v4l2_flash_ops *ops;
>> +
>> +	struct v4l2_subdev sd;
>> +	struct v4l2_ctrl_handler hdl;
>> +	struct v4l2_ctrl *ctrls[STROBE_SOURCE + 1];
>> +};
>> +
>> +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
>> + * @fled_cdev:	the LED Flash class device to wrap
>> + * @flash_ops:	V4L2 Flash device ops
>> + * @config:	initialization data for V4L2 Flash controls
>> + *
>> + * 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 led_classdev_flash *fled_cdev,
>> +				   const struct v4l2_flash_ops *ops,
>> +				   struct v4l2_flash_ctrl_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
>> +#define v4l2_flash_init(fled_cdev, ops, config) (NULL)
>> +#define v4l2_flash_release(v4l2_flash)
>> +#endif /* CONFIG_V4L2_FLASH_LED_CLASS */
>> +
>> +#endif /* _V4L2_FLASH_H */
>


-- 
Best Regards,
Jacek Anaszewski

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

* Re: [PATCH v1 06/11] exynos4-is: Add support for v4l2-flash subdevs
  2015-03-22  1:21         ` Sakari Ailus
  (?)
@ 2015-03-23 15:32         ` Jacek Anaszewski
  2015-03-23 22:39           ` Sakari Ailus
  -1 siblings, 1 reply; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-23 15:32 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki

Hi Sakari,

Thanks for the review.

On 03/22/2015 02:21 AM, Sakari Ailus wrote:
> Hi Jacek,
>
> Some comments below. Please also get an ack from Sylwester! :-)

No doubt about that :)

> On Fri, Mar 20, 2015 at 04:03:26PM +0100, Jacek Anaszewski wrote:
>> This patch adds support for external v4l2-flash devices.
>> The support includes parsing "flashes" DT property
>> and asynchronous subdevice 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 |   36 +++++++++++++++++++++++--
>>   drivers/media/platform/exynos4-is/media-dev.h |   13 ++++++++-
>>   2 files changed, 46 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
>> index f315ef9..8dd0e5d 100644
>> --- a/drivers/media/platform/exynos4-is/media-dev.c
>> +++ b/drivers/media/platform/exynos4-is/media-dev.c
>> @@ -451,6 +451,25 @@ rpm_put:
>>   	return ret;
>>   }
>>
>> +static void fimc_md_register_flash_entities(struct fimc_md *fmd)
>> +{
>> +	struct device_node *parent = fmd->pdev->dev.of_node;
>> +	struct device_node *np;
>> +	int i = 0;
>> +
>> +	do {
>> +		np = of_parse_phandle(parent, "flashes", i);
>> +		if (np) {
>
> if (!np)
> 	break;
>
> And you can remove checking np another time in the loop condition.

Thanks, this will be cleaner indeed.

>> +			fmd->flash[fmd->num_flashes].asd.match_type =
>> +							V4L2_ASYNC_MATCH_OF;
>> +			fmd->flash[fmd->num_flashes].asd.match.of.node = np;
>> +			fmd->num_flashes++;
>> +			fmd->async_subdevs[fmd->num_sensors + i] =
>> +						&fmd->flash[i].asd;
>
> Have all the sensors been already registered by this point?

Function fimc_md_register_sensor_entities is called before
this one.

>> +		}
>> +	} while (np && (++i < FIMC_MAX_FLASHES));
>
> How about instead:
>
> fmd->num_flashes < FIMC_MAX_FLASHES
>
> And drop i. Also move incrementing num_flashes as last in the if.

Dropping i will enforce referring to fmd->num_flashes 7 times
in this short fragment of code.
Maybe it would be better to use a pointer to it?
int *nf = &fmd=>num_flashes ?

>> +}
>> +
>>   static int __of_get_csis_id(struct device_node *np)
>>   {
>>   	u32 reg = 0;
>> @@ -1275,6 +1294,15 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
>>   	struct fimc_sensor_info *si = NULL;
>>   	int i;
>>
>> +	/* Register flash subdev if detected any */
>> +	for (i = 0; i < ARRAY_SIZE(fmd->flash); i++) {
>> +		if (fmd->flash[i].asd.match.of.node == subdev->dev->of_node) {
>> +			fmd->flash[i].subdev = subdev;
>> +			fmd->num_flashes++;
>> +			return 0;
>> +		}
>> +	}
>> +
>>   	/* Find platform data for this sensor subdev */
>>   	for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++)
>>   		if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node)
>> @@ -1385,6 +1413,8 @@ static int fimc_md_probe(struct platform_device *pdev)
>>   		goto err_m_ent;
>>   	}
>>
>> +	fimc_md_register_flash_entities(fmd);
>> +
>>   	mutex_unlock(&fmd->media_dev.graph_mutex);
>>
>>   	ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
>> @@ -1401,12 +1431,14 @@ static int fimc_md_probe(struct platform_device *pdev)
>>   		goto err_attr;
>>   	}
>>
>> -	if (fmd->num_sensors > 0) {
>> +	if (fmd->num_sensors > 0 || fmd->num_flashes > 0) {
>>   		fmd->subdev_notifier.subdevs = fmd->async_subdevs;
>> -		fmd->subdev_notifier.num_subdevs = fmd->num_sensors;
>> +		fmd->subdev_notifier.num_subdevs = fmd->num_sensors +
>> +							fmd->num_flashes;
>>   		fmd->subdev_notifier.bound = subdev_notifier_bound;
>>   		fmd->subdev_notifier.complete = subdev_notifier_complete;
>>   		fmd->num_sensors = 0;
>> +		fmd->num_flashes = 0;
>>
>>   		ret = v4l2_async_notifier_register(&fmd->v4l2_dev,
>>   						&fmd->subdev_notifier);
>> diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
>> index 0321454..feff9c8 100644
>> --- a/drivers/media/platform/exynos4-is/media-dev.h
>> +++ b/drivers/media/platform/exynos4-is/media-dev.h
>> @@ -34,6 +34,8 @@
>>
>>   #define FIMC_MAX_SENSORS	4
>>   #define FIMC_MAX_CAMCLKS	2
>> +#define FIMC_MAX_FLASHES	2
>> +#define FIMC_MAX_ASYNC_SUBDEVS (FIMC_MAX_SENSORS + FIMC_MAX_FLASHES)
>>   #define DEFAULT_SENSOR_CLK_FREQ	24000000U
>>
>>   /* LCD/ISP Writeback clocks (PIXELASYNCMx) */
>> @@ -93,6 +95,11 @@ struct fimc_sensor_info {
>>   	struct fimc_dev *host;
>>   };
>>
>> +struct fimc_flash_info {
>> +	struct v4l2_subdev *subdev;
>> +	struct v4l2_async_subdev asd;
>> +};
>> +
>>   struct cam_clk {
>>   	struct clk_hw hw;
>>   	struct fimc_md *fmd;
>> @@ -104,6 +111,8 @@ struct cam_clk {
>>    * @csis: MIPI CSIS subdevs data
>>    * @sensor: array of registered sensor subdevs
>>    * @num_sensors: actual number of registered sensors
>> + * @flash: array of registered flash subdevs
>> + * @num_flashes: actual number of registered flashes
>>    * @camclk: external sensor clock information
>>    * @fimc: array of registered fimc devices
>>    * @fimc_is: fimc-is data structure
>> @@ -123,6 +132,8 @@ struct fimc_md {
>>   	struct fimc_csis_info csis[CSIS_MAX_ENTITIES];
>>   	struct fimc_sensor_info sensor[FIMC_MAX_SENSORS];
>>   	int num_sensors;
>> +	struct fimc_flash_info flash[FIMC_MAX_FLASHES];
>> +	int num_flashes;
>>   	struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS];
>>   	struct clk *wbclk[FIMC_MAX_WBCLKS];
>>   	struct fimc_lite *fimc_lite[FIMC_LITE_MAX_DEVS];
>> @@ -149,7 +160,7 @@ struct fimc_md {
>>   	} clk_provider;
>>
>>   	struct v4l2_async_notifier subdev_notifier;
>> -	struct v4l2_async_subdev *async_subdevs[FIMC_MAX_SENSORS];
>> +	struct v4l2_async_subdev *async_subdevs[FIMC_MAX_ASYNC_SUBDEVS];
>>
>>   	bool user_subdev_api;
>>   	spinlock_t slock;
>


-- 
Best Regards,
Jacek Anaszewski

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

* Re: [PATCH v1 07/11] media: Add registration helpers for V4L2 flash sub-devices
  2015-03-23 15:08     ` Jacek Anaszewski
@ 2015-03-23 22:35       ` Sakari Ailus
  2015-03-24  8:35         ` Jacek Anaszewski
  0 siblings, 1 reply; 39+ messages in thread
From: Sakari Ailus @ 2015-03-23 22:35 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki, Hans Verkuil

Hi Jacek,

On Mon, Mar 23, 2015 at 04:08:10PM +0100, Jacek Anaszewski wrote:
> Hi Sakari,
> 
> Thanks for the review.
> 
> On 03/22/2015 01:22 AM, Sakari Ailus wrote:
> >Hi Jacek,
> >
> >Thanks for the updated set. Some comments below.
> >
> >On Fri, Mar 20, 2015 at 04:03:27PM +0100, Jacek Anaszewski 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.
> >>
> >>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      |   12 +
> >>  drivers/media/v4l2-core/Makefile     |    2 +
> >>  drivers/media/v4l2-core/v4l2-flash.c |  607 ++++++++++++++++++++++++++++++++++
> >>  include/media/v4l2-flash.h           |  145 ++++++++
> >>  4 files changed, 766 insertions(+)
> >>  create mode 100644 drivers/media/v4l2-core/v4l2-flash.c
> >>  create mode 100644 include/media/v4l2-flash.h
> >>
> >>diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
> >>index ba7e21a..0659598 100644
> >>--- a/drivers/media/v4l2-core/Kconfig
> >>+++ b/drivers/media/v4l2-core/Kconfig
> >>@@ -44,6 +44,18 @@ config V4L2_MEM2MEM_DEV
> >>          tristate
> >>          depends on VIDEOBUF2_CORE
> >>
> >>+# Used by LED subsystem flash drivers
> >>+config V4L2_FLASH_LED_CLASS
> >>+	tristate "Enable support for Flash sub-devices"
> >>+	depends on VIDEO_V4L2_SUBDEV_API
> >>+	depends on LEDS_CLASS_FLASH
> >>+	depends on OF
> >
> >I think you can drop OF dependency. A no-op implementation will be used if
> >it's disabled.
> 
> The sub-devices are matched by a sub-LED's device_node. This is the only
> way of matching the v4l2-flash sub-devs. Without OF a v4l2-flash
> sub-device will not get registered at all.

Not all systems use OF, and for those there are different async sub-device
matching options such as i2c or device name. A sub-device driver should not
mandate using a particular one.

> What do you mean by "no-op implementation"?

There are variants of most of_*() functions that do nothing. These variants
are used if CONFIG_OF is disabled, so the drivers won't need lots of ugly
#ifdefs.

> >>+	---help---
> >>+	  Say Y here to enable support for Flash sub-devices, which allow
> >>+	  to control LED class devices with use of V4L2 Flash controls.
> >>+
> >>+	  When in doubt, say N.
> >>+
> >>  # Used by drivers that need Videobuf modules
> >>  config VIDEOBUF_GEN
> >>  	tristate
> >>diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
> >>index 63d29f2..44e858c 100644
> >>--- a/drivers/media/v4l2-core/Makefile
> >>+++ b/drivers/media/v4l2-core/Makefile
> >>@@ -22,6 +22,8 @@ obj-$(CONFIG_VIDEO_TUNER) += tuner.o
> >>
> >>  obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
> >>
> >>+obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash.o
> >>+
> >>  obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
> >>  obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
> >>  obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o
> >>diff --git a/drivers/media/v4l2-core/v4l2-flash.c b/drivers/media/v4l2-core/v4l2-flash.c
> >>new file mode 100644
> >>index 0000000..804c2e4
> >>--- /dev/null
> >>+++ b/drivers/media/v4l2-core/v4l2-flash.c
> >>@@ -0,0 +1,607 @@
> >>+/*
> >>+ * 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.h>
> >>+#include "../../leds/leds.h"
> >
> >What do you need from leds.h? Shouldn't this be e.g. under include/linux
> >instead?
> >
> >>+#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)
> >>+
> >>+static enum led_brightness __intensity_to_led_brightness(
> >>+					struct v4l2_ctrl *ctrl,
> >>+					s32 intensity)
> >>+{
> >>+	s64 intensity64 = intensity - ctrl->minimum;
> >>+
> >>+	do_div(intensity64, 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->id != V4L2_CID_FLASH_INDICATOR_INTENSITY)
> >>+		++intensity64;
> >>+
> >>+	return intensity64;
> >>+}
> >>+
> >>+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] &&
> >>+	    ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
> >>+		return;
> >>+
> >>+	led_set_brightness(&v4l2_flash->fled_cdev->led_cdev, brightness);
> >>+}
> >>+
> >>+static int v4l2_flash_update_led_brightness(struct v4l2_flash *v4l2_flash,
> >>+					struct v4l2_ctrl *ctrl)
> >>+{
> >>+	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;
> >>+	int ret;
> >>+
> >>+	/*
> >>+	 * 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 (ctrl == ctrls[TORCH_INTENSITY] &&
> >>+	    ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
> >>+		return 0;
> >>+
> >>+	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 */
> >>+		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 */
> >>+		return led_set_flash_timeout(fled_cdev, c->val);
> >>+	case V4L2_CID_FLASH_INTENSITY:
> >>+		/* no conversion is needed */
> >>+		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 fill_ctrl_init_data(struct v4l2_flash *v4l2_flash,
> >>+			  struct v4l2_flash_ctrl_config *flash_ctrl_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_ctrl_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_ctrl_cfg->flash_faults;
> >>+		ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
> >>+				  V4L2_CTRL_FLAG_READ_ONLY;
> >>+	}
> >>+
> >>+	/* Init INDICATOR_INTENSITY ctrl data */
> >>+	if (flash_ctrl_cfg->indicator_led) {
> >>+		ctrl_init_data[INDICATOR_INTENSITY].cid =
> >>+					V4L2_CID_FLASH_INDICATOR_INTENSITY;
> >>+		ctrl_init_data[INDICATOR_INTENSITY].config =
> >>+						flash_ctrl_cfg->intensity;
> >>+		ctrl_cfg = &ctrl_init_data[INDICATOR_INTENSITY].config;
> >>+		ctrl_cfg->id = V4L2_CID_FLASH_INDICATOR_INTENSITY;
> >>+		ctrl_cfg->min = 0;
> >>+		ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
> >>+
> >>+		/* Indicator LED can have only faults and intensity controls. */
> >>+		return;
> >>+	}
> >>+
> >>+	/* 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_init_data[TORCH_INTENSITY].config = flash_ctrl_cfg->intensity;
> >>+	ctrl_cfg = &ctrl_init_data[TORCH_INTENSITY].config;
> >>+	ctrl_cfg->id = V4L2_CID_FLASH_TORCH_INTENSITY;
> >>+	ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
> >>+
> >>+	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_ctrl_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_init_data[FLASH_TIMEOUT].config =
> >>+					flash_ctrl_cfg->flash_timeout;
> >>+		ctrl_cfg = &ctrl_init_data[FLASH_TIMEOUT].config;
> >>+		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_init_data[FLASH_INTENSITY].config =
> >>+					flash_ctrl_cfg->flash_intensity;
> >>+		ctrl_cfg = &ctrl_init_data[FLASH_INTENSITY].config;
> >>+		ctrl_cfg->id = V4L2_CID_FLASH_INTENSITY;
> >>+		ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
> >>+	}
> >>+}
> >>+
> >>+static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash,
> >>+				struct v4l2_flash_ctrl_config *flash_ctrl_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;
> >>+
> >>+	/* 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_ctrl_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;
> >>+
> >>+	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);
> >>+
> >>+	if (ctrls[INDICATOR_INTENSITY])
> >>+		v4l2_flash_set_led_brightness(v4l2_flash,
> >>+						ctrls[INDICATOR_INTENSITY]);
> >>+	else
> >>+		v4l2_flash_set_led_brightness(v4l2_flash,
> >>+						ctrls[TORCH_INTENSITY]);
> >>+
> >>+	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;
> >>+	int ret = 0;
> >
> >No need to initialise ret.
> >
> >>+
> >>+	mutex_lock(&led_cdev->led_access);
> >>+
> >>+	if (!v4l2_fh_is_singular(&fh->vfh)) {
> >
> >I'm not sure about this --- no current flash driver AFAIK provides exclusive
> >access.
> 
> v4l2_flash_open has a side effect in the form of writing device
> registers with v4l2-flash controls state. Generally it should not
> affect e.g. the flash strobe if it took place in the same moment.
> 
> What should be added, if we are to allow multiple users, is the
> reference counter to be able to detect that last user is closing
> a sub-dev and only then set the strobe source to software.

You shouldn't need a counter, just use v4l2_if_is_singular() as you already
do. But instead of returning -EBUSY, return 0. :-)

> >>+		ret = -EBUSY;
> >>+		goto unlock;
> >>+	}
> >>+
> >>+	led_sysfs_disable(led_cdev);
> >>+	led_trigger_remove(led_cdev);
> >>+
> >>+	ret = __sync_device_with_v4l2_controls(v4l2_flash);
> >>+
> >>+unlock:
> >>+	mutex_unlock(&led_cdev->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;
> >>+	int ret = 0;
> >
> >ret is redundant.
> 
> I think that I forgot to assign the v4l2_ctrl_s_ctrl result to it.
> 
> >>+
> >>+	mutex_lock(&led_cdev->led_access);
> >>+
> >>+	if (v4l2_flash->ctrls[STROBE_SOURCE])
> >>+		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);
> >>+
> >>+	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 led_classdev_flash *fled_cdev,
> >>+				   const struct v4l2_flash_ops *ops,
> >>+				   struct v4l2_flash_ctrl_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->ops = ops;
> >>+	sd->dev = led_cdev->dev;
> >>+	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)
> >
> >You're missing media_entity_cleanup() here. How about adding a new label
> >below for that?
> 
> Right, thanks.
> 
> >>+		return ERR_PTR(ret);
> >>+
> >>+	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);
> >>+	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 = &v4l2_flash->sd;
> >>+	struct led_classdev *led_cdev = &v4l2_flash->fled_cdev->led_cdev;
> >>+
> >>+	v4l2_async_unregister_subdev(sd);
> >>+	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.h b/include/media/v4l2-flash.h
> >>new file mode 100644
> >>index 0000000..cc0138d
> >>--- /dev/null
> >>+++ b/include/media/v4l2-flash.h
> >>@@ -0,0 +1,145 @@
> >>+/*
> >>+ * 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;
> >>+
> >>+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,
> >>+};
> >>+
> >>+/*
> >>+ * 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_ctrl_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_ctrl_config - V4L2 Flash controls initialization data
> >
> >This is a bit more than just about controls. How about dropping "_ctrl" from
> >the name?
> 
> Good point.
> 
> >>+ * @dev_name:			human readable device name
> >
> >Could you use "the name of the media entity, unique in the system" instead,
> >please?
> >
> >>+ * @intensity:			constraints for the led in a non-flash mode
> >>+ * @flash_intensity:		V4L2_CID_FLASH_INTENSITY constraints
> >>+ * @flash_timeout:		V4L2_CID_FLASH_TIMEOUT constraints
> >>+ * @flash_faults:		possible flash faults
> >
> >Please refer to documentation
> 
> Do you want to put here whole description from documentation?
> Or maybe only mention that faults can "prevent further use of some of
> the flash controls"?

No, just a pointer where the flash faults are defined, or which #defines
they use for example so the reader can find them easily.

> 
> >
> >>+ * @has_external_strobe:	external strobe capability
> >>+ * @indicator_led:		signifies that a led is of indicator type
> >>+ */
> >>+struct v4l2_flash_ctrl_config {
> >>+	char dev_name[32];
> >>+	struct v4l2_ctrl_config intensity;
> >>+	struct v4l2_ctrl_config flash_intensity;
> >>+	struct v4l2_ctrl_config flash_timeout;
> >
> >I think I may have suggested using v4l2_ctrl_config here, but currently the
> >drivers need to copy the values to v4l2_ctrl_config struct, instead of using
> >struct led_flash_setting which they have to use anyway.
> >
> >What would you think it'd be feasible to use led_flash_setting here as well?
> 
> I tried and it turned out that there was a room for even greater number
> of optimizations around flash settings and config initialization.

:-)

> 
> 
> >>+	u32 flash_faults;
> >>+	unsigned int has_external_strobe:1;
> >>+	unsigned int indicator_led:1;
> >>+};
> >>+
> >>+/**
> >>+ * struct v4l2_flash - Flash sub-device context
> >>+ * @fled_cdev:		LED Flash class device controlled by this sub-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;
> >>+	const struct v4l2_flash_ops *ops;
> >>+
> >>+	struct v4l2_subdev sd;
> >>+	struct v4l2_ctrl_handler hdl;
> >>+	struct v4l2_ctrl *ctrls[STROBE_SOURCE + 1];
> >>+};
> >>+
> >>+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
> >>+ * @fled_cdev:	the LED Flash class device to wrap
> >>+ * @flash_ops:	V4L2 Flash device ops
> >>+ * @config:	initialization data for V4L2 Flash controls
> >>+ *
> >>+ * 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 led_classdev_flash *fled_cdev,
> >>+				   const struct v4l2_flash_ops *ops,
> >>+				   struct v4l2_flash_ctrl_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
> >>+#define v4l2_flash_init(fled_cdev, ops, config) (NULL)
> >>+#define v4l2_flash_release(v4l2_flash)
> >>+#endif /* CONFIG_V4L2_FLASH_LED_CLASS */
> >>+
> >>+#endif /* _V4L2_FLASH_H */
> >
> 
> 

-- 
Kind regards,

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

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

* Re: [PATCH v1 06/11] exynos4-is: Add support for v4l2-flash subdevs
  2015-03-23 15:32         ` Jacek Anaszewski
@ 2015-03-23 22:39           ` Sakari Ailus
  2015-03-24  8:52             ` Jacek Anaszewski
  0 siblings, 1 reply; 39+ messages in thread
From: Sakari Ailus @ 2015-03-23 22:39 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki

Hi Jacek,

On Mon, Mar 23, 2015 at 04:32:12PM +0100, Jacek Anaszewski wrote:
> Hi Sakari,
> 
> Thanks for the review.
> 
> On 03/22/2015 02:21 AM, Sakari Ailus wrote:
> >Hi Jacek,
> >
> >Some comments below. Please also get an ack from Sylwester! :-)
> 
> No doubt about that :)
> 
> >On Fri, Mar 20, 2015 at 04:03:26PM +0100, Jacek Anaszewski wrote:
> >>This patch adds support for external v4l2-flash devices.
> >>The support includes parsing "flashes" DT property
> >>and asynchronous subdevice 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 |   36 +++++++++++++++++++++++--
> >>  drivers/media/platform/exynos4-is/media-dev.h |   13 ++++++++-
> >>  2 files changed, 46 insertions(+), 3 deletions(-)
> >>
> >>diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
> >>index f315ef9..8dd0e5d 100644
> >>--- a/drivers/media/platform/exynos4-is/media-dev.c
> >>+++ b/drivers/media/platform/exynos4-is/media-dev.c
> >>@@ -451,6 +451,25 @@ rpm_put:
> >>  	return ret;
> >>  }
> >>
> >>+static void fimc_md_register_flash_entities(struct fimc_md *fmd)
> >>+{
> >>+	struct device_node *parent = fmd->pdev->dev.of_node;
> >>+	struct device_node *np;
> >>+	int i = 0;
> >>+
> >>+	do {
> >>+		np = of_parse_phandle(parent, "flashes", i);
> >>+		if (np) {
> >
> >if (!np)
> >	break;
> >
> >And you can remove checking np another time in the loop condition.
> 
> Thanks, this will be cleaner indeed.
> 
> >>+			fmd->flash[fmd->num_flashes].asd.match_type =
> >>+							V4L2_ASYNC_MATCH_OF;
> >>+			fmd->flash[fmd->num_flashes].asd.match.of.node = np;
> >>+			fmd->num_flashes++;
> >>+			fmd->async_subdevs[fmd->num_sensors + i] =
> >>+						&fmd->flash[i].asd;
> >
> >Have all the sensors been already registered by this point?
> 
> Function fimc_md_register_sensor_entities is called before
> this one.

Ok. Then it's fine. I just thing this would be much cleaner if there was no
assumption that fmd->num_flashes is necessarily zero (and all sensors have
been registered).

> >>+		}
> >>+	} while (np && (++i < FIMC_MAX_FLASHES));
> >
> >How about instead:
> >
> >fmd->num_flashes < FIMC_MAX_FLASHES
> >
> >And drop i. Also move incrementing num_flashes as last in the if.
> 
> Dropping i will enforce referring to fmd->num_flashes 7 times
> in this short fragment of code.
> Maybe it would be better to use a pointer to it?
> int *nf = &fmd=>num_flashes ?

You could also do

const int nf = fmd->num_flashes;

in the beginning of the loop.

Up to you. Either is IMO better than an unrelated counter variable i. :-)

> >>+}
> >>+
> >>  static int __of_get_csis_id(struct device_node *np)
> >>  {
> >>  	u32 reg = 0;
> >>@@ -1275,6 +1294,15 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
> >>  	struct fimc_sensor_info *si = NULL;
> >>  	int i;
> >>
> >>+	/* Register flash subdev if detected any */
> >>+	for (i = 0; i < ARRAY_SIZE(fmd->flash); i++) {
> >>+		if (fmd->flash[i].asd.match.of.node == subdev->dev->of_node) {
> >>+			fmd->flash[i].subdev = subdev;
> >>+			fmd->num_flashes++;
> >>+			return 0;
> >>+		}
> >>+	}
> >>+
> >>  	/* Find platform data for this sensor subdev */
> >>  	for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++)
> >>  		if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node)
> >>@@ -1385,6 +1413,8 @@ static int fimc_md_probe(struct platform_device *pdev)
> >>  		goto err_m_ent;
> >>  	}
> >>
> >>+	fimc_md_register_flash_entities(fmd);
> >>+
> >>  	mutex_unlock(&fmd->media_dev.graph_mutex);
> >>
> >>  	ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
> >>@@ -1401,12 +1431,14 @@ static int fimc_md_probe(struct platform_device *pdev)
> >>  		goto err_attr;
> >>  	}
> >>
> >>-	if (fmd->num_sensors > 0) {
> >>+	if (fmd->num_sensors > 0 || fmd->num_flashes > 0) {
> >>  		fmd->subdev_notifier.subdevs = fmd->async_subdevs;
> >>-		fmd->subdev_notifier.num_subdevs = fmd->num_sensors;
> >>+		fmd->subdev_notifier.num_subdevs = fmd->num_sensors +
> >>+							fmd->num_flashes;
> >>  		fmd->subdev_notifier.bound = subdev_notifier_bound;
> >>  		fmd->subdev_notifier.complete = subdev_notifier_complete;
> >>  		fmd->num_sensors = 0;
> >>+		fmd->num_flashes = 0;
> >>
> >>  		ret = v4l2_async_notifier_register(&fmd->v4l2_dev,
> >>  						&fmd->subdev_notifier);
> >>diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
> >>index 0321454..feff9c8 100644
> >>--- a/drivers/media/platform/exynos4-is/media-dev.h
> >>+++ b/drivers/media/platform/exynos4-is/media-dev.h
> >>@@ -34,6 +34,8 @@
> >>
> >>  #define FIMC_MAX_SENSORS	4
> >>  #define FIMC_MAX_CAMCLKS	2
> >>+#define FIMC_MAX_FLASHES	2
> >>+#define FIMC_MAX_ASYNC_SUBDEVS (FIMC_MAX_SENSORS + FIMC_MAX_FLASHES)
> >>  #define DEFAULT_SENSOR_CLK_FREQ	24000000U
> >>
> >>  /* LCD/ISP Writeback clocks (PIXELASYNCMx) */
> >>@@ -93,6 +95,11 @@ struct fimc_sensor_info {
> >>  	struct fimc_dev *host;
> >>  };
> >>
> >>+struct fimc_flash_info {
> >>+	struct v4l2_subdev *subdev;
> >>+	struct v4l2_async_subdev asd;
> >>+};
> >>+
> >>  struct cam_clk {
> >>  	struct clk_hw hw;
> >>  	struct fimc_md *fmd;
> >>@@ -104,6 +111,8 @@ struct cam_clk {
> >>   * @csis: MIPI CSIS subdevs data
> >>   * @sensor: array of registered sensor subdevs
> >>   * @num_sensors: actual number of registered sensors
> >>+ * @flash: array of registered flash subdevs
> >>+ * @num_flashes: actual number of registered flashes
> >>   * @camclk: external sensor clock information
> >>   * @fimc: array of registered fimc devices
> >>   * @fimc_is: fimc-is data structure
> >>@@ -123,6 +132,8 @@ struct fimc_md {
> >>  	struct fimc_csis_info csis[CSIS_MAX_ENTITIES];
> >>  	struct fimc_sensor_info sensor[FIMC_MAX_SENSORS];
> >>  	int num_sensors;
> >>+	struct fimc_flash_info flash[FIMC_MAX_FLASHES];
> >>+	int num_flashes;
> >>  	struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS];
> >>  	struct clk *wbclk[FIMC_MAX_WBCLKS];
> >>  	struct fimc_lite *fimc_lite[FIMC_LITE_MAX_DEVS];
> >>@@ -149,7 +160,7 @@ struct fimc_md {
> >>  	} clk_provider;
> >>
> >>  	struct v4l2_async_notifier subdev_notifier;
> >>-	struct v4l2_async_subdev *async_subdevs[FIMC_MAX_SENSORS];
> >>+	struct v4l2_async_subdev *async_subdevs[FIMC_MAX_ASYNC_SUBDEVS];
> >>
> >>  	bool user_subdev_api;
> >>  	spinlock_t slock;
> >
> 
> 

-- 
Kind regards,

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

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

* Re: [PATCH v1 07/11] media: Add registration helpers for V4L2 flash sub-devices
  2015-03-23 22:35       ` Sakari Ailus
@ 2015-03-24  8:35         ` Jacek Anaszewski
  2015-03-25  1:00           ` Sakari Ailus
  0 siblings, 1 reply; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-24  8:35 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki, Hans Verkuil

Hi Sakari,

On 03/23/2015 11:35 PM, Sakari Ailus wrote:
> Hi Jacek,
>
> On Mon, Mar 23, 2015 at 04:08:10PM +0100, Jacek Anaszewski wrote:
>> Hi Sakari,
>>
>> Thanks for the review.
>>
>> On 03/22/2015 01:22 AM, Sakari Ailus wrote:
>>> Hi Jacek,
>>>
>>> Thanks for the updated set. Some comments below.
>>>
>>> On Fri, Mar 20, 2015 at 04:03:27PM +0100, Jacek Anaszewski 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.
>>>>
>>>> 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      |   12 +
>>>>   drivers/media/v4l2-core/Makefile     |    2 +
>>>>   drivers/media/v4l2-core/v4l2-flash.c |  607 ++++++++++++++++++++++++++++++++++
>>>>   include/media/v4l2-flash.h           |  145 ++++++++
>>>>   4 files changed, 766 insertions(+)
>>>>   create mode 100644 drivers/media/v4l2-core/v4l2-flash.c
>>>>   create mode 100644 include/media/v4l2-flash.h
>>>>
>>>> diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
>>>> index ba7e21a..0659598 100644
>>>> --- a/drivers/media/v4l2-core/Kconfig
>>>> +++ b/drivers/media/v4l2-core/Kconfig
>>>> @@ -44,6 +44,18 @@ config V4L2_MEM2MEM_DEV
>>>>           tristate
>>>>           depends on VIDEOBUF2_CORE
>>>>
>>>> +# Used by LED subsystem flash drivers
>>>> +config V4L2_FLASH_LED_CLASS
>>>> +	tristate "Enable support for Flash sub-devices"
>>>> +	depends on VIDEO_V4L2_SUBDEV_API
>>>> +	depends on LEDS_CLASS_FLASH
>>>> +	depends on OF
>>>
>>> I think you can drop OF dependency. A no-op implementation will be used if
>>> it's disabled.
>>
>> The sub-devices are matched by a sub-LED's device_node. This is the only
>> way of matching the v4l2-flash sub-devs. Without OF a v4l2-flash
>> sub-device will not get registered at all.
>
> Not all systems use OF, and for those there are different async sub-device
> matching options such as i2c or device name. A sub-device driver should not
> mandate using a particular one.
>
>> What do you mean by "no-op implementation"?
>
> There are variants of most of_*() functions that do nothing. These variants
> are used if CONFIG_OF is disabled, so the drivers won't need lots of ugly
> #ifdefs.

OK, so OF dependency is to be dropped.

>>>> +	---help---
>>>> +	  Say Y here to enable support for Flash sub-devices, which allow
>>>> +	  to control LED class devices with use of V4L2 Flash controls.
>>>> +
>>>> +	  When in doubt, say N.
>>>> +
>>>>   # Used by drivers that need Videobuf modules
>>>>   config VIDEOBUF_GEN
>>>>   	tristate
>>>> diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
>>>> index 63d29f2..44e858c 100644
>>>> --- a/drivers/media/v4l2-core/Makefile
>>>> +++ b/drivers/media/v4l2-core/Makefile
>>>> @@ -22,6 +22,8 @@ obj-$(CONFIG_VIDEO_TUNER) += tuner.o
>>>>
>>>>   obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
>>>>
>>>> +obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash.o
>>>> +
>>>>   obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
>>>>   obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
>>>>   obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o
>>>> diff --git a/drivers/media/v4l2-core/v4l2-flash.c b/drivers/media/v4l2-core/v4l2-flash.c
>>>> new file mode 100644
>>>> index 0000000..804c2e4
>>>> --- /dev/null
>>>> +++ b/drivers/media/v4l2-core/v4l2-flash.c
>>>> @@ -0,0 +1,607 @@
>>>> +/*
>>>> + * 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.h>
>>>> +#include "../../leds/leds.h"
>>>
>>> What do you need from leds.h? Shouldn't this be e.g. under include/linux
>>> instead?

I need led_trigger_remove function.

>>>> +#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)
>>>> +
>>>> +static enum led_brightness __intensity_to_led_brightness(
>>>> +					struct v4l2_ctrl *ctrl,
>>>> +					s32 intensity)
>>>> +{
>>>> +	s64 intensity64 = intensity - ctrl->minimum;
>>>> +
>>>> +	do_div(intensity64, 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->id != V4L2_CID_FLASH_INDICATOR_INTENSITY)
>>>> +		++intensity64;
>>>> +
>>>> +	return intensity64;
>>>> +}
>>>> +
>>>> +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] &&
>>>> +	    ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
>>>> +		return;
>>>> +
>>>> +	led_set_brightness(&v4l2_flash->fled_cdev->led_cdev, brightness);
>>>> +}
>>>> +
>>>> +static int v4l2_flash_update_led_brightness(struct v4l2_flash *v4l2_flash,
>>>> +					struct v4l2_ctrl *ctrl)
>>>> +{
>>>> +	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;
>>>> +	int ret;
>>>> +
>>>> +	/*
>>>> +	 * 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 (ctrl == ctrls[TORCH_INTENSITY] &&
>>>> +	    ctrls[LED_MODE]->val != V4L2_FLASH_LED_MODE_TORCH)
>>>> +		return 0;
>>>> +
>>>> +	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 */
>>>> +		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 */
>>>> +		return led_set_flash_timeout(fled_cdev, c->val);
>>>> +	case V4L2_CID_FLASH_INTENSITY:
>>>> +		/* no conversion is needed */
>>>> +		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 fill_ctrl_init_data(struct v4l2_flash *v4l2_flash,
>>>> +			  struct v4l2_flash_ctrl_config *flash_ctrl_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_ctrl_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_ctrl_cfg->flash_faults;
>>>> +		ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE |
>>>> +				  V4L2_CTRL_FLAG_READ_ONLY;
>>>> +	}
>>>> +
>>>> +	/* Init INDICATOR_INTENSITY ctrl data */
>>>> +	if (flash_ctrl_cfg->indicator_led) {
>>>> +		ctrl_init_data[INDICATOR_INTENSITY].cid =
>>>> +					V4L2_CID_FLASH_INDICATOR_INTENSITY;
>>>> +		ctrl_init_data[INDICATOR_INTENSITY].config =
>>>> +						flash_ctrl_cfg->intensity;
>>>> +		ctrl_cfg = &ctrl_init_data[INDICATOR_INTENSITY].config;
>>>> +		ctrl_cfg->id = V4L2_CID_FLASH_INDICATOR_INTENSITY;
>>>> +		ctrl_cfg->min = 0;
>>>> +		ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
>>>> +
>>>> +		/* Indicator LED can have only faults and intensity controls. */
>>>> +		return;
>>>> +	}
>>>> +
>>>> +	/* 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_init_data[TORCH_INTENSITY].config = flash_ctrl_cfg->intensity;
>>>> +	ctrl_cfg = &ctrl_init_data[TORCH_INTENSITY].config;
>>>> +	ctrl_cfg->id = V4L2_CID_FLASH_TORCH_INTENSITY;
>>>> +	ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
>>>> +
>>>> +	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_ctrl_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_init_data[FLASH_TIMEOUT].config =
>>>> +					flash_ctrl_cfg->flash_timeout;
>>>> +		ctrl_cfg = &ctrl_init_data[FLASH_TIMEOUT].config;
>>>> +		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_init_data[FLASH_INTENSITY].config =
>>>> +					flash_ctrl_cfg->flash_intensity;
>>>> +		ctrl_cfg = &ctrl_init_data[FLASH_INTENSITY].config;
>>>> +		ctrl_cfg->id = V4L2_CID_FLASH_INTENSITY;
>>>> +		ctrl_cfg->flags = V4L2_CTRL_FLAG_VOLATILE;
>>>> +	}
>>>> +}
>>>> +
>>>> +static int v4l2_flash_init_controls(struct v4l2_flash *v4l2_flash,
>>>> +				struct v4l2_flash_ctrl_config *flash_ctrl_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;
>>>> +
>>>> +	/* 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_ctrl_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;
>>>> +
>>>> +	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);
>>>> +
>>>> +	if (ctrls[INDICATOR_INTENSITY])
>>>> +		v4l2_flash_set_led_brightness(v4l2_flash,
>>>> +						ctrls[INDICATOR_INTENSITY]);
>>>> +	else
>>>> +		v4l2_flash_set_led_brightness(v4l2_flash,
>>>> +						ctrls[TORCH_INTENSITY]);
>>>> +
>>>> +	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;
>>>> +	int ret = 0;
>>>
>>> No need to initialise ret.
>>>
>>>> +
>>>> +	mutex_lock(&led_cdev->led_access);
>>>> +
>>>> +	if (!v4l2_fh_is_singular(&fh->vfh)) {
>>>
>>> I'm not sure about this --- no current flash driver AFAIK provides exclusive
>>> access.
>>
>> v4l2_flash_open has a side effect in the form of writing device
>> registers with v4l2-flash controls state. Generally it should not
>> affect e.g. the flash strobe if it took place in the same moment.
>>
>> What should be added, if we are to allow multiple users, is the
>> reference counter to be able to detect that last user is closing
>> a sub-dev and only then set the strobe source to software.
>
> You shouldn't need a counter, just use v4l2_if_is_singular() as you already
> do. But instead of returning -EBUSY, return 0. :-)

Good point :)

>>>> +		ret = -EBUSY;
>>>> +		goto unlock;
>>>> +	}
>>>> +
>>>> +	led_sysfs_disable(led_cdev);
>>>> +	led_trigger_remove(led_cdev);
>>>> +
>>>> +	ret = __sync_device_with_v4l2_controls(v4l2_flash);
>>>> +
>>>> +unlock:
>>>> +	mutex_unlock(&led_cdev->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;
>>>> +	int ret = 0;
>>>
>>> ret is redundant.
>>
>> I think that I forgot to assign the v4l2_ctrl_s_ctrl result to it.
>>
>>>> +
>>>> +	mutex_lock(&led_cdev->led_access);
>>>> +	if (v4l2_flash->ctrls[STROBE_SOURCE])
>>>> +		v4l2_ctrl_s_ctrl(v4l2_flash->ctrls[STROBE_SOURCE],
>>>> +				V4L2_FLASH_STROBE_SOURCE_SOFTWARE);

v4l2_fh_is_singular will also have to be added to the condition above.

>>>> +	led_sysfs_enable(led_cdev);
>>>> +
>>>> +	mutex_unlock(&led_cdev->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 led_classdev_flash *fled_cdev,
>>>> +				   const struct v4l2_flash_ops *ops,
>>>> +				   struct v4l2_flash_ctrl_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->ops = ops;
>>>> +	sd->dev = led_cdev->dev;
>>>> +	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)
>>>
>>> You're missing media_entity_cleanup() here. How about adding a new label
>>> below for that?
>>
>> Right, thanks.
>>
>>>> +		return ERR_PTR(ret);
>>>> +
>>>> +	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);
>>>> +	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 = &v4l2_flash->sd;
>>>> +	struct led_classdev *led_cdev = &v4l2_flash->fled_cdev->led_cdev;
>>>> +
>>>> +	v4l2_async_unregister_subdev(sd);
>>>> +	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.h b/include/media/v4l2-flash.h
>>>> new file mode 100644
>>>> index 0000000..cc0138d
>>>> --- /dev/null
>>>> +++ b/include/media/v4l2-flash.h
>>>> @@ -0,0 +1,145 @@
>>>> +/*
>>>> + * 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;
>>>> +
>>>> +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,
>>>> +};
>>>> +
>>>> +/*
>>>> + * 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_ctrl_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_ctrl_config - V4L2 Flash controls initialization data
>>>
>>> This is a bit more than just about controls. How about dropping "_ctrl" from
>>> the name?
>>
>> Good point.
>>
>>>> + * @dev_name:			human readable device name
>>>
>>> Could you use "the name of the media entity, unique in the system" instead,
>>> please?
>>>
>>>> + * @intensity:			constraints for the led in a non-flash mode
>>>> + * @flash_intensity:		V4L2_CID_FLASH_INTENSITY constraints
>>>> + * @flash_timeout:		V4L2_CID_FLASH_TIMEOUT constraints
>>>> + * @flash_faults:		possible flash faults
>>>
>>> Please refer to documentation
>>
>> Do you want to put here whole description from documentation?
>> Or maybe only mention that faults can "prevent further use of some of
>> the flash controls"?
>
> No, just a pointer where the flash faults are defined, or which #defines
> they use for example so the reader can find them easily.

OK, I see.

>>
>>>
>>>> + * @has_external_strobe:	external strobe capability
>>>> + * @indicator_led:		signifies that a led is of indicator type
>>>> + */
>>>> +struct v4l2_flash_ctrl_config {
>>>> +	char dev_name[32];
>>>> +	struct v4l2_ctrl_config intensity;
>>>> +	struct v4l2_ctrl_config flash_intensity;
>>>> +	struct v4l2_ctrl_config flash_timeout;
>>>
>>> I think I may have suggested using v4l2_ctrl_config here, but currently the
>>> drivers need to copy the values to v4l2_ctrl_config struct, instead of using
>>> struct led_flash_setting which they have to use anyway.
>>>
>>> What would you think it'd be feasible to use led_flash_setting here as well?
>>
>> I tried and it turned out that there was a room for even greater number
>> of optimizations around flash settings and config initialization.
>
> :-)
>
>>
>>
>>>> +	u32 flash_faults;
>>>> +	unsigned int has_external_strobe:1;
>>>> +	unsigned int indicator_led:1;
>>>> +};
>>>> +
>>>> +/**
>>>> + * struct v4l2_flash - Flash sub-device context
>>>> + * @fled_cdev:		LED Flash class device controlled by this sub-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;
>>>> +	const struct v4l2_flash_ops *ops;
>>>> +
>>>> +	struct v4l2_subdev sd;
>>>> +	struct v4l2_ctrl_handler hdl;
>>>> +	struct v4l2_ctrl *ctrls[STROBE_SOURCE + 1];
>>>> +};
>>>> +
>>>> +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
>>>> + * @fled_cdev:	the LED Flash class device to wrap
>>>> + * @flash_ops:	V4L2 Flash device ops
>>>> + * @config:	initialization data for V4L2 Flash controls
>>>> + *
>>>> + * 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 led_classdev_flash *fled_cdev,
>>>> +				   const struct v4l2_flash_ops *ops,
>>>> +				   struct v4l2_flash_ctrl_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
>>>> +#define v4l2_flash_init(fled_cdev, ops, config) (NULL)
>>>> +#define v4l2_flash_release(v4l2_flash)
>>>> +#endif /* CONFIG_V4L2_FLASH_LED_CLASS */
>>>> +
>>>> +#endif /* _V4L2_FLASH_H */
>>>
>>
>>
>


-- 
Best Regards,
Jacek Anaszewski

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

* Re: [PATCH v1 06/11] exynos4-is: Add support for v4l2-flash subdevs
  2015-03-23 22:39           ` Sakari Ailus
@ 2015-03-24  8:52             ` Jacek Anaszewski
  0 siblings, 0 replies; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-24  8:52 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki

Hi Sakari,

On 03/23/2015 11:39 PM, Sakari Ailus wrote:
> Hi Jacek,
>
> On Mon, Mar 23, 2015 at 04:32:12PM +0100, Jacek Anaszewski wrote:
>> Hi Sakari,
>>
>> Thanks for the review.
>>
>> On 03/22/2015 02:21 AM, Sakari Ailus wrote:
>>> Hi Jacek,
>>>
>>> Some comments below. Please also get an ack from Sylwester! :-)
>>
>> No doubt about that :)
>>
>>> On Fri, Mar 20, 2015 at 04:03:26PM +0100, Jacek Anaszewski wrote:
>>>> This patch adds support for external v4l2-flash devices.
>>>> The support includes parsing "flashes" DT property
>>>> and asynchronous subdevice 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 |   36 +++++++++++++++++++++++--
>>>>   drivers/media/platform/exynos4-is/media-dev.h |   13 ++++++++-
>>>>   2 files changed, 46 insertions(+), 3 deletions(-)
>>>>
>>>> diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
>>>> index f315ef9..8dd0e5d 100644
>>>> --- a/drivers/media/platform/exynos4-is/media-dev.c
>>>> +++ b/drivers/media/platform/exynos4-is/media-dev.c
>>>> @@ -451,6 +451,25 @@ rpm_put:
>>>>   	return ret;
>>>>   }
>>>>
>>>> +static void fimc_md_register_flash_entities(struct fimc_md *fmd)
>>>> +{
>>>> +	struct device_node *parent = fmd->pdev->dev.of_node;
>>>> +	struct device_node *np;
>>>> +	int i = 0;
>>>> +
>>>> +	do {
>>>> +		np = of_parse_phandle(parent, "flashes", i);
>>>> +		if (np) {
>>>
>>> if (!np)
>>> 	break;
>>>
>>> And you can remove checking np another time in the loop condition.
>>
>> Thanks, this will be cleaner indeed.
>>
>>>> +			fmd->flash[fmd->num_flashes].asd.match_type =
>>>> +							V4L2_ASYNC_MATCH_OF;
>>>> +			fmd->flash[fmd->num_flashes].asd.match.of.node = np;
>>>> +			fmd->num_flashes++;
>>>> +			fmd->async_subdevs[fmd->num_sensors + i] =
>>>> +						&fmd->flash[i].asd;
>>>
>>> Have all the sensors been already registered by this point?
>>
>> Function fimc_md_register_sensor_entities is called before
>> this one.
>
> Ok. Then it's fine. I just thing this would be much cleaner if there was no
> assumption that fmd->num_flashes is necessarily zero (and all sensors have
> been registered).

Do you think this should be approached differently? I don't catch your
point here, could you be more specific? :)

>>>> +		}
>>>> +	} while (np && (++i < FIMC_MAX_FLASHES));
>>>
>>> How about instead:
>>>
>>> fmd->num_flashes < FIMC_MAX_FLASHES
>>>
>>> And drop i. Also move incrementing num_flashes as last in the if.
>>
>> Dropping i will enforce referring to fmd->num_flashes 7 times
>> in this short fragment of code.
>> Maybe it would be better to use a pointer to it?
>> int *nf = &fmd=>num_flashes ?
>
> You could also do
>
> const int nf = fmd->num_flashes;

fmd->num_flashes is incremented in the loop, so the constant
will not work here. There is a tradeoff - counter variable
or many references to the fmd->num_flashes.

>
> in the beginning of the loop.
>
> Up to you. Either is IMO better than an unrelated counter variable i. :-)
>
>>>> +}
>>>> +
>>>>   static int __of_get_csis_id(struct device_node *np)
>>>>   {
>>>>   	u32 reg = 0;
>>>> @@ -1275,6 +1294,15 @@ static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
>>>>   	struct fimc_sensor_info *si = NULL;
>>>>   	int i;
>>>>
>>>> +	/* Register flash subdev if detected any */
>>>> +	for (i = 0; i < ARRAY_SIZE(fmd->flash); i++) {
>>>> +		if (fmd->flash[i].asd.match.of.node == subdev->dev->of_node) {
>>>> +			fmd->flash[i].subdev = subdev;
>>>> +			fmd->num_flashes++;
>>>> +			return 0;
>>>> +		}
>>>> +	}
>>>> +
>>>>   	/* Find platform data for this sensor subdev */
>>>>   	for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++)
>>>>   		if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node)
>>>> @@ -1385,6 +1413,8 @@ static int fimc_md_probe(struct platform_device *pdev)
>>>>   		goto err_m_ent;
>>>>   	}
>>>>
>>>> +	fimc_md_register_flash_entities(fmd);
>>>> +
>>>>   	mutex_unlock(&fmd->media_dev.graph_mutex);
>>>>
>>>>   	ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
>>>> @@ -1401,12 +1431,14 @@ static int fimc_md_probe(struct platform_device *pdev)
>>>>   		goto err_attr;
>>>>   	}
>>>>
>>>> -	if (fmd->num_sensors > 0) {
>>>> +	if (fmd->num_sensors > 0 || fmd->num_flashes > 0) {
>>>>   		fmd->subdev_notifier.subdevs = fmd->async_subdevs;
>>>> -		fmd->subdev_notifier.num_subdevs = fmd->num_sensors;
>>>> +		fmd->subdev_notifier.num_subdevs = fmd->num_sensors +
>>>> +							fmd->num_flashes;
>>>>   		fmd->subdev_notifier.bound = subdev_notifier_bound;
>>>>   		fmd->subdev_notifier.complete = subdev_notifier_complete;
>>>>   		fmd->num_sensors = 0;
>>>> +		fmd->num_flashes = 0;
>>>>
>>>>   		ret = v4l2_async_notifier_register(&fmd->v4l2_dev,
>>>>   						&fmd->subdev_notifier);
>>>> diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h
>>>> index 0321454..feff9c8 100644
>>>> --- a/drivers/media/platform/exynos4-is/media-dev.h
>>>> +++ b/drivers/media/platform/exynos4-is/media-dev.h
>>>> @@ -34,6 +34,8 @@
>>>>
>>>>   #define FIMC_MAX_SENSORS	4
>>>>   #define FIMC_MAX_CAMCLKS	2
>>>> +#define FIMC_MAX_FLASHES	2
>>>> +#define FIMC_MAX_ASYNC_SUBDEVS (FIMC_MAX_SENSORS + FIMC_MAX_FLASHES)
>>>>   #define DEFAULT_SENSOR_CLK_FREQ	24000000U
>>>>
>>>>   /* LCD/ISP Writeback clocks (PIXELASYNCMx) */
>>>> @@ -93,6 +95,11 @@ struct fimc_sensor_info {
>>>>   	struct fimc_dev *host;
>>>>   };
>>>>
>>>> +struct fimc_flash_info {
>>>> +	struct v4l2_subdev *subdev;
>>>> +	struct v4l2_async_subdev asd;
>>>> +};
>>>> +
>>>>   struct cam_clk {
>>>>   	struct clk_hw hw;
>>>>   	struct fimc_md *fmd;
>>>> @@ -104,6 +111,8 @@ struct cam_clk {
>>>>    * @csis: MIPI CSIS subdevs data
>>>>    * @sensor: array of registered sensor subdevs
>>>>    * @num_sensors: actual number of registered sensors
>>>> + * @flash: array of registered flash subdevs
>>>> + * @num_flashes: actual number of registered flashes
>>>>    * @camclk: external sensor clock information
>>>>    * @fimc: array of registered fimc devices
>>>>    * @fimc_is: fimc-is data structure
>>>> @@ -123,6 +132,8 @@ struct fimc_md {
>>>>   	struct fimc_csis_info csis[CSIS_MAX_ENTITIES];
>>>>   	struct fimc_sensor_info sensor[FIMC_MAX_SENSORS];
>>>>   	int num_sensors;
>>>> +	struct fimc_flash_info flash[FIMC_MAX_FLASHES];
>>>> +	int num_flashes;
>>>>   	struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS];
>>>>   	struct clk *wbclk[FIMC_MAX_WBCLKS];
>>>>   	struct fimc_lite *fimc_lite[FIMC_LITE_MAX_DEVS];
>>>> @@ -149,7 +160,7 @@ struct fimc_md {
>>>>   	} clk_provider;
>>>>
>>>>   	struct v4l2_async_notifier subdev_notifier;
>>>> -	struct v4l2_async_subdev *async_subdevs[FIMC_MAX_SENSORS];
>>>> +	struct v4l2_async_subdev *async_subdevs[FIMC_MAX_ASYNC_SUBDEVS];
>>>>
>>>>   	bool user_subdev_api;
>>>>   	spinlock_t slock;
>>>
>>
>>
>


-- 
Best Regards,
Jacek Anaszewski

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

* Re: [PATCH v1 07/11] media: Add registration helpers for V4L2 flash sub-devices
  2015-03-20 15:03 ` [PATCH v1 07/11] media: Add registration helpers for V4L2 flash sub-devices Jacek Anaszewski
  2015-03-22  0:22   ` Sakari Ailus
@ 2015-03-25  0:40   ` Sakari Ailus
  1 sibling, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2015-03-25  0:40 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki, Hans Verkuil

Hi Jacek,

One more comment on this one:

On Fri, Mar 20, 2015 at 04:03:27PM +0100, Jacek Anaszewski wrote:
...
> +struct v4l2_flash *v4l2_flash_init(struct led_classdev_flash *fled_cdev,
> +				   const struct v4l2_flash_ops *ops,
> +				   struct v4l2_flash_ctrl_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->ops = ops;
> +	sd->dev = led_cdev->dev;
> +	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)
> +		return ERR_PTR(ret);
> +
> +	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);
> +	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 = &v4l2_flash->sd;
> +	struct led_classdev *led_cdev = &v4l2_flash->fled_cdev->led_cdev;
> +
> +	v4l2_async_unregister_subdev(sd);
> +	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);

It'd be very nice if v4l2_flash_release() could graciously behave with NULL
or negative error code as an argument, such as those produced by
v4l2_flash_init(). This makes error handling a lot easier in drivers.

-- 
Regards,

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

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

* Re: [PATCH v1 02/11] DT: Add documentation for the mfd Maxim max77693
  2015-03-23  9:54     ` Jacek Anaszewski
@ 2015-03-25  0:55       ` Sakari Ailus
  0 siblings, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2015-03-25  0:55 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki, Andrzej Hajda, Lee Jones,
	Chanwoo Choi

Hi Jacek,

On Mon, Mar 23, 2015 at 10:54:11AM +0100, Jacek Anaszewski wrote:
> Hi Sakari,
> 
> On 03/21/2015 11:49 PM, Sakari Ailus wrote:
> >Hi Jacek,
> >
> >On Fri, Mar 20, 2015 at 04:03:22PM +0100, Jacek Anaszewski wrote:
> >>+Optional properties of the LED child node:
> >>+- label : see Documentation/devicetree/bindings/leds/common.txt
> >
> >I'm still not comfortable using the label field as-is as the entity name in
> >the later patches, there's one important problem: it is not guaranteed to be
> >unique in the system.
> 
> I don't use it as-is in my patches. For max77603-led the i2c adapter id
> and client address is added to it, and for aat1290 there is '_n' suffix
> added. Nonetheless I didn't notice that the patch [1] was already
> merged. It checks if a LED class device with given name isn't already
> registered and adds a '_n" suffix if there was any. If it was exported
> I could use it in the leds-aat1290 driver and avoid depending on the
> static variable.
> 
> Whereas for I2C devices the problem doesn't exist (it is guaranteed that
> no more than one I2C client with an address can be present on the
> same bus), for devices driven through GPIOs we haven't stable unique
> identifier.
> 
> I thought that we agreed on #v4l about adding numerical postfixes
> in case of such devices.
> 
> >Do you think this could be added to
> >Documentation/devicetree/bindings/leds/common.txt, with perhaps enforcing it
> >in the LED framework? Bryan, what do you think?
> 
> The patch [1] seems to address the issue.

Replied to that, you're cc'd.

For this patch:

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

* Re: [PATCH v1 07/11] media: Add registration helpers for V4L2 flash sub-devices
  2015-03-24  8:35         ` Jacek Anaszewski
@ 2015-03-25  1:00           ` Sakari Ailus
  2015-03-25  7:29             ` Jacek Anaszewski
  0 siblings, 1 reply; 39+ messages in thread
From: Sakari Ailus @ 2015-03-25  1:00 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki, Hans Verkuil

Hi Jacek,

On Tue, Mar 24, 2015 at 09:35:05AM +0100, Jacek Anaszewski wrote:
...
> >>>>diff --git a/drivers/media/v4l2-core/v4l2-flash.c b/drivers/media/v4l2-core/v4l2-flash.c
> >>>>new file mode 100644
> >>>>index 0000000..804c2e4
> >>>>--- /dev/null
> >>>>+++ b/drivers/media/v4l2-core/v4l2-flash.c
> >>>>@@ -0,0 +1,607 @@
> >>>>+/*
> >>>>+ * 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.h>
> >>>>+#include "../../leds/leds.h"
> >>>
> >>>What do you need from leds.h? Shouldn't this be e.g. under include/linux
> >>>instead?
> 
> I need led_trigger_remove function.

It's exported but defined in what is obviously a private header file to the
framework. Could it be moved to include/linux/leds.h instead?

-- 
Regards,

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

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

* Re: [PATCH v1 09/11] DT: Add documentation for exynos4-is 'flashes' property
  2015-03-20 15:03 ` [PATCH v1 09/11] DT: Add documentation for exynos4-is 'flashes' property Jacek Anaszewski
@ 2015-03-25  1:06   ` Sakari Ailus
  2015-03-25  8:52     ` Jacek Anaszewski
  0 siblings, 1 reply; 39+ messages in thread
From: Sakari Ailus @ 2015-03-25  1:06 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki

Hi Jacek,

On Fri, Mar 20, 2015 at 04:03:29PM +0100, Jacek Anaszewski wrote:
> This patch adds a description of '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>
> ---
>  .../devicetree/bindings/media/samsung-fimc.txt     |    8 ++++++++
>  1 file changed, 8 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt
> index 922d6f8..cb0e263 100644
> --- a/Documentation/devicetree/bindings/media/samsung-fimc.txt
> +++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt
> @@ -40,6 +40,13 @@ 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:
> +
> +- flashes - Array of phandles to the flash LEDs that can be controlled by the
> +	    sub-devices contained in this media device. Flash LED is
> +	    represented by a child node of a flash LED device

This should be in
Documentation/devicetree/bindings/media/video-interfaces.txt.

Should flash devices be associated with sensors somehow rather than ISPs?
That's how they commonly are arranged, however that doesn't limit placing
them in silly places.

I'm not necessarily saying the flashes-property should be present in
sensor's DT nodes, but it'd be good to be able to make the association if
it's there.

> +	    (see Documentation/devicetree/bindings/leds/common.txt).
> +
>  The 'camera' node must include at least one 'fimc' child node.
>  
>  
> @@ -166,6 +173,7 @@ Example:
>  		clock-output-names = "cam_a_clkout", "cam_b_clkout";
>  		pinctrl-names = "default";
>  		pinctrl-0 = <&cam_port_a_clk_active>;
> +		flashes = <&camera_flash>, <&system_torch>;
>  		status = "okay";
>  		#address-cells = <1>;
>  		#size-cells = <1>;

There will be other kind of devices that have somewhat similar relationship.
They just haven't been defined yet. Lens controllers or EEPROM for instance.
The two are an integral part of a module, something which is not modelled in
DT in any way, but perhaps should be.

-- 
Kind regards,

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

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

* Re: [PATCH v1 07/11] media: Add registration helpers for V4L2 flash sub-devices
  2015-03-25  1:00           ` Sakari Ailus
@ 2015-03-25  7:29             ` Jacek Anaszewski
  0 siblings, 0 replies; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-25  7:29 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki, Hans Verkuil

Hi Sakari,

On 03/25/2015 02:00 AM, Sakari Ailus wrote:
> Hi Jacek,
>
> On Tue, Mar 24, 2015 at 09:35:05AM +0100, Jacek Anaszewski wrote:
> ...
>>>>>> diff --git a/drivers/media/v4l2-core/v4l2-flash.c b/drivers/media/v4l2-core/v4l2-flash.c
>>>>>> new file mode 100644
>>>>>> index 0000000..804c2e4
>>>>>> --- /dev/null
>>>>>> +++ b/drivers/media/v4l2-core/v4l2-flash.c
>>>>>> @@ -0,0 +1,607 @@
>>>>>> +/*
>>>>>> + * 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.h>
>>>>>> +#include "../../leds/leds.h"
>>>>>
>>>>> What do you need from leds.h? Shouldn't this be e.g. under include/linux
>>>>> instead?
>>
>> I need led_trigger_remove function.
>
> It's exported but defined in what is obviously a private header file to the
> framework. Could it be moved to include/linux/leds.h instead?
>

I guess so. I'll try to create a suitable patch.

-- 
Best Regards,
Jacek Anaszewski

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

* Re: [PATCH v1 09/11] DT: Add documentation for exynos4-is 'flashes' property
  2015-03-25  1:06   ` Sakari Ailus
@ 2015-03-25  8:52     ` Jacek Anaszewski
  2015-04-03 10:53       ` Sylwester Nawrocki
  2015-04-03 12:15       ` Sakari Ailus
  0 siblings, 2 replies; 39+ messages in thread
From: Jacek Anaszewski @ 2015-03-25  8:52 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki

Hi Sakari,

On 03/25/2015 02:06 AM, Sakari Ailus wrote:
> Hi Jacek,
>
> On Fri, Mar 20, 2015 at 04:03:29PM +0100, Jacek Anaszewski wrote:
>> This patch adds a description of '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>
>> ---
>>   .../devicetree/bindings/media/samsung-fimc.txt     |    8 ++++++++
>>   1 file changed, 8 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt
>> index 922d6f8..cb0e263 100644
>> --- a/Documentation/devicetree/bindings/media/samsung-fimc.txt
>> +++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt
>> @@ -40,6 +40,13 @@ 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:
>> +
>> +- flashes - Array of phandles to the flash LEDs that can be controlled by the
>> +	    sub-devices contained in this media device. Flash LED is
>> +	    represented by a child node of a flash LED device
>
> This should be in
> Documentation/devicetree/bindings/media/video-interfaces.txt.
>
> Should flash devices be associated with sensors somehow rather than ISPs?
> That's how they commonly are arranged, however that doesn't limit placing
> them in silly places.
>
> I'm not necessarily saying the flashes-property should be present in
> sensor's DT nodes, but it'd be good to be able to make the association if
> it's there.

I know of a SoC, which drives the flash from its on-chip ISP. The GPIO
connected to the flash controller's external strobe pin can be
configured so that the signal is routed to it from the ISP or from
CPU (for software strobe mode).

I think that Sylwester could say more in this subject.


>> +	    (see Documentation/devicetree/bindings/leds/common.txt).
>> +
>>   The 'camera' node must include at least one 'fimc' child node.
>>
>>
>> @@ -166,6 +173,7 @@ Example:
>>   		clock-output-names = "cam_a_clkout", "cam_b_clkout";
>>   		pinctrl-names = "default";
>>   		pinctrl-0 = <&cam_port_a_clk_active>;
>> +		flashes = <&camera_flash>, <&system_torch>;
>>   		status = "okay";
>>   		#address-cells = <1>;
>>   		#size-cells = <1>;
>
> There will be other kind of devices that have somewhat similar relationship.
> They just haven't been defined yet. Lens controllers or EEPROM for instance.
> The two are an integral part of a module, something which is not modelled in
> DT in any way, but perhaps should be.

Do you suggest using more generic name than 'flashes'?

-- 
Best Regards,
Jacek Anaszewski

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

* Re: [PATCH v1 02/11] DT: Add documentation for the mfd Maxim max77693
  2015-03-20 15:03 ` [PATCH v1 02/11] DT: Add documentation for the mfd Maxim max77693 Jacek Anaszewski
  2015-03-21 22:49   ` Sakari Ailus
@ 2015-03-26 14:06   ` Lee Jones
  1 sibling, 0 replies; 39+ messages in thread
From: Lee Jones @ 2015-03-26 14:06 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, sakari.ailus, s.nawrocki, Andrzej Hajda,
	Chanwoo Choi

On Fri, 20 Mar 2015, Jacek Anaszewski wrote:

> This patch adds device tree binding documentation for
> the flash cell of the Maxim max77693 multifunctional device.
> 
> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
> Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> Cc: Lee Jones <lee.jones@linaro.org>
> Cc: Chanwoo Choi <cw00.choi@samsung.com>
> Cc: Bryan Wu <cooloney@gmail.com>
> Cc: Richard Purdie <rpurdie@rpsys.net>
> ---
>  Documentation/devicetree/bindings/mfd/max77693.txt |   61 ++++++++++++++++++++
>  1 file changed, 61 insertions(+)

Bryan and/or one of the DT folks really need to Ack this.

> diff --git a/Documentation/devicetree/bindings/mfd/max77693.txt b/Documentation/devicetree/bindings/mfd/max77693.txt
> index 38e6440..15c546e 100644
> --- a/Documentation/devicetree/bindings/mfd/max77693.txt
> +++ b/Documentation/devicetree/bindings/mfd/max77693.txt
> @@ -76,7 +76,53 @@ Optional properties:
>      Valid values: 4300000, 4700000, 4800000, 4900000
>      Default: 4300000
>  
> +- led : the LED submodule device node
> +
> +There are two LED outputs available - FLED1 and FLED2. Each of them can
> +control a separate LED or they can be connected together to double
> +the maximum current for a single connected LED. One LED is represented
> +by one child node.
> +
> +Required properties:
> +- compatible : Must be "maxim,max77693-led".
> +
> +Optional properties:
> +- maxim,trigger-type : Flash trigger type.
> +	Possible trigger types:
> +		LEDS_TRIG_TYPE_EDGE (0) - Rising edge of the signal triggers
> +			the flash,
> +		LEDS_TRIG_TYPE_LEVEL (1) - Strobe pulse length controls duration
> +			of the flash.
> +- maxim,boost-mode :
> +	In boost mode the device can produce up to 1.2A of total current
> +	on both outputs. The maximum current on each output is reduced
> +	to 625mA then. If not enabled explicitly, boost setting defaults to
> +	LEDS_BOOST_FIXED in case both current sources are used.
> +	Possible values:
> +		LEDS_BOOST_OFF (0) - no boost,
> +		LEDS_BOOST_ADAPTIVE (1) - adaptive mode,
> +		LEDS_BOOST_FIXED (2) - fixed mode.
> +- maxim,boost-mvout : Output voltage of the boost module in millivolts.
> +- maxim,mvsys-min : Low input voltage level in millivolts. Flash is not fired
> +	if chip estimates that system voltage could drop below this level due
> +	to flash power consumption.
> +
> +Required properties of the LED child node:
> +- led-sources : see Documentation/devicetree/bindings/leds/common.txt;
> +		device current output identifiers: 0 - FLED1, 1 - FLED2
> +
> +Optional properties of the LED child node:
> +- label : see Documentation/devicetree/bindings/leds/common.txt
> +- max-microamp : see Documentation/devicetree/bindings/leds/common.txt
> +		Range: 15625 - 250000
> +- flash-max-microamp : see Documentation/devicetree/bindings/leds/common.txt
> +		Range: 15625 - 1000000
> +- flash-timeout-us : see Documentation/devicetree/bindings/leds/common.txt
> +		Range: 62500 - 1000000
> +
>  Example:
> +#include <dt-bindings/leds/common.h>
> +
>  	max77693@66 {
>  		compatible = "maxim,max77693";
>  		reg = <0x66>;
> @@ -117,5 +163,20 @@ Example:
>  			maxim,thermal-regulation-celsius = <75>;
>  			maxim,battery-overcurrent-microamp = <3000000>;
>  			maxim,charge-input-threshold-microvolt = <4300000>;
> +
> +		led {
> +			compatible = "maxim,max77693-led";
> +			maxim,trigger-type = <LEDS_TRIG_TYPE_LEVEL>;
> +			maxim,boost-mode = <LEDS_BOOST_FIXED>;
> +			maxim,boost-mvout = <5000>;
> +			maxim,mvsys-min = <2400>;
> +
> +			camera_flash: flash-led {
> +				label = "max77693-flash";
> +				led-sources = <0>, <1>;
> +				max-microamp = <500000>;
> +				flash-max-microamp = <1250000>;
> +				flash-timeout-us = <1000000>;
> +			};
>  		};
>  	};

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

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

* Re: [PATCH v1 09/11] DT: Add documentation for exynos4-is 'flashes' property
  2015-03-25  8:52     ` Jacek Anaszewski
@ 2015-04-03 10:53       ` Sylwester Nawrocki
  2015-04-30 12:53         ` Jacek Anaszewski
  2015-04-03 12:15       ` Sakari Ailus
  1 sibling, 1 reply; 39+ messages in thread
From: Sylwester Nawrocki @ 2015-04-03 10:53 UTC (permalink / raw)
  To: Jacek Anaszewski, Sakari Ailus
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie

Hello,

On 25/03/15 09:52, Jacek Anaszewski wrote:
> On 03/25/2015 02:06 AM, Sakari Ailus wrote:
>> On Fri, Mar 20, 2015 at 04:03:29PM +0100, Jacek Anaszewski wrote:
>>> This patch adds a description of '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>
>>> ---
>>>   .../devicetree/bindings/media/samsung-fimc.txt     |    8 ++++++++
>>>   1 file changed, 8 insertions(+)
>>>
>>> diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt
>>> index 922d6f8..cb0e263 100644
>>> --- a/Documentation/devicetree/bindings/media/samsung-fimc.txt
>>> +++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt
>>> @@ -40,6 +40,13 @@ 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:
>>> +
>>> +- flashes - Array of phandles to the flash LEDs that can be controlled by the
>>> +	    sub-devices contained in this media device. Flash LED is
>>> +	    represented by a child node of a flash LED device
>>
>> This should be in
>> Documentation/devicetree/bindings/media/video-interfaces.txt.
>>
>> Should flash devices be associated with sensors somehow rather than ISPs?
>> That's how they commonly are arranged, however that doesn't limit placing
>> them in silly places.
>>
>> I'm not necessarily saying the flashes-property should be present in
>> sensor's DT nodes, but it'd be good to be able to make the association if
>> it's there.

IMHO 'flashes' is a misleading name, these are simply high brightness LEDs
which can work as camera flash or auxiliary light for camcording, in context of
a camera device.

The led DT nodes which the entries of above flashes property is pointing to
have a text label, which presumably could be used to associate a LED device
with an image sensor. That said, I think we should allow a property as above
'flashes' be placed in aggregate camera node and also in sensor device node.
I think it should be left to the bridge/ISP binding to choose one option or
the other.

For now I would propose to rename the "flashes" property to "samsung,leds" or
"leds" and leave it in "camera" node.

> I know of a SoC, which drives the flash from its on-chip ISP. The GPIO
> connected to the flash controller's external strobe pin can be
> configured so that the signal is routed to it from the ISP or from
> CPU (for software strobe mode).
> 
> I think that Sylwester could say more in this subject.
> 
> 
>>> +	    (see Documentation/devicetree/bindings/leds/common.txt).
>>> +
>>>   The 'camera' node must include at least one 'fimc' child node.
>>>
>>>
>>> @@ -166,6 +173,7 @@ Example:
>>>   		clock-output-names = "cam_a_clkout", "cam_b_clkout";
>>>   		pinctrl-names = "default";
>>>   		pinctrl-0 = <&cam_port_a_clk_active>;
>>> +		flashes = <&camera_flash>, <&system_torch>;
>>>   		status = "okay";
>>>   		#address-cells = <1>;
>>>   		#size-cells = <1>;
>>
>> There will be other kind of devices that have somewhat similar relationship.
>> They just haven't been defined yet. Lens controllers or EEPROM for instance.
>> The two are an integral part of a module, something which is not modelled in
>> DT in any way, but perhaps should be.

Indeed, I'd say it belongs to a particular image sensor (camera module) binding
to describe each its physical subdevices, i.e. if a pointer to lens or EEPROM
is needed in the main module DT node.

> Do you suggest using more generic name than 'flashes'?


-- 
Regards,
Sylwester

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

* Re: [PATCH v1 09/11] DT: Add documentation for exynos4-is 'flashes' property
  2015-03-25  8:52     ` Jacek Anaszewski
  2015-04-03 10:53       ` Sylwester Nawrocki
@ 2015-04-03 12:15       ` Sakari Ailus
  1 sibling, 0 replies; 39+ messages in thread
From: Sakari Ailus @ 2015-04-03 12:15 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie, s.nawrocki

Hi Jacek,

On Wed, Mar 25, 2015 at 09:52:02AM +0100, Jacek Anaszewski wrote:
> Hi Sakari,
> 
> On 03/25/2015 02:06 AM, Sakari Ailus wrote:
> >Hi Jacek,
> >
> >On Fri, Mar 20, 2015 at 04:03:29PM +0100, Jacek Anaszewski wrote:
> >>This patch adds a description of '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>
> >>---
> >>  .../devicetree/bindings/media/samsung-fimc.txt     |    8 ++++++++
> >>  1 file changed, 8 insertions(+)
> >>
> >>diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt
> >>index 922d6f8..cb0e263 100644
> >>--- a/Documentation/devicetree/bindings/media/samsung-fimc.txt
> >>+++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt
> >>@@ -40,6 +40,13 @@ 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:
> >>+
> >>+- flashes - Array of phandles to the flash LEDs that can be controlled by the
> >>+	    sub-devices contained in this media device. Flash LED is
> >>+	    represented by a child node of a flash LED device
> >
> >This should be in
> >Documentation/devicetree/bindings/media/video-interfaces.txt.
> >
> >Should flash devices be associated with sensors somehow rather than ISPs?
> >That's how they commonly are arranged, however that doesn't limit placing
> >them in silly places.
> >
> >I'm not necessarily saying the flashes-property should be present in
> >sensor's DT nodes, but it'd be good to be able to make the association if
> >it's there.
> 
> I know of a SoC, which drives the flash from its on-chip ISP. The GPIO
> connected to the flash controller's external strobe pin can be
> configured so that the signal is routed to it from the ISP or from
> CPU (for software strobe mode).
> 
> I think that Sylwester could say more in this subject.

Yeah, some ISPs can also generate strobe signals. Otherwise this would be
the job of the sensor, sadly many module vendors ignore the sensor strobe
signal routing even when it's available. The sensor is by far in the best
position to trigger the flash since it has the best readout timing
information.

Currently we have no way to express this, I think what's first needed is the
data in the DT. Probably the flash driver and the driver that provides the
strobe signal should both be aware of this. User space interface wise the
strobe source control could be used --- we'd just need new menu entries.

> 
> 
> >>+	    (see Documentation/devicetree/bindings/leds/common.txt).
> >>+
> >>  The 'camera' node must include at least one 'fimc' child node.
> >>
> >>
> >>@@ -166,6 +173,7 @@ Example:
> >>  		clock-output-names = "cam_a_clkout", "cam_b_clkout";
> >>  		pinctrl-names = "default";
> >>  		pinctrl-0 = <&cam_port_a_clk_active>;
> >>+		flashes = <&camera_flash>, <&system_torch>;
> >>  		status = "okay";
> >>  		#address-cells = <1>;
> >>  		#size-cells = <1>;
> >
> >There will be other kind of devices that have somewhat similar relationship.
> >They just haven't been defined yet. Lens controllers or EEPROM for instance.
> >The two are an integral part of a module, something which is not modelled in
> >DT in any way, but perhaps should be.
> 
> Do you suggest using more generic name than 'flashes'?

So far I don't have a better suggestion for the name of the property.
However, it'd be good to be able to associate the flash to a sensor. I
wonder how wrong would it be to put the flashes property to the port node...

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

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

* Re: [PATCH v1 09/11] DT: Add documentation for exynos4-is 'flashes' property
  2015-04-03 10:53       ` Sylwester Nawrocki
@ 2015-04-30 12:53         ` Jacek Anaszewski
  0 siblings, 0 replies; 39+ messages in thread
From: Jacek Anaszewski @ 2015-04-30 12:53 UTC (permalink / raw)
  To: Sylwester Nawrocki, Sakari Ailus
  Cc: linux-leds, linux-media, devicetree, kyungmin.park, pavel,
	cooloney, rpurdie

Hi Sakari and Sylwester,

On 04/03/2015 12:53 PM, Sylwester Nawrocki wrote:
> Hello,
>
> On 25/03/15 09:52, Jacek Anaszewski wrote:
>> On 03/25/2015 02:06 AM, Sakari Ailus wrote:
>>> On Fri, Mar 20, 2015 at 04:03:29PM +0100, Jacek Anaszewski wrote:
>>>> This patch adds a description of '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>
>>>> ---
>>>>    .../devicetree/bindings/media/samsung-fimc.txt     |    8 ++++++++
>>>>    1 file changed, 8 insertions(+)
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/media/samsung-fimc.txt b/Documentation/devicetree/bindings/media/samsung-fimc.txt
>>>> index 922d6f8..cb0e263 100644
>>>> --- a/Documentation/devicetree/bindings/media/samsung-fimc.txt
>>>> +++ b/Documentation/devicetree/bindings/media/samsung-fimc.txt
>>>> @@ -40,6 +40,13 @@ 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:
>>>> +
>>>> +- flashes - Array of phandles to the flash LEDs that can be controlled by the
>>>> +	    sub-devices contained in this media device. Flash LED is
>>>> +	    represented by a child node of a flash LED device
>>>
>>> This should be in
>>> Documentation/devicetree/bindings/media/video-interfaces.txt.

This file documents DT nodes starting from the level one below
the camera node aggregating all the devices belonging to the media
device (I am referring to the 'camera' node from the file
arch/arm/boot/dts/exynos4412-trats2.dts). Should 'leds' property be
put there, the aggregating node would have to be described there at
first. I am wondering whether video-interfaces is a suitable place
for documenting illuminators, though.

>>> Should flash devices be associated with sensors somehow rather than ISPs?
>>> That's how they commonly are arranged, however that doesn't limit placing
>>> them in silly places.
>>>
>>> I'm not necessarily saying the flashes-property should be present in
>>> sensor's DT nodes, but it'd be good to be able to make the association if
>>> it's there.
>
> IMHO 'flashes' is a misleading name, these are simply high brightness LEDs
> which can work as camera flash or auxiliary light for camcording, in context of
> a camera device.
>
> The led DT nodes which the entries of above flashes property is pointing to
> have a text label, which presumably could be used to associate a LED device
> with an image sensor. That said, I think we should allow a property as above
> 'flashes' be placed in aggregate camera node and also in sensor device node.
> I think it should be left to the bridge/ISP binding to choose one option or
> the other.

I agree.

> For now I would propose to rename the "flashes" property to "samsung,leds" or
> "leds" and leave it in "camera" node.

How about flash-leds?

>> I know of a SoC, which drives the flash from its on-chip ISP. The GPIO
>> connected to the flash controller's external strobe pin can be
>> configured so that the signal is routed to it from the ISP or from
>> CPU (for software strobe mode).
>>
>> I think that Sylwester could say more in this subject.
>>
>>
>>>> +	    (see Documentation/devicetree/bindings/leds/common.txt).
>>>> +
>>>>    The 'camera' node must include at least one 'fimc' child node.
>>>>
>>>>
>>>> @@ -166,6 +173,7 @@ Example:
>>>>    		clock-output-names = "cam_a_clkout", "cam_b_clkout";
>>>>    		pinctrl-names = "default";
>>>>    		pinctrl-0 = <&cam_port_a_clk_active>;
>>>> +		flashes = <&camera_flash>, <&system_torch>;
>>>>    		status = "okay";
>>>>    		#address-cells = <1>;
>>>>    		#size-cells = <1>;
>>>
>>> There will be other kind of devices that have somewhat similar relationship.
>>> They just haven't been defined yet. Lens controllers or EEPROM for instance.
>>> The two are an integral part of a module, something which is not modelled in
>>> DT in any way, but perhaps should be.
>
> Indeed, I'd say it belongs to a particular image sensor (camera module) binding
> to describe each its physical subdevices, i.e. if a pointer to lens or EEPROM
> is needed in the main module DT node.
>
>> Do you suggest using more generic name than 'flashes'?
>
>


-- 
Best Regards,
Jacek Anaszewski

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

end of thread, other threads:[~2015-04-30 12:53 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-03-20 15:03 [PATCH v1 00/11] LED / flash API integration Jacek Anaszewski
2015-03-20 15:03 ` [PATCH v1 01/11] leds: Add support for max77693 mfd flash cell Jacek Anaszewski
2015-03-21 22:44   ` Sakari Ailus
2015-03-23 13:22     ` Jacek Anaszewski
2015-03-20 15:03 ` [PATCH v1 02/11] DT: Add documentation for the mfd Maxim max77693 Jacek Anaszewski
2015-03-21 22:49   ` Sakari Ailus
2015-03-23  9:54     ` Jacek Anaszewski
2015-03-25  0:55       ` Sakari Ailus
2015-03-26 14:06   ` Lee Jones
2015-03-20 15:03 ` [PATCH v1 03/11] leds: Add driver for AAT1290 current regulator Jacek Anaszewski
2015-03-21 22:55   ` Sakari Ailus
2015-03-20 15:03 ` [PATCH v1 04/11] of: Add Skyworks Solutions, Inc. vendor prefix Jacek Anaszewski
2015-03-20 15:03 ` [PATCH v1 05/11] DT: Add documentation for the Skyworks AAT1290 Jacek Anaszewski
2015-03-20 15:03 ` [PATCH v1 07/11] media: Add registration helpers for V4L2 flash sub-devices Jacek Anaszewski
2015-03-22  0:22   ` Sakari Ailus
2015-03-23 15:08     ` Jacek Anaszewski
2015-03-23 22:35       ` Sakari Ailus
2015-03-24  8:35         ` Jacek Anaszewski
2015-03-25  1:00           ` Sakari Ailus
2015-03-25  7:29             ` Jacek Anaszewski
2015-03-25  0:40   ` Sakari Ailus
     [not found] ` <1426863811-12516-1-git-send-email-j.anaszewski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
2015-03-20 15:03   ` [PATCH v1 06/11] exynos4-is: Add support for v4l2-flash subdevs Jacek Anaszewski
2015-03-20 15:03     ` Jacek Anaszewski
     [not found]     ` <1426863811-12516-7-git-send-email-j.anaszewski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
2015-03-22  1:21       ` Sakari Ailus
2015-03-22  1:21         ` Sakari Ailus
2015-03-23 15:32         ` Jacek Anaszewski
2015-03-23 22:39           ` Sakari Ailus
2015-03-24  8:52             ` Jacek Anaszewski
2015-03-20 15:03   ` [PATCH v1 08/11] Documentation: leds: Add description of v4l2-flash sub-device Jacek Anaszewski
2015-03-20 15:03     ` Jacek Anaszewski
2015-03-20 15:03 ` [PATCH v1 09/11] DT: Add documentation for exynos4-is 'flashes' property Jacek Anaszewski
2015-03-25  1:06   ` Sakari Ailus
2015-03-25  8:52     ` Jacek Anaszewski
2015-04-03 10:53       ` Sylwester Nawrocki
2015-04-30 12:53         ` Jacek Anaszewski
2015-04-03 12:15       ` Sakari Ailus
2015-03-20 15:03 ` [PATCH v1 10/11] leds: max77693: add support for V4L2 Flash sub-device Jacek Anaszewski
2015-03-22  0:28   ` Sakari Ailus
2015-03-20 15:03 ` [PATCH v1 11/11] leds: aat1290: " Jacek Anaszewski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.