All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH/RFC v5 0/3] LED / flash API integration - LED Flash Class drivers
@ 2014-08-20 13:43 ` Jacek Anaszewski
  0 siblings, 0 replies; 7+ messages in thread
From: Jacek Anaszewski @ 2014-08-20 13:43 UTC (permalink / raw)
  To: linux-leds-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-media-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ,
	b.zolnierkie-Sze3O3UU22JBDgjK7y7TUQ, Jacek Anaszewski

This patch set is the follow-up of the LED / flash API integration
series [1]. For clarity reasons the patchset has been split into
five subsets:

- LED Flash Class
- Flash Manager
- V4L2 Flash
- LED Flash Class drivers
- Documentation

The series is based on linux-next-20140820.

Thanks,
Jacek Anaszewski

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

Jacek Anaszewski (3):
  mfd: max77693: Fix register enum name
  leds: Add support for max77693 mfd flash cell
  leds: Add driver for AAT1290 current regulator

 drivers/leds/Kconfig                 |   15 +
 drivers/leds/Makefile                |    2 +
 drivers/leds/leds-aat1290.c          |  448 +++++++++++++++
 drivers/leds/leds-max77693.c         | 1048 ++++++++++++++++++++++++++++++++++
 drivers/mfd/max77693.c               |    5 +-
 include/linux/mfd/max77693-private.h |   61 +-
 include/linux/mfd/max77693.h         |   40 ++
 7 files changed, 1617 insertions(+), 2 deletions(-)
 create mode 100644 drivers/leds/leds-aat1290.c
 create mode 100644 drivers/leds/leds-max77693.c

-- 
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	[flat|nested] 7+ messages in thread

* [PATCH/RFC v5 0/3] LED / flash API integration - LED Flash Class drivers
@ 2014-08-20 13:43 ` Jacek Anaszewski
  0 siblings, 0 replies; 7+ messages in thread
From: Jacek Anaszewski @ 2014-08-20 13:43 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski

This patch set is the follow-up of the LED / flash API integration
series [1]. For clarity reasons the patchset has been split into
five subsets:

- LED Flash Class
- Flash Manager
- V4L2 Flash
- LED Flash Class drivers
- Documentation

The series is based on linux-next-20140820.

Thanks,
Jacek Anaszewski

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

Jacek Anaszewski (3):
  mfd: max77693: Fix register enum name
  leds: Add support for max77693 mfd flash cell
  leds: Add driver for AAT1290 current regulator

 drivers/leds/Kconfig                 |   15 +
 drivers/leds/Makefile                |    2 +
 drivers/leds/leds-aat1290.c          |  448 +++++++++++++++
 drivers/leds/leds-max77693.c         | 1048 ++++++++++++++++++++++++++++++++++
 drivers/mfd/max77693.c               |    5 +-
 include/linux/mfd/max77693-private.h |   61 +-
 include/linux/mfd/max77693.h         |   40 ++
 7 files changed, 1617 insertions(+), 2 deletions(-)
 create mode 100644 drivers/leds/leds-aat1290.c
 create mode 100644 drivers/leds/leds-max77693.c

-- 
1.7.9.5


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

* [PATCH/RFC v5 1/3] mfd: max77693: Fix register enum name
  2014-08-20 13:43 ` Jacek Anaszewski
  (?)
@ 2014-08-20 13:43 ` Jacek Anaszewski
  2014-08-21 16:02   ` Lee Jones
  -1 siblings, 1 reply; 7+ messages in thread
From: Jacek Anaszewski @ 2014-08-20 13:43 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Lee Jones,
	SangYoung Son, Samuel Ortiz

According to the MAX77693 documentation the name of
the register is FLASH_STATUS.

Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Lee Jones <lee.jones@linaro.org>
Cc: SangYoung Son <hello.son@smasung.com>
Cc: Samuel Ortiz <sameo@linux.intel.com>
---
 include/linux/mfd/max77693-private.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h
index c466ff3..615f121 100644
--- a/include/linux/mfd/max77693-private.h
+++ b/include/linux/mfd/max77693-private.h
@@ -46,7 +46,7 @@ enum max77693_pmic_reg {
 	MAX77693_LED_REG_VOUT_FLASH2			= 0x0C,
 	MAX77693_LED_REG_FLASH_INT			= 0x0E,
 	MAX77693_LED_REG_FLASH_INT_MASK			= 0x0F,
-	MAX77693_LED_REG_FLASH_INT_STATUS		= 0x10,
+	MAX77693_LED_REG_FLASH_STATUS			= 0x10,
 
 	MAX77693_PMIC_REG_PMIC_ID1			= 0x20,
 	MAX77693_PMIC_REG_PMIC_ID2			= 0x21,
-- 
1.7.9.5

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

* [PATCH/RFC v5 2/3] leds: Add support for max77693 mfd flash cell
  2014-08-20 13:43 ` Jacek Anaszewski
  (?)
  (?)
@ 2014-08-20 13:43 ` Jacek Anaszewski
  2014-08-21 16:00   ` Lee Jones
  -1 siblings, 1 reply; 7+ messages in thread
From: Jacek Anaszewski @ 2014-08-20 13:43 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Andrzej Hajda,
	Lee Jones, Bryan Wu, Richard Purdie, SangYoung Son, Samuel Ortiz

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

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

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index f8a26ec..27031ab 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -465,6 +465,15 @@ config LEDS_TCA6507
 	  LED driver chips accessed via the I2C bus.
 	  Driver support brightness control and hardware-assisted blinking.
 
+config LEDS_MAX77693
+	tristate "LED support for MAX77693 Flash"
+	depends on LEDS_CLASS_FLASH
+	depends on MFD_MAX77693
+	help
+	  This option enables support for the flash part of the MAX77693
+	  multifunction device. It has build in control for two leds in flash
+	  and torch mode.
+
 config LEDS_MAX8997
 	tristate "LED support for MAX8997 PMIC"
 	depends on LEDS_CLASS && MFD_MAX8997
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index d063364..8d7acec 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_LEDS_MC13783)		+= leds-mc13783.o
 obj-$(CONFIG_LEDS_NS2)			+= leds-ns2.o
 obj-$(CONFIG_LEDS_NETXBIG)		+= leds-netxbig.o
 obj-$(CONFIG_LEDS_ASIC3)		+= leds-asic3.o
+obj-$(CONFIG_LEDS_MAX77693)		+= leds-max77693.o
 obj-$(CONFIG_LEDS_MAX8997)		+= leds-max8997.o
 obj-$(CONFIG_LEDS_LM355x)		+= leds-lm355x.o
 obj-$(CONFIG_LEDS_BLINKM)		+= leds-blinkm.o
diff --git a/drivers/leds/leds-max77693.c b/drivers/leds/leds-max77693.c
new file mode 100644
index 0000000..e1386d3
--- /dev/null
+++ b/drivers/leds/leds-max77693.c
@@ -0,0 +1,1048 @@
+/*
+ * LED Flash Class driver for the flash cell of max77693 mfd.
+ *
+ *	Copyright (C) 2014, Samsung Electronics Co., Ltd.
+ *
+ *	Authors: Jacek Anaszewski <j.anaszewski@samsung.com>
+ *		 Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <asm/div64.h>
+#include <linux/led-class-flash.h>
+#include <linux/led-flash-manager.h>
+#include <linux/mfd/max77693.h>
+#include <linux/mfd/max77693-private.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-flash.h>
+
+#define MAX77693_LED_NAME_1		"max77693-flash_1"
+#define MAX77693_LED_NAME_2		"max77693-flash_2"
+
+#define MODE_OFF		0
+#define MODE_FLASH1		(1 << 0)
+#define MODE_FLASH2		(1 << 1)
+#define MODE_TORCH1		(1 << 2)
+#define MODE_TORCH2		(1 << 3)
+#define MODE_FLASH_EXTERNAL1	(1 << 4)
+#define MODE_FLASH_EXTERNAL2	(1 << 5)
+
+enum {
+	FLED1,
+	FLED2
+};
+
+enum {
+	FLASH,
+	TORCH
+};
+
+struct max77693_sub_led {
+	struct led_classdev_flash ldev;
+	struct work_struct work_brightness_set;
+	struct v4l2_flash *v4l2_flash;
+
+	unsigned int torch_brightness;
+	unsigned int flash_timeout;
+};
+
+struct max77693_led {
+	struct regmap *regmap;
+	struct platform_device *pdev;
+	struct max77693_led_platform_data *pdata;
+	struct mutex lock;
+
+	struct max77693_sub_led sub_leds[2];
+
+	unsigned int current_flash_timeout;
+	unsigned int mode_flags;
+	u8 torch_iout_reg;
+	bool iout_joint;
+	int strobing_sub_led_id;
+};
+
+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_led *ldev1_to_led(
+					struct led_classdev_flash *ldev)
+{
+	struct max77693_sub_led *sub_led = container_of(ldev,
+						struct max77693_sub_led,
+						ldev);
+	return container_of(sub_led, struct max77693_led, sub_leds[0]);
+}
+
+static inline struct max77693_led *ldev2_to_led(
+					struct led_classdev_flash *ldev)
+{
+	struct max77693_sub_led *sub_led = container_of(ldev,
+						struct max77693_sub_led,
+						ldev);
+	return container_of(sub_led, struct max77693_led, sub_leds[1]);
+}
+
+static u8 max77693_led_vsys_to_reg(u32 mv)
+{
+	return ((mv - MAX_FLASH1_VSYS_MIN) / MAX_FLASH1_VSYS_STEP) << 2;
+}
+
+static u8 max77693_led_vout_to_reg(u32 mv)
+{
+	return (mv - FLASH_VOUT_MIN) / FLASH_VOUT_STEP + FLASH_VOUT_RMIN;
+}
+
+/* split composite current @i into two @iout according to @imax weights */
+static void max77693_calc_iout(u32 iout[2], u32 i, u32 imax[2])
+{
+	u64 t = i;
+
+	t *= imax[1];
+	do_div(t, imax[0] + imax[1]);
+
+	iout[1] = (u32)t / FLASH_IOUT_STEP * FLASH_IOUT_STEP;
+	iout[0] = i - iout[1];
+}
+
+static int max77693_set_mode(struct max77693_led *led, unsigned int mode)
+{
+	struct max77693_led_platform_data *p = led->pdata;
+	struct regmap *rmap = led->regmap;
+	int ret, v = 0;
+
+	if (mode & MODE_TORCH1) {
+		if (p->trigger[FLED1] & MAX77693_LED_TRIG_SOFT)
+			v |= FLASH_EN_ON << TORCH_EN_SHIFT(1);
+	}
+
+	if (mode & MODE_TORCH2) {
+		if (p->trigger[FLED2] & MAX77693_LED_TRIG_SOFT)
+			v |= FLASH_EN_ON << TORCH_EN_SHIFT(2);
+	}
+
+	if (mode & MODE_FLASH1) {
+		if (p->trigger[FLED1] & MAX77693_LED_TRIG_SOFT)
+			v |= FLASH_EN_ON << FLASH_EN_SHIFT(1);
+	} else if (mode & MODE_FLASH_EXTERNAL1) {
+		if (p->trigger[FLED1] & MAX77693_LED_TRIG_EXT)
+			v |= FLASH_EN_FLASH << FLASH_EN_SHIFT(2);
+		/*
+		 * Enable hw triggering also for torch mode, as some camera
+		 * sensors use torch led to fathom ambient light conditions
+		 * before strobing the flash.
+		 */
+		if (p->trigger[FLED1] & MAX77693_LED_TRIG_EXT)
+			v |= FLASH_EN_TORCH << TORCH_EN_SHIFT(1);
+	}
+
+	if (mode & MODE_FLASH2) {
+		if (p->trigger[FLED2] & MAX77693_LED_TRIG_SOFT)
+			v |= FLASH_EN_ON << FLASH_EN_SHIFT(2);
+	} else if (mode & MODE_FLASH_EXTERNAL2) {
+		if (p->trigger[FLED2] & MAX77693_LED_TRIG_EXT)
+			v |= FLASH_EN_FLASH << FLASH_EN_SHIFT(2);
+		/*
+		 * Enable hw triggering also for torch mode, as some camera
+		 * sensors use torch led to fathom ambient light conditions
+		 * before strobing the flash.
+		 */
+		if (p->trigger[FLED2] & MAX77693_LED_TRIG_EXT)
+			v |= FLASH_EN_TORCH << TORCH_EN_SHIFT(2);
+	}
+
+	/* Reset the register only prior setting flash modes */
+	if (mode & ~(MODE_TORCH1 | MODE_TORCH2)) {
+		ret = regmap_write(rmap, MAX77693_LED_REG_FLASH_EN, 0);
+		if (ret < 0)
+			return ret;
+	}
+
+	return regmap_write(rmap, MAX77693_LED_REG_FLASH_EN, v);
+}
+
+static int max77693_add_mode(struct max77693_led *led, unsigned int mode)
+{
+	int ret;
+
+	/* Span mode on FLED2 for joint iouts case */
+	if (led->iout_joint)
+		mode |= (mode << 1);
+
+	/*
+	 * Torch mode once enabled remains active until turned off,
+	 * and thus no action is required in this case.
+	 */
+	if ((mode & MODE_TORCH1) &&
+	    (led->mode_flags & MODE_TORCH1))
+		return 0;
+	if ((mode & MODE_TORCH2) &&
+	    (led->mode_flags & MODE_TORCH2))
+		return 0;
+	/*
+	 * FLASH_EXTERNAL mode activates HW triggered flash and torch
+	 * modes in the device. The related register settings interfere
+	 * with SW triggerred modes, thus clear them to ensure proper
+	 * device configuration.
+	 */
+	if (mode & MODE_FLASH_EXTERNAL1)
+		led->mode_flags &= (~MODE_TORCH1 & ~MODE_FLASH1);
+	if (mode & MODE_FLASH_EXTERNAL2)
+		led->mode_flags &= (~MODE_TORCH2 & ~MODE_FLASH2);
+
+	led->mode_flags |= mode;
+
+	ret = max77693_set_mode(led, led->mode_flags);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * Clear flash mode flag after setting the mode to avoid
+	 * spurous flash strobing on every subsequent torch mode
+	 * setting.
+	 */
+	if (mode & MODE_FLASH1 || mode & MODE_FLASH_EXTERNAL1 ||
+	    mode & MODE_FLASH2 || mode & MODE_FLASH_EXTERNAL2)
+		led->mode_flags &= ~mode;
+
+	return ret;
+}
+
+static int max77693_clear_mode(struct max77693_led *led, unsigned int mode)
+{
+	int ret;
+
+	/* Span mode on FLED2 for joint iouts case */
+	if (led->iout_joint)
+		mode |= (mode << 1);
+
+	led->mode_flags &= ~mode;
+
+	ret = max77693_set_mode(led, led->mode_flags);
+
+	return ret;
+}
+
+static int max77693_set_torch_current(struct max77693_led *led,
+					int led_id,
+					u32 micro_amp)
+{
+	struct max77693_led_platform_data *p = led->pdata;
+	struct regmap *rmap = led->regmap;
+	u32 iout[2], iout_max[2];
+	u8 iout1_reg = 0, iout2_reg = 0;
+
+	iout_max[FLED1] = p->iout_torch[FLED1];
+	iout_max[FLED2] = p->iout_torch[FLED2];
+
+	if (led_id == FLED1) {
+		/*
+		 * Preclude splitting current to FLED2 if we
+		 * are driving two separate leds.
+		 */
+		if (!led->iout_joint)
+			iout_max[FLED2] = 0;
+		max77693_calc_iout(iout, micro_amp, iout_max);
+	} else if (led_id == FLED2) {
+		iout_max[FLED1] = 0;
+		max77693_calc_iout(iout, micro_amp, iout_max);
+	}
+
+	if (led_id == FLED1 || led->iout_joint) {
+		iout1_reg = max77693_led_iout_to_reg(iout[FLED1]);
+		led->torch_iout_reg &= 0xf0;
+	}
+	if (led_id == FLED2 || led->iout_joint) {
+		iout2_reg = max77693_led_iout_to_reg(iout[FLED2]);
+		led->torch_iout_reg &= 0x0f;
+	}
+
+	led->torch_iout_reg |= ((iout1_reg << 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 *led,
+					int led_id,
+					u32 micro_amp)
+{
+	struct max77693_led_platform_data *p = led->pdata;
+	struct regmap *rmap = led->regmap;
+	u32 iout[2], iout_max[2];
+	u8 iout1_reg, iout2_reg;
+	int ret = -EINVAL;
+
+	iout_max[FLED1] = p->iout_flash[FLED1];
+	iout_max[FLED2] = p->iout_flash[FLED2];
+
+	if (led_id == FLED1) {
+		/*
+		 * Preclude splitting current to FLED2 if we
+		 * are driving two separate leds.
+		 */
+		if (!led->iout_joint)
+			iout_max[FLED2] = 0;
+		max77693_calc_iout(iout, micro_amp, iout_max);
+	} else if (led_id == FLED2) {
+		iout_max[FLED1] = 0;
+		max77693_calc_iout(iout, micro_amp, iout_max);
+	}
+
+	if (led_id == FLED1 || led->iout_joint) {
+		iout1_reg = max77693_led_iout_to_reg(iout[FLED1]);
+		ret = regmap_write(rmap, MAX77693_LED_REG_IFLASH1,
+							iout1_reg);
+		if (ret < 0)
+			return ret;
+	}
+	if (led_id == FLED2 || led->iout_joint) {
+		iout2_reg = max77693_led_iout_to_reg(iout[FLED2]);
+		ret = regmap_write(rmap, MAX77693_LED_REG_IFLASH2,
+							iout2_reg);
+	}
+
+	return ret;
+}
+
+static int max77693_set_timeout(struct max77693_led *led, u32 timeout)
+{
+	struct max77693_led_platform_data *p = led->pdata;
+	struct regmap *rmap = led->regmap;
+	u8 v;
+	int ret;
+
+	v = max77693_flash_timeout_to_reg(timeout);
+
+	if (p->trigger_type[FLASH] == MAX77693_LED_TRIG_TYPE_LEVEL)
+		v |= FLASH_TMR_LEVEL;
+
+	ret = regmap_write(rmap, MAX77693_LED_REG_FLASH_TIMER, v);
+	if (ret < 0)
+		return ret;
+
+	led->current_flash_timeout = timeout;
+
+	return 0;
+}
+
+static int max77693_strobe_status_get(struct max77693_led *led, bool *state)
+{
+	struct regmap *rmap = led->regmap;
+	unsigned int v;
+	int ret;
+
+	ret = regmap_read(rmap, MAX77693_LED_REG_FLASH_STATUS, &v);
+	if (ret < 0)
+		return ret;
+
+	*state = v & FLASH_STATUS_FLASH_ON;
+
+	return ret;
+}
+
+static int max77693_int_flag_get(struct max77693_led *led, unsigned int *v)
+{
+	struct regmap *rmap = led->regmap;
+
+	return regmap_read(rmap, MAX77693_LED_REG_FLASH_INT, v);
+}
+
+static int max77693_setup(struct max77693_led *led)
+{
+	struct max77693_led_platform_data *p = led->pdata;
+	struct regmap *rmap = led->regmap;
+	int i, 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] = p->iout_flash[FLED1] +
+					p->iout_flash[FLED2];
+	} else {
+		first_led = p->fleds[FLED1] ? FLED1 : FLED2;
+		last_led = p->num_leds == 2 ? FLED2 : first_led;
+		max_flash_curr[FLED1] = p->iout_flash[FLED1];
+		max_flash_curr[FLED2] = p->iout_flash[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;
+
+	ret = max77693_set_timeout(led,	p->flash_timeout);
+	if (ret < 0)
+		return ret;
+
+	if (p->low_vsys > 0)
+		v = max77693_led_vsys_to_reg(p->low_vsys) |
+						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 (p->boost_mode == MAX77693_LED_BOOST_FIXED)
+		v = FLASH_BOOST_FIXED;
+	else
+		v = p->boost_mode | p->boost_mode << 1;
+	if (p->fleds[FLED1] && p->fleds[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(p->boost_vout);
+	ret = regmap_write(rmap, MAX77693_LED_REG_VOUT_FLASH1, v);
+	if (ret < 0)
+		return ret;
+
+	return max77693_set_mode(led, MODE_OFF);
+}
+
+static int max77693_led_brightness_set(struct max77693_led *led,
+					int led_id, enum led_brightness value)
+{
+	int ret;
+
+	mutex_lock(&led->lock);
+
+	if (value == 0) {
+		ret = max77693_clear_mode(led, MODE_TORCH1 << led_id);
+		if (ret < 0)
+			dev_dbg(&led->pdev->dev,
+				"Failed to clear torch mode (%d)\n",
+				ret);
+		goto unlock;
+	}
+
+	ret = max77693_set_torch_current(led, led_id, value * 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_TORCH1 << led_id);
+	if (ret < 0)
+		dev_dbg(&led->pdev->dev,
+			"Failed to set torch mode (%d)\n",
+			ret);
+unlock:
+	mutex_unlock(&led->lock);
+	return ret;
+}
+
+#define MAX77693_LED_BRIGHTNESS_SET_WORK(ID)				\
+static void max77693_led##ID##_brightness_set_work(			\
+					struct work_struct *work)	\
+{									\
+	struct max77693_sub_led *sub_led =				\
+			container_of(work, struct max77693_sub_led,	\
+					work_brightness_set);		\
+	struct max77693_led *led = container_of(sub_led,		\
+					struct max77693_led,		\
+					sub_leds[FLED##ID]);		\
+	struct max77693_sub_led *sub_leds = led->sub_leds;		\
+									\
+	max77693_led_brightness_set(led, FLED##ID,			\
+				sub_leds[FLED##ID].torch_brightness);	\
+}
+
+/* LED subsystem callbacks */
+
+#define MAX77693_LED_TORCH_BRIGHTNESS_SET(ID)				\
+static int max77693_led##ID##_torch_brightness_set(			\
+				struct led_classdev *led_cdev,		\
+				enum led_brightness value)		\
+{									\
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);	\
+	struct max77693_led *led = ldev##ID##_to_led(flash);		\
+									\
+	return max77693_led_brightness_set(led, FLED##ID, value);	\
+}
+
+#define MAX77693_LED_BRIGHTNESS_SET(ID)					\
+static void max77693_led##ID##_brightness_set(				\
+				struct led_classdev *led_cdev,		\
+				enum led_brightness value)		\
+{									\
+	struct led_classdev_flash *flash = lcdev_to_flash(led_cdev);	\
+	struct max77693_led *led = ldev##ID##_to_led(flash);		\
+	struct max77693_sub_led *sub_leds = led->sub_leds;		\
+									\
+	sub_leds[FLED##ID].torch_brightness = value;			\
+	schedule_work(&sub_leds[FLED##ID].work_brightness_set);		\
+}
+
+#define MAX77693_LED_FLASH_BRIGHTNESS_SET(ID)				\
+static int max77693_led##ID##_flash_brightness_set(			\
+				struct led_classdev_flash *flash,	\
+				u32 brightness)				\
+{									\
+	struct max77693_led *led = ldev##ID##_to_led(flash);		\
+	int ret;							\
+									\
+	mutex_lock(&led->lock);						\
+	ret = max77693_set_flash_current(led, FLED##ID, brightness);	\
+	mutex_unlock(&led->lock);					\
+									\
+	return ret;							\
+}
+
+#define MAX77693_LED_FLASH_STROBE_SET(ID)				\
+static int max77693_led##ID##_flash_strobe_set(				\
+				struct led_classdev_flash *flash,	\
+				bool state)				\
+{									\
+	struct max77693_led *led = ldev##ID##_to_led(flash);		\
+	struct max77693_sub_led *sub_leds = led->sub_leds;		\
+	int ret;							\
+									\
+	mutex_lock(&led->lock);						\
+									\
+	if (!state) {							\
+		ret = max77693_clear_mode(led, MODE_FLASH##ID);		\
+		goto unlock;						\
+	}								\
+									\
+	if (sub_leds[FLED##ID].flash_timeout !=				\
+				led->current_flash_timeout) {		\
+		ret = max77693_set_timeout(led,				\
+				sub_leds[FLED##ID].flash_timeout);	\
+		if (ret < 0)						\
+			goto unlock;					\
+	}								\
+									\
+	led->strobing_sub_led_id = ID;					\
+									\
+	ret = max77693_add_mode(led, MODE_FLASH##ID);			\
+									\
+unlock:									\
+	mutex_unlock(&led->lock);					\
+	return ret;							\
+}
+
+#define MAX77693_LED_FLASH_EXTERNAL_STROBE_SET(ID)			\
+static int max77693_led##ID##_external_strobe_set(			\
+				struct led_classdev_flash *flash,	\
+				bool enable)				\
+{									\
+	struct max77693_led *led = ldev##ID##_to_led(flash);		\
+	int ret;							\
+									\
+	mutex_lock(&led->lock);						\
+									\
+	if (enable)							\
+		ret = max77693_add_mode(led, MODE_FLASH_EXTERNAL##ID);	\
+	else								\
+		ret = max77693_clear_mode(led,				\
+					MODE_FLASH_EXTERNAL##ID);	\
+									\
+	mutex_unlock(&led->lock);					\
+									\
+	return ret;							\
+}
+
+#define MAX77693_LED_FLASH_FAULT_GET(ID)				\
+static int max77693_led##ID##_flash_fault_get(				\
+				struct led_classdev_flash *flash,	\
+				u32 *fault)				\
+{									\
+	struct max77693_led *led = ldev##ID##_to_led(flash);		\
+	unsigned int v;							\
+	int ret;							\
+									\
+	ret = max77693_int_flag_get(led, &v);				\
+	if (ret < 0)							\
+		return ret;						\
+									\
+	*fault = 0;							\
+									\
+	if (v & FLASH_INT_FLED##ID##_OPEN)				\
+		*fault |= LED_FAULT_OVER_VOLTAGE;			\
+	if (v & FLASH_INT_FLED##ID##_SHORT)				\
+		*fault |= LED_FAULT_SHORT_CIRCUIT;			\
+	if (v & FLASH_INT_OVER_CURRENT)					\
+		*fault |= LED_FAULT_OVER_CURRENT;			\
+									\
+	return 0;							\
+}
+
+#define MAX77693_LED_FLASH_STROBE_GET(ID)				\
+static int max77693_led##ID##_flash_strobe_get(				\
+				struct led_classdev_flash *flash,	\
+				bool *state)				\
+{									\
+	struct max77693_led *led = ldev##ID##_to_led(flash);		\
+	int ret;							\
+									\
+	if (!state)							\
+		return -EINVAL;						\
+									\
+	mutex_lock(&led->lock);						\
+									\
+	ret = max77693_strobe_status_get(led, state);			\
+									\
+	*state = !!(*state && led->strobing_sub_led_id == ID);		\
+									\
+	mutex_unlock(&led->lock);					\
+									\
+	return ret;							\
+}
+
+#define MAX77693_LED_FLASH_TIMEOUT_SET(ID)				\
+static int max77693_led##ID##_flash_timeout_set(			\
+				struct led_classdev_flash *flash,	\
+				u32 timeout)				\
+{									\
+	struct max77693_led *led = ldev##ID##_to_led(flash);		\
+	struct max77693_sub_led *sub_leds = led->sub_leds;		\
+									\
+	mutex_lock(&led->lock);						\
+	sub_leds[FLED##ID].flash_timeout = timeout;			\
+	mutex_unlock(&led->lock);					\
+									\
+	return 0;							\
+}
+
+MAX77693_LED_BRIGHTNESS_SET(1)
+MAX77693_LED_BRIGHTNESS_SET_WORK(1)
+MAX77693_LED_TORCH_BRIGHTNESS_SET(1)
+MAX77693_LED_FLASH_BRIGHTNESS_SET(1)
+MAX77693_LED_FLASH_STROBE_SET(1)
+MAX77693_LED_FLASH_STROBE_GET(1)
+MAX77693_LED_FLASH_EXTERNAL_STROBE_SET(1)
+MAX77693_LED_FLASH_TIMEOUT_SET(1)
+MAX77693_LED_FLASH_FAULT_GET(1)
+
+MAX77693_LED_BRIGHTNESS_SET(2)
+MAX77693_LED_BRIGHTNESS_SET_WORK(2)
+MAX77693_LED_TORCH_BRIGHTNESS_SET(2)
+MAX77693_LED_FLASH_BRIGHTNESS_SET(2)
+MAX77693_LED_FLASH_STROBE_SET(2)
+MAX77693_LED_FLASH_STROBE_GET(2)
+MAX77693_LED_FLASH_EXTERNAL_STROBE_SET(2)
+MAX77693_LED_FLASH_TIMEOUT_SET(2)
+MAX77693_LED_FLASH_FAULT_GET(2)
+
+static void max77693_led_parse_dt(struct max77693_led_platform_data *p,
+			    struct device_node *node)
+{
+	of_property_read_u32_array(node, "iout-torch", p->iout_torch, 2);
+	of_property_read_u32_array(node, "iout-flash", p->iout_flash, 2);
+	of_property_read_u32_array(node, "maxim,fleds", p->fleds, 2);
+	of_property_read_u32_array(node, "maxim,trigger", p->trigger, 2);
+	of_property_read_u32_array(node, "maxim,trigger-type", p->trigger_type,
+									2);
+	of_property_read_u32(node, "flash-timeout", &p->flash_timeout);
+	of_property_read_u32(node, "maxim,boost-mode", &p->boost_mode);
+	of_property_read_u32(node, "maxim,boost-vout", &p->boost_vout);
+	of_property_read_u32(node, "maxim,num-leds", &p->num_leds);
+	of_property_read_u32(node, "maxim,vsys-min", &p->low_vsys);
+}
+
+static void clamp_align(u32 *v, u32 min, u32 max, u32 step)
+{
+	*v = clamp_val(*v, min, max);
+	if (step > 1)
+		*v = (*v - min) / step * step + min;
+}
+
+static void max77693_led_validate_platform_data(
+					struct max77693_led_platform_data *p)
+{
+	u32 max;
+	int i;
+
+	p->boost_mode = clamp_val(p->boost_mode, MAX77693_LED_BOOST_NONE,
+			    MAX77693_LED_BOOST_FIXED);
+	p->num_leds = clamp_val(p->num_leds, 1, 2);
+
+	for (i = 0; i < ARRAY_SIZE(p->fleds); ++i)
+		p->fleds[i] = clamp_val(p->fleds[i], 0, 1);
+
+	/* Ensure fleds configuration is sane */
+	if (!p->fleds[FLED1] && !p->fleds[FLED2]) {
+		p->fleds[FLED1] = p->fleds[FLED2] = 1;
+		p->num_leds = 1;
+	}
+
+	/* Ensure num_leds is consistent with fleds configuration */
+	if ((!p->fleds[FLED1] || !p->fleds[FLED2]) && p->num_leds == 2)
+		p->num_leds = 1;
+
+	/*
+	 * boost must be enabled if current outputs
+	 * are connected to separate leds.
+	 */
+	if ((p->num_leds == 2 || (p->fleds[FLED1] && p->fleds[FLED2])) &&
+	    p->boost_mode == MAX77693_LED_BOOST_NONE)
+		p->boost_mode = MAX77693_LED_BOOST_FIXED;
+
+	max = p->boost_mode ? FLASH_IOUT_MAX_2LEDS : FLASH_IOUT_MAX_1LED;
+
+	if (p->fleds[FLED1]) {
+		clamp_align(&p->iout_torch[FLED1], TORCH_IOUT_MIN,
+					TORCH_IOUT_MAX, TORCH_IOUT_STEP);
+		clamp_align(&p->iout_flash[FLED1], FLASH_IOUT_MIN, max,
+							FLASH_IOUT_STEP);
+	} else {
+		p->iout_torch[FLED1] = p->iout_flash[FLED1] = 0;
+	}
+	if (p->fleds[FLED2]) {
+		clamp_align(&p->iout_torch[FLED2], TORCH_IOUT_MIN,
+					TORCH_IOUT_MAX, TORCH_IOUT_STEP);
+		clamp_align(&p->iout_flash[FLED2], FLASH_IOUT_MIN, max,
+							FLASH_IOUT_STEP);
+	} else {
+		p->iout_torch[FLED2] = p->iout_flash[FLED2] = 0;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(p->trigger); ++i)
+		p->trigger[i] = clamp_val(p->trigger[i], 0, 7);
+	for (i = 0; i < ARRAY_SIZE(p->trigger_type); ++i)
+		p->trigger_type[i] = clamp_val(p->trigger_type[i],
+					MAX77693_LED_TRIG_TYPE_EDGE,
+					MAX77693_LED_TRIG_TYPE_LEVEL);
+
+	clamp_align(&p->flash_timeout, FLASH_TIMEOUT_MIN, FLASH_TIMEOUT_MAX,
+						FLASH_TIMEOUT_STEP);
+
+	clamp_align(&p->boost_vout, FLASH_VOUT_MIN, FLASH_VOUT_MAX,
+							FLASH_VOUT_STEP);
+
+	if (p->low_vsys)
+		clamp_align(&p->low_vsys, MAX_FLASH1_VSYS_MIN,
+				MAX_FLASH1_VSYS_MAX, MAX_FLASH1_VSYS_STEP);
+}
+
+static int max77693_led_get_platform_data(struct max77693_led *led)
+{
+	struct max77693_led_platform_data *p;
+	struct device *dev = &led->pdev->dev;
+
+	if (dev->of_node) {
+		p = devm_kzalloc(dev, sizeof(*led->pdata), GFP_KERNEL);
+		if (!p)
+			return -ENOMEM;
+		max77693_led_parse_dt(p, dev->of_node);
+	} else {
+		p = dev_get_platdata(dev);
+		if (!p)
+			return -ENODEV;
+	}
+	led->pdata = p;
+
+	max77693_led_validate_platform_data(p);
+
+	return 0;
+}
+
+#define MAX77693_LED_INIT_FLASH_OPS(ID)						\
+const struct led_flash_ops flash_ops##ID = {					\
+										\
+	.flash_brightness_set	= max77693_led##ID##_flash_brightness_set,	\
+	.strobe_set 		= max77693_led##ID##_flash_strobe_set,		\
+	.strobe_get		= max77693_led##ID##_flash_strobe_get,		\
+	.timeout_set		= max77693_led##ID##_flash_timeout_set,		\
+	.external_strobe_set	= max77693_led##ID##_external_strobe_set,	\
+	.fault_get		= max77693_led##ID##_flash_fault_get,		\
+}
+
+MAX77693_LED_INIT_FLASH_OPS(1);
+MAX77693_LED_INIT_FLASH_OPS(2);
+
+static void max77693_init_flash_settings(struct max77693_led *led,
+					 struct max77693_led_settings *s,
+					 int led_id)
+{
+	struct max77693_led_platform_data *p = led->pdata;
+	struct led_flash_setting *setting;
+
+	/* Init torch intensity setting */
+	setting = &s->torch_brightness;
+	setting->min = led->iout_joint ? TORCH_IOUT_MIN * 2 :
+					 TORCH_IOUT_MIN;
+	setting->max = led->iout_joint ?
+			p->iout_torch[FLED1] + p->iout_torch[FLED2] :
+			p->iout_torch[led_id];
+	setting->step = TORCH_IOUT_STEP;
+	setting->val = setting->max;
+
+	/* Init flash intensity setting */
+	setting = &s->flash_brightness;
+	setting->min = led->iout_joint ? FLASH_IOUT_MIN * 2 :
+					 FLASH_IOUT_MIN;
+	setting->max = led->iout_joint ?
+			p->iout_flash[FLED1] + p->iout_flash[FLED2] :
+			p->iout_flash[led_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 = FLASH_TIMEOUT_MAX;
+	setting->step = FLASH_TIMEOUT_STEP;
+	setting->val = p->flash_timeout;
+}
+
+#ifdef CONFIG_V4L2_FLASH_LED_CLASS
+static void max77693_init_v4l2_ctrl_config(struct max77693_led_settings *s,
+					struct v4l2_flash_ctrl_config *config)
+{
+	struct led_flash_setting *setting;
+	struct v4l2_ctrl_config *c;
+
+	c = &config->torch_intensity;
+	setting = &s->torch_brightness;
+	c->min = setting->min;
+	c->max = setting->max;
+	c->step = setting->step;
+	c->def = setting->val;
+
+	c = &config->flash_intensity;
+	setting = &s->flash_brightness;
+	c->min = setting->min;
+	c->max = setting->max;
+	c->step = setting->step;
+	c->def = setting->val;
+
+	c = &config->flash_timeout;
+	setting = &s->flash_timeout;
+	c->min = setting->min;
+	c->max = setting->max;
+	c->step = setting->step;
+	c->def = setting->val;
+
+	/* Init flash faults config */
+	config->flash_faults =	V4L2_FLASH_FAULT_OVER_VOLTAGE |
+				V4L2_FLASH_FAULT_SHORT_CIRCUIT |
+				V4L2_FLASH_FAULT_OVER_CURRENT;
+}
+#else
+#define max77693_init_v4l2_ctrl_config(s, config)
+#endif
+
+static int max77693_register_led(struct max77693_led *led, int id)
+{
+	struct platform_device *pdev = led->pdev;
+	struct device *dev = &pdev->dev;
+	struct led_classdev_flash *flash;
+	struct led_classdev *led_cdev;
+	struct max77693_led_platform_data *p = led->pdata;
+	struct max77693_sub_led *sub_leds = led->sub_leds;
+#ifdef CONFIG_V4L2_FLASH_LED_CLASS
+	struct v4l2_flash_ctrl_config v4l2_flash_config;
+#endif
+	struct max77693_led_settings settings;
+	int ret;
+
+	flash = &sub_leds[id].ldev;
+
+	/* Init flash settings */
+	max77693_init_flash_settings(led, &settings, id);
+	/* Init V4L2 Flash controls basing on initialized settings */
+	max77693_init_v4l2_ctrl_config(&settings, &v4l2_flash_config);
+
+	/* Init led class */
+	led_cdev = &flash->led_cdev;
+
+	if (id == FLED1) {
+		led_cdev->name = MAX77693_LED_NAME_1;
+		led_cdev->brightness_set = max77693_led1_brightness_set;
+		led_cdev->torch_brightness_set =
+				max77693_led1_torch_brightness_set;
+		INIT_WORK(&sub_leds[id].work_brightness_set,
+				max77693_led1_brightness_set_work);
+		flash->ops = &flash_ops1;
+	} else {
+		led_cdev->name = MAX77693_LED_NAME_2;
+		led_cdev->brightness_set = max77693_led2_brightness_set;
+		led_cdev->torch_brightness_set =
+				max77693_led2_torch_brightness_set;
+		INIT_WORK(&sub_leds[id].work_brightness_set,
+				max77693_led2_brightness_set_work);
+		flash->ops = &flash_ops2;
+	}
+
+	led_cdev->max_brightness = settings.torch_brightness.val /
+					TORCH_IOUT_STEP;
+	led_cdev->flags |= LED_DEV_CAP_FLASH;
+
+	flash->brightness = settings.flash_brightness;
+	flash->timeout = settings.flash_timeout;
+	sub_leds[id].flash_timeout = flash->timeout.val;
+
+	if (p->trigger[id] & MAX77693_LED_TRIG_FLASH)
+		flash->has_external_strobe = true;
+
+	/* Register in the LED subsystem. */
+	ret = led_classdev_flash_register(&pdev->dev, flash,
+						dev->of_node);
+	if (ret < 0)
+		return ret;
+
+	sub_leds[id].v4l2_flash =
+			v4l2_flash_init(flash,
+					&v4l2_flash_config,
+					led_get_v4l2_flash_ops());
+
+	if (IS_ERR(sub_leds[id].v4l2_flash)) {
+		ret = PTR_ERR(sub_leds[id].v4l2_flash);
+		goto err_v4l2_flash_init;
+	}
+
+	return 0;
+
+err_v4l2_flash_init:
+	led_classdev_flash_unregister(flash);
+
+	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 *led;
+	struct max77693_led_platform_data *p;
+	struct max77693_sub_led *sub_leds;
+	int ret;
+
+	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	led->pdev = pdev;
+	led->regmap = iodev->regmap;
+	sub_leds = led->sub_leds;
+	platform_set_drvdata(pdev, led);
+	ret = max77693_led_get_platform_data(led);
+	if (ret < 0)
+		return -EINVAL;
+
+	p = led->pdata;
+	mutex_init(&led->lock);
+
+	if (p->num_leds == 1 && p->fleds[FLED1] && p->fleds[FLED2])
+		led->iout_joint = true;
+
+	ret = max77693_setup(led);
+	if (ret < 0)
+		goto unlock;
+
+	if (led->iout_joint || p->fleds[FLED1]) {
+		ret = max77693_register_led(led, FLED1);
+		if (ret < 0)
+			goto unlock;
+	}
+
+	if (!led->iout_joint && p->fleds[FLED2]) {
+		ret = max77693_register_led(led, FLED2);
+		if (ret < 0)
+			goto err_register_led2;
+	}
+
+	return 0;
+
+err_register_led2:
+	if (!p->fleds[FLED1])
+		goto unlock;
+	v4l2_flash_release(sub_leds[FLED1].v4l2_flash);
+	led_classdev_flash_unregister(&sub_leds[FLED1].ldev);
+unlock:
+	mutex_destroy(&led->lock);
+
+	return ret;
+}
+
+static int max77693_led_remove(struct platform_device *pdev)
+{
+	struct max77693_led *led = platform_get_drvdata(pdev);
+	struct max77693_led_platform_data *p = led->pdata;
+	struct max77693_sub_led *sub_leds = led->sub_leds;
+
+	if (led->iout_joint || p->fleds[FLED1]) {
+		v4l2_flash_release(sub_leds[FLED1].v4l2_flash);
+		led_classdev_flash_unregister(&sub_leds[FLED1].ldev);
+		cancel_work_sync(&sub_leds[FLED1].work_brightness_set);
+	}
+
+	if (!led->iout_joint && p->fleds[FLED2]) {
+		v4l2_flash_release(sub_leds[FLED2].v4l2_flash);
+		led_classdev_flash_unregister(&sub_leds[FLED2].ldev);
+		cancel_work_sync(&sub_leds[FLED2].work_brightness_set);
+	}
+
+	mutex_destroy(&led->lock);
+
+	return 0;
+}
+
+static struct of_device_id max77693_led_dt_match[] = {
+	{.compatible = "maxim,max77693-flash"},
+	{},
+};
+
+static struct platform_driver max77693_led_driver = {
+	.probe		= max77693_led_probe,
+	.remove		= max77693_led_remove,
+	.driver		= {
+		.name	= "max77693-flash",
+		.owner	= THIS_MODULE,
+		.of_match_table = max77693_led_dt_match,
+	},
+};
+
+module_platform_driver(max77693_led_driver);
+
+MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
+MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
+MODULE_DESCRIPTION("Maxim MAX77693 led flash driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c
index 249c139..cf008f4 100644
--- a/drivers/mfd/max77693.c
+++ b/drivers/mfd/max77693.c
@@ -44,9 +44,12 @@
 static const struct mfd_cell max77693_devs[] = {
 	{ .name = "max77693-pmic", },
 	{ .name = "max77693-charger", },
-	{ .name = "max77693-flash", },
 	{ .name = "max77693-muic", },
 	{ .name = "max77693-haptic", },
+	{
+		.name = "max77693-flash",
+		.of_compatible = "maxim,max77693-flash",
+	},
 };
 
 static const struct regmap_config max77693_regmap_config = {
diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h
index 615f121..de06377 100644
--- a/include/linux/mfd/max77693-private.h
+++ b/include/linux/mfd/max77693-private.h
@@ -85,6 +85,65 @@ enum max77693_pmic_reg {
 	MAX77693_PMIC_REG_END,
 };
 
+/* MAX77693 ITORCH register */
+#define TORCH_IOUT1_SHIFT	0
+#define TORCH_IOUT2_SHIFT	4
+#define TORCH_IOUT_MIN		15625
+#define TORCH_IOUT_MAX		250000
+#define TORCH_IOUT_STEP		15625
+
+/* MAX77693 IFLASH1 and IFLASH2 registers */
+#define FLASH_IOUT_MIN		15625
+#define FLASH_IOUT_MAX_1LED	1000000
+#define FLASH_IOUT_MAX_2LEDS	625000
+#define FLASH_IOUT_STEP		15625
+
+/* MAX77693 TORCH_TIMER register */
+#define TORCH_TMR_NO_TIMER	0x40
+#define TORCH_TIMEOUT_MIN	262000
+#define TORCH_TIMEOUT_MAX	15728000
+
+/* MAX77693 FLASH_TIMER register */
+#define FLASH_TMR_LEVEL		0x80
+#define FLASH_TIMEOUT_MIN	62500
+#define FLASH_TIMEOUT_MAX	1000000
+#define FLASH_TIMEOUT_STEP	62500
+
+/* MAX77693 FLASH_EN register */
+#define FLASH_EN_OFF		0x0
+#define FLASH_EN_FLASH		0x1
+#define FLASH_EN_TORCH		0x2
+#define FLASH_EN_ON		0x3
+#define FLASH_EN_SHIFT(x)	(6 - ((x) - 1) * 2)
+#define TORCH_EN_SHIFT(x)	(2 - ((x) - 1) * 2)
+
+/* MAX77693 MAX_FLASH1 register */
+#define MAX_FLASH1_MAX_FL_EN	0x80
+#define MAX_FLASH1_VSYS_MIN	2400
+#define MAX_FLASH1_VSYS_MAX	3400
+#define MAX_FLASH1_VSYS_STEP	33
+
+/* MAX77693 VOUT_CNTL register */
+#define FLASH_BOOST_FIXED	0x04
+#define FLASH_BOOST_LEDNUM_2	0x80
+
+/* MAX77693 VOUT_FLASH1 register */
+#define FLASH_VOUT_MIN		3300
+#define FLASH_VOUT_MAX		5500
+#define FLASH_VOUT_STEP		25
+#define FLASH_VOUT_RMIN		0x0c
+
+/* MAX77693 FLASH_STATUS register */
+#define FLASH_STATUS_FLASH_ON	BIT(3)
+#define FLASH_STATUS_TORCH_ON	BIT(2)
+
+/* MAX77693 FLASH_INT register */
+#define FLASH_INT_FLED2_OPEN	BIT(0)
+#define FLASH_INT_FLED2_SHORT	BIT(1)
+#define FLASH_INT_FLED1_OPEN	BIT(2)
+#define FLASH_INT_FLED1_SHORT	BIT(3)
+#define FLASH_INT_OVER_CURRENT	BIT(4)
+
 /* MAX77693 CHG_CNFG_00 register */
 #define CHG_CNFG_00_CHG_MASK		0x1
 #define CHG_CNFG_00_BUCK_MASK		0x4
diff --git a/include/linux/mfd/max77693.h b/include/linux/mfd/max77693.h
index 3f3dc45..f0b6585 100644
--- a/include/linux/mfd/max77693.h
+++ b/include/linux/mfd/max77693.h
@@ -63,6 +63,45 @@ struct max77693_muic_platform_data {
 	int path_uart;
 };
 
+/* MAX77693 led flash */
+
+/* triggers */
+enum max77693_led_trigger {
+	MAX77693_LED_TRIG_OFF,
+	MAX77693_LED_TRIG_FLASH,
+	MAX77693_LED_TRIG_TORCH,
+	MAX77693_LED_TRIG_EXT,
+	MAX77693_LED_TRIG_SOFT,
+};
+
+/* trigger types */
+enum max77693_led_trigger_type {
+	MAX77693_LED_TRIG_TYPE_EDGE,
+	MAX77693_LED_TRIG_TYPE_LEVEL,
+};
+
+/* boost modes */
+enum max77693_led_boost_mode {
+	MAX77693_LED_BOOST_NONE,
+	MAX77693_LED_BOOST_ADAPTIVE,
+	MAX77693_LED_BOOST_FIXED,
+};
+
+struct max77693_led_platform_data {
+	u32 fleds[2];
+	u32 iout_torch[2];
+	u32 iout_flash[2];
+	u32 trigger[2];
+	u32 trigger_type[2];
+	u32 num_leds;
+	u32 boost_mode;
+	u32 flash_timeout;
+	u32 boost_vout;
+	u32 low_vsys;
+};
+
+/* MAX77693 */
+
 struct max77693_platform_data {
 	/* regulator data */
 	struct max77693_regulator_data *regulators;
@@ -70,5 +109,6 @@ struct max77693_platform_data {
 
 	/* muic data */
 	struct max77693_muic_platform_data *muic_data;
+	struct max77693_led_platform_data *led_data;
 };
 #endif	/* __LINUX_MFD_MAX77693_H */
-- 
1.7.9.5

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

* [PATCH/RFC v5 3/3] leds: Add driver for AAT1290 current regulator
  2014-08-20 13:43 ` Jacek Anaszewski
                   ` (2 preceding siblings ...)
  (?)
@ 2014-08-20 13:43 ` Jacek Anaszewski
  -1 siblings, 0 replies; 7+ messages in thread
From: Jacek Anaszewski @ 2014-08-20 13:43 UTC (permalink / raw)
  To: linux-leds, devicetree, linux-media, linux-kernel
  Cc: kyungmin.park, b.zolnierkie, Jacek Anaszewski, Bryan Wu, Richard Purdie

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

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

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

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

* Re: [PATCH/RFC v5 2/3] leds: Add support for max77693 mfd flash cell
  2014-08-20 13:43 ` [PATCH/RFC v5 2/3] leds: Add support for max77693 mfd flash cell Jacek Anaszewski
@ 2014-08-21 16:00   ` Lee Jones
  0 siblings, 0 replies; 7+ messages in thread
From: Lee Jones @ 2014-08-21 16:00 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie, Andrzej Hajda, Bryan Wu, Richard Purdie,
	SangYoung Son, Samuel Ortiz

On Wed, 20 Aug 2014, Jacek Anaszewski wrote:

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

Please break the MFD changes out into a separate patch, I can't (at
first glace) see any reason why the changed need to be bundled
together like this.

[...]

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

* Re: [PATCH/RFC v5 1/3] mfd: max77693: Fix register enum name
  2014-08-20 13:43 ` [PATCH/RFC v5 1/3] mfd: max77693: Fix register enum name Jacek Anaszewski
@ 2014-08-21 16:02   ` Lee Jones
  0 siblings, 0 replies; 7+ messages in thread
From: Lee Jones @ 2014-08-21 16:02 UTC (permalink / raw)
  To: Jacek Anaszewski
  Cc: linux-leds, devicetree, linux-media, linux-kernel, kyungmin.park,
	b.zolnierkie, SangYoung Son, Samuel Ortiz

On Wed, 20 Aug 2014, Jacek Anaszewski wrote:
> According to the MAX77693 documentation the name of
> the register is FLASH_STATUS.
> 
> Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
> Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
> Cc: Lee Jones <lee.jones@linaro.org>
> Cc: SangYoung Son <hello.son@smasung.com>
> Cc: Samuel Ortiz <sameo@linux.intel.com>
> ---
>  include/linux/mfd/max77693-private.h |    2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Applied, thanks.

> diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h
> index c466ff3..615f121 100644
> --- a/include/linux/mfd/max77693-private.h
> +++ b/include/linux/mfd/max77693-private.h
> @@ -46,7 +46,7 @@ enum max77693_pmic_reg {
>  	MAX77693_LED_REG_VOUT_FLASH2			= 0x0C,
>  	MAX77693_LED_REG_FLASH_INT			= 0x0E,
>  	MAX77693_LED_REG_FLASH_INT_MASK			= 0x0F,
> -	MAX77693_LED_REG_FLASH_INT_STATUS		= 0x10,
> +	MAX77693_LED_REG_FLASH_STATUS			= 0x10,
>  
>  	MAX77693_PMIC_REG_PMIC_ID1			= 0x20,
>  	MAX77693_PMIC_REG_PMIC_ID2			= 0x21,

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

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

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-08-20 13:43 [PATCH/RFC v5 0/3] LED / flash API integration - LED Flash Class drivers Jacek Anaszewski
2014-08-20 13:43 ` Jacek Anaszewski
2014-08-20 13:43 ` [PATCH/RFC v5 1/3] mfd: max77693: Fix register enum name Jacek Anaszewski
2014-08-21 16:02   ` Lee Jones
2014-08-20 13:43 ` [PATCH/RFC v5 2/3] leds: Add support for max77693 mfd flash cell Jacek Anaszewski
2014-08-21 16:00   ` Lee Jones
2014-08-20 13:43 ` [PATCH/RFC v5 3/3] leds: Add driver for AAT1290 current regulator 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.