All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/9] TI LMU and Dedicated LED drivers
@ 2018-09-26 13:09 ` Dan Murphy
  0 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 13:09 UTC (permalink / raw)
  To: robh+dt, jacek.anaszewski, pavel
  Cc: devicetree, linux-kernel, lee.jones, linux-omap, linux-leds, Dan Murphy

All

This is an attempt to create a common TI LMU code base that can be leveraged
by dedicate LED drivers.

This code is by no means complete as I am looking for comments on the
implementation and demonstrating the advantage of adding unique features within
a dedicated LED driver while using common code to perform common features.

Assuming that the implementation can be used the code will be debugged and the
DT bindings and TI-lmu code will be scrubbed.

Dan

Dan Murphy (8):
  dt-bindings: ti-lmu: Remove LM3697
  mfd: ti-lmu: Remove support for LM3697
  dt-bindings: leds: Add bindings for lm3697 driver
  leds: lm3697: Introduce the lm3697 driver
  dt-bindings: leds: Add support for the LM3633
  leds: lm3633: Introduce the lm3633 driver
  dt-bindings: leds: Add the LM3632 LED dt binding
  leds: lm3632: Introduce the TI LM3632 driver

Pavel Machek (1):
  leds: add TI LMU backlight driver

 .../devicetree/bindings/leds/leds-lm3632.txt  |  53 ++
 .../devicetree/bindings/leds/leds-lm3633.txt  |  69 +++
 .../devicetree/bindings/leds/leds-lm3697.txt  |  98 ++++
 .../devicetree/bindings/mfd/ti-lmu.txt        |  26 +-
 drivers/leds/Kconfig                          |  29 +
 drivers/leds/Makefile                         |   4 +
 drivers/leds/leds-lm3632.c                    | 547 ++++++++++++++++++
 drivers/leds/leds-lm3633.c                    | 430 ++++++++++++++
 drivers/leds/leds-lm3697.c                    | 389 +++++++++++++
 drivers/leds/ti-lmu-led-common.c              | 175 ++++++
 drivers/leds/ti-lmu-led-common.h              |  43 ++
 drivers/mfd/Kconfig                           |   2 +-
 drivers/mfd/ti-lmu.c                          |  17 -
 include/linux/mfd/ti-lmu-register.h           |  44 --
 include/linux/mfd/ti-lmu.h                    |   1 -
 15 files changed, 1839 insertions(+), 88 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-lm3632.txt
 create mode 100644 Documentation/devicetree/bindings/leds/leds-lm3633.txt
 create mode 100644 Documentation/devicetree/bindings/leds/leds-lm3697.txt
 create mode 100644 drivers/leds/leds-lm3632.c
 create mode 100644 drivers/leds/leds-lm3633.c
 create mode 100644 drivers/leds/leds-lm3697.c
 create mode 100644 drivers/leds/ti-lmu-led-common.c
 create mode 100644 drivers/leds/ti-lmu-led-common.h

-- 
2.19.0

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

* [RFC PATCH 0/9] TI LMU and Dedicated LED drivers
@ 2018-09-26 13:09 ` Dan Murphy
  0 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 13:09 UTC (permalink / raw)
  To: robh+dt, jacek.anaszewski, pavel
  Cc: devicetree, linux-kernel, lee.jones, linux-omap, linux-leds, Dan Murphy

All

This is an attempt to create a common TI LMU code base that can be leveraged
by dedicate LED drivers.

This code is by no means complete as I am looking for comments on the
implementation and demonstrating the advantage of adding unique features within
a dedicated LED driver while using common code to perform common features.

Assuming that the implementation can be used the code will be debugged and the
DT bindings and TI-lmu code will be scrubbed.

Dan

Dan Murphy (8):
  dt-bindings: ti-lmu: Remove LM3697
  mfd: ti-lmu: Remove support for LM3697
  dt-bindings: leds: Add bindings for lm3697 driver
  leds: lm3697: Introduce the lm3697 driver
  dt-bindings: leds: Add support for the LM3633
  leds: lm3633: Introduce the lm3633 driver
  dt-bindings: leds: Add the LM3632 LED dt binding
  leds: lm3632: Introduce the TI LM3632 driver

Pavel Machek (1):
  leds: add TI LMU backlight driver

 .../devicetree/bindings/leds/leds-lm3632.txt  |  53 ++
 .../devicetree/bindings/leds/leds-lm3633.txt  |  69 +++
 .../devicetree/bindings/leds/leds-lm3697.txt  |  98 ++++
 .../devicetree/bindings/mfd/ti-lmu.txt        |  26 +-
 drivers/leds/Kconfig                          |  29 +
 drivers/leds/Makefile                         |   4 +
 drivers/leds/leds-lm3632.c                    | 547 ++++++++++++++++++
 drivers/leds/leds-lm3633.c                    | 430 ++++++++++++++
 drivers/leds/leds-lm3697.c                    | 389 +++++++++++++
 drivers/leds/ti-lmu-led-common.c              | 175 ++++++
 drivers/leds/ti-lmu-led-common.h              |  43 ++
 drivers/mfd/Kconfig                           |   2 +-
 drivers/mfd/ti-lmu.c                          |  17 -
 include/linux/mfd/ti-lmu-register.h           |  44 --
 include/linux/mfd/ti-lmu.h                    |   1 -
 15 files changed, 1839 insertions(+), 88 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-lm3632.txt
 create mode 100644 Documentation/devicetree/bindings/leds/leds-lm3633.txt
 create mode 100644 Documentation/devicetree/bindings/leds/leds-lm3697.txt
 create mode 100644 drivers/leds/leds-lm3632.c
 create mode 100644 drivers/leds/leds-lm3633.c
 create mode 100644 drivers/leds/leds-lm3697.c
 create mode 100644 drivers/leds/ti-lmu-led-common.c
 create mode 100644 drivers/leds/ti-lmu-led-common.h

-- 
2.19.0


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

* [RFC PATCH 1/9] leds: add TI LMU backlight driver
  2018-09-26 13:09 ` Dan Murphy
@ 2018-09-26 13:09   ` Dan Murphy
  -1 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 13:09 UTC (permalink / raw)
  To: robh+dt, jacek.anaszewski, pavel
  Cc: devicetree, linux-kernel, lee.jones, linux-omap, linux-leds,
	Milo Kim, Sebastian Reichel

From: Pavel Machek <pavel@ucw.cz>

This adds backlight support for the following TI LMU
chips: LM3532, LM3631, LM3632, LM3633, LM3695 and LM3697.

It controls LEDs on Droid 4
smartphone, including keyboard and screen backlights.

Signed-off-by: Milo Kim <milo.kim@ti.com>
[add LED subsystem support for keyboard backlight and rework DT
binding according to Rob Herrings feedback]
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.co.uk>
[remove backlight subsystem support for now]
Signed-off-by: Pavel Machek <pavel@ucw.cz>
---
 drivers/leds/Kconfig             |   8 ++
 drivers/leds/Makefile            |   1 +
 drivers/leds/ti-lmu-led-common.c | 175 +++++++++++++++++++++++++++++++
 drivers/leds/ti-lmu-led-common.h |  43 ++++++++
 4 files changed, 227 insertions(+)
 create mode 100644 drivers/leds/ti-lmu-led-common.c
 create mode 100644 drivers/leds/ti-lmu-led-common.h

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 44097a3e0fcc..dc717b30d9d3 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -756,6 +756,14 @@ config LEDS_NIC78BX
 	  To compile this driver as a module, choose M here: the module
 	  will be called leds-nic78bx.
 
+config LEDS_TI_LMU_COMMON
+	tristate "LED driver for TI LMU"
+	depends on REGMAP
+	help
+          Say Y to enable the LED driver for TI LMU devices.
+          This supports common features between the TI LM3532, LM3631, LM3632,
+	  LM3633, LM3695 and LM3697.
+
 comment "LED Triggers"
 source "drivers/leds/trigger/Kconfig"
 
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 420b5d2cfa62..e09bb27bc7ea 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -78,6 +78,7 @@ obj-$(CONFIG_LEDS_MT6323)		+= leds-mt6323.o
 obj-$(CONFIG_LEDS_LM3692X)		+= leds-lm3692x.o
 obj-$(CONFIG_LEDS_SC27XX_BLTC)		+= leds-sc27xx-bltc.o
 obj-$(CONFIG_LEDS_LM3601X)		+= leds-lm3601x.o
+obj-$(CONFIG_LEDS_TI_LMU_COMMON)	+= ti-lmu-led-common.o
 
 # LED SPI Drivers
 obj-$(CONFIG_LEDS_CR0014114)		+= leds-cr0014114.o
diff --git a/drivers/leds/ti-lmu-led-common.c b/drivers/leds/ti-lmu-led-common.c
new file mode 100644
index 000000000000..60e900b71681
--- /dev/null
+++ b/drivers/leds/ti-lmu-led-common.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2015 Texas Instruments
+ * Copyright 2018 Sebastian Reichel
+ * Copyright 2018 Pavel Machek <pavel@ucw.cz>
+ *
+ * TI LMU Led driver, based on previous work from
+ * Milo Kim <milo.kim@ti.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "ti-lmu-led-common.h"
+
+const static int ramp_table[16] = { 2, 262, 524, 1049, 2090, 4194, 8389,
+				16780, 33550, 41940, 50330, 58720,
+				67110, 83880, 100660, 117440};
+
+static int ti_lmu_common_enable(struct ti_lmu_bank *lmu_bank, bool enable)
+{
+	struct regmap *regmap = lmu_bank->regmap;
+	unsigned long enable_time = lmu_bank->enable_usec;
+	u8 reg = lmu_bank->enable_reg;
+	u8 mask = BIT(lmu_bank->bank_id);
+	u8 val = (enable == true) ? mask : 0;
+	int ret;
+
+	return 0;
+	if (!reg)
+		return -EINVAL;
+
+	ret = regmap_update_bits(regmap, reg, mask, val);
+	if (ret)
+		return ret;
+
+	if (enable_time > 0)
+		usleep_range(enable_time, enable_time + 100);
+
+	return 0;
+}
+
+static int ti_lmu_common_update_brightness_register(struct ti_lmu_bank *lmu_bank,
+						       int brightness)
+{
+	struct regmap *regmap = lmu_bank->regmap;
+	u8 reg, val;
+	int ret;
+
+	/*
+	 * Brightness register update
+	 *
+	 * 11 bit dimming: update LSB bits and write MSB byte.
+	 *		   MSB brightness should be shifted.
+	 *  8 bit dimming: write MSB byte.
+	 */
+	if (lmu_bank->max_brightness == MAX_BRIGHTNESS_11BIT) {
+		reg = lmu_bank->lsb_brightness_reg;
+		ret = regmap_update_bits(regmap, reg,
+					 LMU_11BIT_LSB_MASK,
+					 brightness);
+		if (ret)
+			return ret;
+
+		val = brightness >> LMU_11BIT_MSB_SHIFT;
+	} else {
+		val = brightness;
+	}
+
+	reg = lmu_bank->msb_brightness_reg;
+printk("%s: Reg 0x%X\n", __func__, reg);
+	return regmap_write(regmap, reg, val);
+}
+
+int ti_lmu_common_set_brightness(struct ti_lmu_bank *lmu_bank,
+				    int brightness)
+{
+	bool enable = brightness > 0;
+	int ret;
+
+	ret = ti_lmu_common_enable(lmu_bank, enable);
+	if (ret)
+		return ret;
+
+	lmu_bank->current_brightness = brightness;
+
+	return ti_lmu_common_update_brightness_register(lmu_bank, brightness);
+}
+EXPORT_SYMBOL(ti_lmu_common_set_brightness);
+
+static int ti_lmu_common_convert_ramp_to_index(unsigned int msec)
+{
+	int size = ARRAY_SIZE(ramp_table);
+	int i;
+
+	if (msec <= ramp_table[0])
+		return 0;
+
+	if (msec > ramp_table[size - 1])
+		return size - 1;
+
+	for (i = 1; i < size; i++) {
+		if (msec == ramp_table[i])
+			return i;
+
+		/* Find an approximate index by looking up the table */
+		if (msec > ramp_table[i - 1] && msec < ramp_table[i]) {
+			if (msec - ramp_table[i - 1] < ramp_table[i] - msec)
+				return i - 1;
+			else
+				return i;
+		}
+	}
+
+	return -EINVAL;
+}
+
+
+int ti_lmu_common_set_ramp(struct ti_lmu_bank *lmu_bank)
+{
+	struct regmap *regmap = lmu_bank->regmap;
+	u8 ramp, ramp_up, ramp_down;
+
+	if (lmu_bank->ramp_up_msec == 0 && lmu_bank->ramp_down_msec == 0) {
+		ramp_up = 0;
+		ramp_down = 0;
+	} else {
+		ramp_up = ti_lmu_common_convert_ramp_to_index(lmu_bank->ramp_up_msec);
+		ramp_down = ti_lmu_common_convert_ramp_to_index(lmu_bank->ramp_down_msec);
+	}
+
+	if (ramp_up < 0 || ramp_down < 0)
+		return -EINVAL;
+
+	ramp = (ramp_up << 4) | ramp_down;
+
+	return regmap_write(regmap, lmu_bank->runtime_ramp_reg, ramp);
+
+}
+EXPORT_SYMBOL(ti_lmu_common_set_ramp);
+
+int ti_lmu_common_get_ramp_params(struct device *dev,
+				  struct fwnode_handle *child,
+				  struct ti_lmu_bank *lmu_data)
+{
+
+	int ret;
+
+	ret = fwnode_property_read_u32(child, "ramp-up-ms",
+				 &lmu_data->ramp_up_msec);
+	if (ret)
+		dev_warn(dev, "ramp-up-ms property missing\n");
+
+
+	ret = fwnode_property_read_u32(child, "ramp-down-ms",
+				 &lmu_data->ramp_down_msec);
+	if (ret)
+		dev_warn(dev, "ramp-down-ms property missing\n");
+
+	return 0;
+}
+EXPORT_SYMBOL(ti_lmu_common_get_ramp_params);
+
+MODULE_DESCRIPTION("TI LMU LED Driver");
+MODULE_AUTHOR("Sebastian Reichel");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:ti-lmu-led");
diff --git a/drivers/leds/ti-lmu-led-common.h b/drivers/leds/ti-lmu-led-common.h
new file mode 100644
index 000000000000..874ebdb62b58
--- /dev/null
+++ b/drivers/leds/ti-lmu-led-common.h
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+// TI LMU Common Core
+// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+
+#define LMU_DUAL_CHANNEL_USED	(BIT(0) | BIT(1))
+#define LMU_11BIT_LSB_MASK	(BIT(0) | BIT(1) | BIT(2))
+#define LMU_11BIT_MSB_SHIFT	3
+
+#define MAX_BRIGHTNESS_8BIT	255
+#define MAX_BRIGHTNESS_11BIT	2047
+
+#define NUM_DUAL_CHANNEL	2
+
+struct ti_lmu_bank {
+	struct regmap *regmap;
+
+	int bank_id;
+	int fault_monitor_used;
+
+	u8 enable_reg;
+	unsigned long enable_usec;
+
+	int current_brightness;
+	u32 default_brightness;
+	int max_brightness;
+
+	u8 lsb_brightness_reg;
+	u8 msb_brightness_reg;
+
+	u8 runtime_ramp_reg;
+	u32 ramp_up_msec;
+	u32 ramp_down_msec;
+};
+
+
+int ti_lmu_common_set_brightness(struct ti_lmu_bank *lmu_bank,
+				    int brightness);
+
+int ti_lmu_common_set_ramp(struct ti_lmu_bank *lmu_bank);
+
+int ti_lmu_common_get_ramp_params(struct device *dev,
+				  struct fwnode_handle *child,
+				  struct ti_lmu_bank *lmu_data);
-- 
2.19.0

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

* [RFC PATCH 1/9] leds: add TI LMU backlight driver
@ 2018-09-26 13:09   ` Dan Murphy
  0 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 13:09 UTC (permalink / raw)
  To: robh+dt, jacek.anaszewski, pavel
  Cc: devicetree, linux-kernel, lee.jones, linux-omap, linux-leds,
	Milo Kim, Sebastian Reichel

From: Pavel Machek <pavel@ucw.cz>

This adds backlight support for the following TI LMU
chips: LM3532, LM3631, LM3632, LM3633, LM3695 and LM3697.

It controls LEDs on Droid 4
smartphone, including keyboard and screen backlights.

Signed-off-by: Milo Kim <milo.kim@ti.com>
[add LED subsystem support for keyboard backlight and rework DT
binding according to Rob Herrings feedback]
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.co.uk>
[remove backlight subsystem support for now]
Signed-off-by: Pavel Machek <pavel@ucw.cz>
---
 drivers/leds/Kconfig             |   8 ++
 drivers/leds/Makefile            |   1 +
 drivers/leds/ti-lmu-led-common.c | 175 +++++++++++++++++++++++++++++++
 drivers/leds/ti-lmu-led-common.h |  43 ++++++++
 4 files changed, 227 insertions(+)
 create mode 100644 drivers/leds/ti-lmu-led-common.c
 create mode 100644 drivers/leds/ti-lmu-led-common.h

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 44097a3e0fcc..dc717b30d9d3 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -756,6 +756,14 @@ config LEDS_NIC78BX
 	  To compile this driver as a module, choose M here: the module
 	  will be called leds-nic78bx.
 
+config LEDS_TI_LMU_COMMON
+	tristate "LED driver for TI LMU"
+	depends on REGMAP
+	help
+          Say Y to enable the LED driver for TI LMU devices.
+          This supports common features between the TI LM3532, LM3631, LM3632,
+	  LM3633, LM3695 and LM3697.
+
 comment "LED Triggers"
 source "drivers/leds/trigger/Kconfig"
 
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 420b5d2cfa62..e09bb27bc7ea 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -78,6 +78,7 @@ obj-$(CONFIG_LEDS_MT6323)		+= leds-mt6323.o
 obj-$(CONFIG_LEDS_LM3692X)		+= leds-lm3692x.o
 obj-$(CONFIG_LEDS_SC27XX_BLTC)		+= leds-sc27xx-bltc.o
 obj-$(CONFIG_LEDS_LM3601X)		+= leds-lm3601x.o
+obj-$(CONFIG_LEDS_TI_LMU_COMMON)	+= ti-lmu-led-common.o
 
 # LED SPI Drivers
 obj-$(CONFIG_LEDS_CR0014114)		+= leds-cr0014114.o
diff --git a/drivers/leds/ti-lmu-led-common.c b/drivers/leds/ti-lmu-led-common.c
new file mode 100644
index 000000000000..60e900b71681
--- /dev/null
+++ b/drivers/leds/ti-lmu-led-common.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2015 Texas Instruments
+ * Copyright 2018 Sebastian Reichel
+ * Copyright 2018 Pavel Machek <pavel@ucw.cz>
+ *
+ * TI LMU Led driver, based on previous work from
+ * Milo Kim <milo.kim@ti.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "ti-lmu-led-common.h"
+
+const static int ramp_table[16] = { 2, 262, 524, 1049, 2090, 4194, 8389,
+				16780, 33550, 41940, 50330, 58720,
+				67110, 83880, 100660, 117440};
+
+static int ti_lmu_common_enable(struct ti_lmu_bank *lmu_bank, bool enable)
+{
+	struct regmap *regmap = lmu_bank->regmap;
+	unsigned long enable_time = lmu_bank->enable_usec;
+	u8 reg = lmu_bank->enable_reg;
+	u8 mask = BIT(lmu_bank->bank_id);
+	u8 val = (enable == true) ? mask : 0;
+	int ret;
+
+	return 0;
+	if (!reg)
+		return -EINVAL;
+
+	ret = regmap_update_bits(regmap, reg, mask, val);
+	if (ret)
+		return ret;
+
+	if (enable_time > 0)
+		usleep_range(enable_time, enable_time + 100);
+
+	return 0;
+}
+
+static int ti_lmu_common_update_brightness_register(struct ti_lmu_bank *lmu_bank,
+						       int brightness)
+{
+	struct regmap *regmap = lmu_bank->regmap;
+	u8 reg, val;
+	int ret;
+
+	/*
+	 * Brightness register update
+	 *
+	 * 11 bit dimming: update LSB bits and write MSB byte.
+	 *		   MSB brightness should be shifted.
+	 *  8 bit dimming: write MSB byte.
+	 */
+	if (lmu_bank->max_brightness == MAX_BRIGHTNESS_11BIT) {
+		reg = lmu_bank->lsb_brightness_reg;
+		ret = regmap_update_bits(regmap, reg,
+					 LMU_11BIT_LSB_MASK,
+					 brightness);
+		if (ret)
+			return ret;
+
+		val = brightness >> LMU_11BIT_MSB_SHIFT;
+	} else {
+		val = brightness;
+	}
+
+	reg = lmu_bank->msb_brightness_reg;
+printk("%s: Reg 0x%X\n", __func__, reg);
+	return regmap_write(regmap, reg, val);
+}
+
+int ti_lmu_common_set_brightness(struct ti_lmu_bank *lmu_bank,
+				    int brightness)
+{
+	bool enable = brightness > 0;
+	int ret;
+
+	ret = ti_lmu_common_enable(lmu_bank, enable);
+	if (ret)
+		return ret;
+
+	lmu_bank->current_brightness = brightness;
+
+	return ti_lmu_common_update_brightness_register(lmu_bank, brightness);
+}
+EXPORT_SYMBOL(ti_lmu_common_set_brightness);
+
+static int ti_lmu_common_convert_ramp_to_index(unsigned int msec)
+{
+	int size = ARRAY_SIZE(ramp_table);
+	int i;
+
+	if (msec <= ramp_table[0])
+		return 0;
+
+	if (msec > ramp_table[size - 1])
+		return size - 1;
+
+	for (i = 1; i < size; i++) {
+		if (msec == ramp_table[i])
+			return i;
+
+		/* Find an approximate index by looking up the table */
+		if (msec > ramp_table[i - 1] && msec < ramp_table[i]) {
+			if (msec - ramp_table[i - 1] < ramp_table[i] - msec)
+				return i - 1;
+			else
+				return i;
+		}
+	}
+
+	return -EINVAL;
+}
+
+
+int ti_lmu_common_set_ramp(struct ti_lmu_bank *lmu_bank)
+{
+	struct regmap *regmap = lmu_bank->regmap;
+	u8 ramp, ramp_up, ramp_down;
+
+	if (lmu_bank->ramp_up_msec == 0 && lmu_bank->ramp_down_msec == 0) {
+		ramp_up = 0;
+		ramp_down = 0;
+	} else {
+		ramp_up = ti_lmu_common_convert_ramp_to_index(lmu_bank->ramp_up_msec);
+		ramp_down = ti_lmu_common_convert_ramp_to_index(lmu_bank->ramp_down_msec);
+	}
+
+	if (ramp_up < 0 || ramp_down < 0)
+		return -EINVAL;
+
+	ramp = (ramp_up << 4) | ramp_down;
+
+	return regmap_write(regmap, lmu_bank->runtime_ramp_reg, ramp);
+
+}
+EXPORT_SYMBOL(ti_lmu_common_set_ramp);
+
+int ti_lmu_common_get_ramp_params(struct device *dev,
+				  struct fwnode_handle *child,
+				  struct ti_lmu_bank *lmu_data)
+{
+
+	int ret;
+
+	ret = fwnode_property_read_u32(child, "ramp-up-ms",
+				 &lmu_data->ramp_up_msec);
+	if (ret)
+		dev_warn(dev, "ramp-up-ms property missing\n");
+
+
+	ret = fwnode_property_read_u32(child, "ramp-down-ms",
+				 &lmu_data->ramp_down_msec);
+	if (ret)
+		dev_warn(dev, "ramp-down-ms property missing\n");
+
+	return 0;
+}
+EXPORT_SYMBOL(ti_lmu_common_get_ramp_params);
+
+MODULE_DESCRIPTION("TI LMU LED Driver");
+MODULE_AUTHOR("Sebastian Reichel");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:ti-lmu-led");
diff --git a/drivers/leds/ti-lmu-led-common.h b/drivers/leds/ti-lmu-led-common.h
new file mode 100644
index 000000000000..874ebdb62b58
--- /dev/null
+++ b/drivers/leds/ti-lmu-led-common.h
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+// TI LMU Common Core
+// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+
+#define LMU_DUAL_CHANNEL_USED	(BIT(0) | BIT(1))
+#define LMU_11BIT_LSB_MASK	(BIT(0) | BIT(1) | BIT(2))
+#define LMU_11BIT_MSB_SHIFT	3
+
+#define MAX_BRIGHTNESS_8BIT	255
+#define MAX_BRIGHTNESS_11BIT	2047
+
+#define NUM_DUAL_CHANNEL	2
+
+struct ti_lmu_bank {
+	struct regmap *regmap;
+
+	int bank_id;
+	int fault_monitor_used;
+
+	u8 enable_reg;
+	unsigned long enable_usec;
+
+	int current_brightness;
+	u32 default_brightness;
+	int max_brightness;
+
+	u8 lsb_brightness_reg;
+	u8 msb_brightness_reg;
+
+	u8 runtime_ramp_reg;
+	u32 ramp_up_msec;
+	u32 ramp_down_msec;
+};
+
+
+int ti_lmu_common_set_brightness(struct ti_lmu_bank *lmu_bank,
+				    int brightness);
+
+int ti_lmu_common_set_ramp(struct ti_lmu_bank *lmu_bank);
+
+int ti_lmu_common_get_ramp_params(struct device *dev,
+				  struct fwnode_handle *child,
+				  struct ti_lmu_bank *lmu_data);
-- 
2.19.0


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

* [RFC PATCH 2/9] dt-bindings: ti-lmu: Remove LM3697
  2018-09-26 13:09 ` Dan Murphy
@ 2018-09-26 13:09   ` Dan Murphy
  -1 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 13:09 UTC (permalink / raw)
  To: robh+dt, jacek.anaszewski, pavel
  Cc: devicetree, linux-kernel, lee.jones, linux-omap, linux-leds, Dan Murphy

Remove support for the LM3697 LED device
from the ti-lmu.  The LM3697 will be supported
via a stand alone LED driver.

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
 .../devicetree/bindings/mfd/ti-lmu.txt        | 26 +------------------
 1 file changed, 1 insertion(+), 25 deletions(-)

diff --git a/Documentation/devicetree/bindings/mfd/ti-lmu.txt b/Documentation/devicetree/bindings/mfd/ti-lmu.txt
index c885cf89b8ce..920f910be4e9 100644
--- a/Documentation/devicetree/bindings/mfd/ti-lmu.txt
+++ b/Documentation/devicetree/bindings/mfd/ti-lmu.txt
@@ -9,7 +9,6 @@ TI LMU driver supports lighting devices below.
   LM3632       Backlight and regulator
   LM3633       Backlight, LED and fault monitor
   LM3695       Backlight
-  LM3697       Backlight and fault monitor
 
 Required properties:
   - compatible: Should be one of:
@@ -18,11 +17,10 @@ Required properties:
                 "ti,lm3632"
                 "ti,lm3633"
                 "ti,lm3695"
-                "ti,lm3697"
   - reg: I2C slave address.
          0x11 for LM3632
          0x29 for LM3631
-         0x36 for LM3633, LM3697
+         0x36 for LM3633
          0x38 for LM3532
          0x63 for LM3695
 
@@ -38,7 +36,6 @@ Optional nodes:
     Required properties:
       - compatible: Should be one of:
                     "ti,lm3633-fault-monitor"
-                    "ti,lm3697-fault-monitor"
   - leds: LED properties for LM3633. Please refer to [2].
   - regulators: Regulator properties for LM3631 and LM3632.
                 Please refer to [3].
@@ -220,24 +217,3 @@ lm3695@63 {
 		};
 	};
 };
-
-lm3697@36 {
-	compatible = "ti,lm3697";
-	reg = <0x36>;
-
-	enable-gpios = <&pioC 2 GPIO_ACTIVE_HIGH>;
-
-	backlight {
-		compatible = "ti,lm3697-backlight";
-
-		lcd {
-			led-sources = <0 1 2>;
-			ramp-up-msec = <200>;
-			ramp-down-msec = <200>;
-		};
-	};
-
-	fault-monitor {
-		compatible = "ti,lm3697-fault-monitor";
-	};
-};
-- 
2.19.0

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

* [RFC PATCH 2/9] dt-bindings: ti-lmu: Remove LM3697
@ 2018-09-26 13:09   ` Dan Murphy
  0 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 13:09 UTC (permalink / raw)
  To: robh+dt, jacek.anaszewski, pavel
  Cc: devicetree, linux-kernel, lee.jones, linux-omap, linux-leds, Dan Murphy

Remove support for the LM3697 LED device
from the ti-lmu.  The LM3697 will be supported
via a stand alone LED driver.

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
 .../devicetree/bindings/mfd/ti-lmu.txt        | 26 +------------------
 1 file changed, 1 insertion(+), 25 deletions(-)

diff --git a/Documentation/devicetree/bindings/mfd/ti-lmu.txt b/Documentation/devicetree/bindings/mfd/ti-lmu.txt
index c885cf89b8ce..920f910be4e9 100644
--- a/Documentation/devicetree/bindings/mfd/ti-lmu.txt
+++ b/Documentation/devicetree/bindings/mfd/ti-lmu.txt
@@ -9,7 +9,6 @@ TI LMU driver supports lighting devices below.
   LM3632       Backlight and regulator
   LM3633       Backlight, LED and fault monitor
   LM3695       Backlight
-  LM3697       Backlight and fault monitor
 
 Required properties:
   - compatible: Should be one of:
@@ -18,11 +17,10 @@ Required properties:
                 "ti,lm3632"
                 "ti,lm3633"
                 "ti,lm3695"
-                "ti,lm3697"
   - reg: I2C slave address.
          0x11 for LM3632
          0x29 for LM3631
-         0x36 for LM3633, LM3697
+         0x36 for LM3633
          0x38 for LM3532
          0x63 for LM3695
 
@@ -38,7 +36,6 @@ Optional nodes:
     Required properties:
       - compatible: Should be one of:
                     "ti,lm3633-fault-monitor"
-                    "ti,lm3697-fault-monitor"
   - leds: LED properties for LM3633. Please refer to [2].
   - regulators: Regulator properties for LM3631 and LM3632.
                 Please refer to [3].
@@ -220,24 +217,3 @@ lm3695@63 {
 		};
 	};
 };
-
-lm3697@36 {
-	compatible = "ti,lm3697";
-	reg = <0x36>;
-
-	enable-gpios = <&pioC 2 GPIO_ACTIVE_HIGH>;
-
-	backlight {
-		compatible = "ti,lm3697-backlight";
-
-		lcd {
-			led-sources = <0 1 2>;
-			ramp-up-msec = <200>;
-			ramp-down-msec = <200>;
-		};
-	};
-
-	fault-monitor {
-		compatible = "ti,lm3697-fault-monitor";
-	};
-};
-- 
2.19.0


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

* [RFC PATCH 3/9] mfd: ti-lmu: Remove support for LM3697
  2018-09-26 13:09 ` Dan Murphy
@ 2018-09-26 13:09   ` Dan Murphy
  -1 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 13:09 UTC (permalink / raw)
  To: robh+dt, jacek.anaszewski, pavel
  Cc: devicetree, linux-kernel, lee.jones, linux-omap, linux-leds, Dan Murphy

Remove support for the LM3697 from the ti-lmu
driver in favor of a dedicated LED driver.

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
 drivers/mfd/Kconfig                 |  2 +-
 drivers/mfd/ti-lmu.c                | 17 -----------
 include/linux/mfd/ti-lmu-register.h | 44 -----------------------------
 include/linux/mfd/ti-lmu.h          |  1 -
 4 files changed, 1 insertion(+), 63 deletions(-)

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 11841f4b7b2b..9b04dd527c68 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1293,7 +1293,7 @@ config MFD_TI_LMU
 	help
 	  Say yes here to enable support for TI LMU chips.
 
-	  TI LMU MFD supports LM3532, LM3631, LM3632, LM3633, LM3695 and LM3697.
+	  TI LMU MFD supports LM3532, LM3631, LM3632, LM3633, and LM3695.
 	  It consists of backlight, LED and regulator driver.
 	  It provides consistent device controls for lighting functions.
 
diff --git a/drivers/mfd/ti-lmu.c b/drivers/mfd/ti-lmu.c
index cfb411cde51c..b6bfa99a29dd 100644
--- a/drivers/mfd/ti-lmu.c
+++ b/drivers/mfd/ti-lmu.c
@@ -128,20 +128,6 @@ static struct mfd_cell lm3695_devices[] = {
 	},
 };
 
-static struct mfd_cell lm3697_devices[] = {
-	{
-		.name          = "ti-lmu-backlight",
-		.id            = LM3697,
-		.of_compatible = "ti,lm3697-backlight",
-	},
-	/* Monitoring driver for open/short circuit detection */
-	{
-		.name          = "ti-lmu-fault-monitor",
-		.id            = LM3697,
-		.of_compatible = "ti,lm3697-fault-monitor",
-	},
-};
-
 #define TI_LMU_DATA(chip, max_reg)		\
 static const struct ti_lmu_data chip##_data =	\
 {						\
@@ -155,7 +141,6 @@ TI_LMU_DATA(lm3631, LM3631_MAX_REG);
 TI_LMU_DATA(lm3632, LM3632_MAX_REG);
 TI_LMU_DATA(lm3633, LM3633_MAX_REG);
 TI_LMU_DATA(lm3695, LM3695_MAX_REG);
-TI_LMU_DATA(lm3697, LM3697_MAX_REG);
 
 static const struct of_device_id ti_lmu_of_match[] = {
 	{ .compatible = "ti,lm3532", .data = &lm3532_data },
@@ -163,7 +148,6 @@ static const struct of_device_id ti_lmu_of_match[] = {
 	{ .compatible = "ti,lm3632", .data = &lm3632_data },
 	{ .compatible = "ti,lm3633", .data = &lm3633_data },
 	{ .compatible = "ti,lm3695", .data = &lm3695_data },
-	{ .compatible = "ti,lm3697", .data = &lm3697_data },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, ti_lmu_of_match);
@@ -237,7 +221,6 @@ static const struct i2c_device_id ti_lmu_ids[] = {
 	{ "lm3632", LM3632 },
 	{ "lm3633", LM3633 },
 	{ "lm3695", LM3695 },
-	{ "lm3697", LM3697 },
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, ti_lmu_ids);
diff --git a/include/linux/mfd/ti-lmu-register.h b/include/linux/mfd/ti-lmu-register.h
index 2125c7c02818..99711ff4b809 100644
--- a/include/linux/mfd/ti-lmu-register.h
+++ b/include/linux/mfd/ti-lmu-register.h
@@ -233,48 +233,4 @@
 #define LM3695_REG_BRT_MSB			0x14
 
 #define LM3695_MAX_REG				0x14
-
-/* LM3697 */
-#define LM3697_REG_HVLED_OUTPUT_CFG		0x10
-#define LM3697_HVLED1_CFG_MASK			BIT(0)
-#define LM3697_HVLED2_CFG_MASK			BIT(1)
-#define LM3697_HVLED3_CFG_MASK			BIT(2)
-#define LM3697_HVLED1_CFG_SHIFT			0
-#define LM3697_HVLED2_CFG_SHIFT			1
-#define LM3697_HVLED3_CFG_SHIFT			2
-
-#define LM3697_REG_BL0_RAMP			0x11
-#define LM3697_REG_BL1_RAMP			0x12
-#define LM3697_RAMPUP_MASK			0xF0
-#define LM3697_RAMPUP_SHIFT			4
-#define LM3697_RAMPDN_MASK			0x0F
-#define LM3697_RAMPDN_SHIFT			0
-
-#define LM3697_REG_RAMP_CONF			0x14
-#define LM3697_RAMP_MASK			0x0F
-#define LM3697_RAMP_EACH			0x05
-
-#define LM3697_REG_PWM_CFG			0x1C
-#define LM3697_PWM_A_MASK			BIT(0)
-#define LM3697_PWM_B_MASK			BIT(1)
-
-#define LM3697_REG_IMAX_A			0x17
-#define LM3697_REG_IMAX_B			0x18
-
-#define LM3697_REG_FEEDBACK_ENABLE		0x19
-
-#define LM3697_REG_BRT_A_LSB			0x20
-#define LM3697_REG_BRT_A_MSB			0x21
-#define LM3697_REG_BRT_B_LSB			0x22
-#define LM3697_REG_BRT_B_MSB			0x23
-
-#define LM3697_REG_ENABLE			0x24
-
-#define LM3697_REG_OPEN_FAULT_STATUS		0xB0
-
-#define LM3697_REG_SHORT_FAULT_STATUS		0xB2
-
-#define LM3697_REG_MONITOR_ENABLE		0xB4
-
-#define LM3697_MAX_REG				0xB4
 #endif
diff --git a/include/linux/mfd/ti-lmu.h b/include/linux/mfd/ti-lmu.h
index 09d5f30384e5..bc9272f08f47 100644
--- a/include/linux/mfd/ti-lmu.h
+++ b/include/linux/mfd/ti-lmu.h
@@ -26,7 +26,6 @@ enum ti_lmu_id {
 	LM3632,
 	LM3633,
 	LM3695,
-	LM3697,
 	LMU_MAX_ID,
 };
 
-- 
2.19.0

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

* [RFC PATCH 3/9] mfd: ti-lmu: Remove support for LM3697
@ 2018-09-26 13:09   ` Dan Murphy
  0 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 13:09 UTC (permalink / raw)
  To: robh+dt, jacek.anaszewski, pavel
  Cc: devicetree, linux-kernel, lee.jones, linux-omap, linux-leds, Dan Murphy

Remove support for the LM3697 from the ti-lmu
driver in favor of a dedicated LED driver.

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
 drivers/mfd/Kconfig                 |  2 +-
 drivers/mfd/ti-lmu.c                | 17 -----------
 include/linux/mfd/ti-lmu-register.h | 44 -----------------------------
 include/linux/mfd/ti-lmu.h          |  1 -
 4 files changed, 1 insertion(+), 63 deletions(-)

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 11841f4b7b2b..9b04dd527c68 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1293,7 +1293,7 @@ config MFD_TI_LMU
 	help
 	  Say yes here to enable support for TI LMU chips.
 
-	  TI LMU MFD supports LM3532, LM3631, LM3632, LM3633, LM3695 and LM3697.
+	  TI LMU MFD supports LM3532, LM3631, LM3632, LM3633, and LM3695.
 	  It consists of backlight, LED and regulator driver.
 	  It provides consistent device controls for lighting functions.
 
diff --git a/drivers/mfd/ti-lmu.c b/drivers/mfd/ti-lmu.c
index cfb411cde51c..b6bfa99a29dd 100644
--- a/drivers/mfd/ti-lmu.c
+++ b/drivers/mfd/ti-lmu.c
@@ -128,20 +128,6 @@ static struct mfd_cell lm3695_devices[] = {
 	},
 };
 
-static struct mfd_cell lm3697_devices[] = {
-	{
-		.name          = "ti-lmu-backlight",
-		.id            = LM3697,
-		.of_compatible = "ti,lm3697-backlight",
-	},
-	/* Monitoring driver for open/short circuit detection */
-	{
-		.name          = "ti-lmu-fault-monitor",
-		.id            = LM3697,
-		.of_compatible = "ti,lm3697-fault-monitor",
-	},
-};
-
 #define TI_LMU_DATA(chip, max_reg)		\
 static const struct ti_lmu_data chip##_data =	\
 {						\
@@ -155,7 +141,6 @@ TI_LMU_DATA(lm3631, LM3631_MAX_REG);
 TI_LMU_DATA(lm3632, LM3632_MAX_REG);
 TI_LMU_DATA(lm3633, LM3633_MAX_REG);
 TI_LMU_DATA(lm3695, LM3695_MAX_REG);
-TI_LMU_DATA(lm3697, LM3697_MAX_REG);
 
 static const struct of_device_id ti_lmu_of_match[] = {
 	{ .compatible = "ti,lm3532", .data = &lm3532_data },
@@ -163,7 +148,6 @@ static const struct of_device_id ti_lmu_of_match[] = {
 	{ .compatible = "ti,lm3632", .data = &lm3632_data },
 	{ .compatible = "ti,lm3633", .data = &lm3633_data },
 	{ .compatible = "ti,lm3695", .data = &lm3695_data },
-	{ .compatible = "ti,lm3697", .data = &lm3697_data },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, ti_lmu_of_match);
@@ -237,7 +221,6 @@ static const struct i2c_device_id ti_lmu_ids[] = {
 	{ "lm3632", LM3632 },
 	{ "lm3633", LM3633 },
 	{ "lm3695", LM3695 },
-	{ "lm3697", LM3697 },
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, ti_lmu_ids);
diff --git a/include/linux/mfd/ti-lmu-register.h b/include/linux/mfd/ti-lmu-register.h
index 2125c7c02818..99711ff4b809 100644
--- a/include/linux/mfd/ti-lmu-register.h
+++ b/include/linux/mfd/ti-lmu-register.h
@@ -233,48 +233,4 @@
 #define LM3695_REG_BRT_MSB			0x14
 
 #define LM3695_MAX_REG				0x14
-
-/* LM3697 */
-#define LM3697_REG_HVLED_OUTPUT_CFG		0x10
-#define LM3697_HVLED1_CFG_MASK			BIT(0)
-#define LM3697_HVLED2_CFG_MASK			BIT(1)
-#define LM3697_HVLED3_CFG_MASK			BIT(2)
-#define LM3697_HVLED1_CFG_SHIFT			0
-#define LM3697_HVLED2_CFG_SHIFT			1
-#define LM3697_HVLED3_CFG_SHIFT			2
-
-#define LM3697_REG_BL0_RAMP			0x11
-#define LM3697_REG_BL1_RAMP			0x12
-#define LM3697_RAMPUP_MASK			0xF0
-#define LM3697_RAMPUP_SHIFT			4
-#define LM3697_RAMPDN_MASK			0x0F
-#define LM3697_RAMPDN_SHIFT			0
-
-#define LM3697_REG_RAMP_CONF			0x14
-#define LM3697_RAMP_MASK			0x0F
-#define LM3697_RAMP_EACH			0x05
-
-#define LM3697_REG_PWM_CFG			0x1C
-#define LM3697_PWM_A_MASK			BIT(0)
-#define LM3697_PWM_B_MASK			BIT(1)
-
-#define LM3697_REG_IMAX_A			0x17
-#define LM3697_REG_IMAX_B			0x18
-
-#define LM3697_REG_FEEDBACK_ENABLE		0x19
-
-#define LM3697_REG_BRT_A_LSB			0x20
-#define LM3697_REG_BRT_A_MSB			0x21
-#define LM3697_REG_BRT_B_LSB			0x22
-#define LM3697_REG_BRT_B_MSB			0x23
-
-#define LM3697_REG_ENABLE			0x24
-
-#define LM3697_REG_OPEN_FAULT_STATUS		0xB0
-
-#define LM3697_REG_SHORT_FAULT_STATUS		0xB2
-
-#define LM3697_REG_MONITOR_ENABLE		0xB4
-
-#define LM3697_MAX_REG				0xB4
 #endif
diff --git a/include/linux/mfd/ti-lmu.h b/include/linux/mfd/ti-lmu.h
index 09d5f30384e5..bc9272f08f47 100644
--- a/include/linux/mfd/ti-lmu.h
+++ b/include/linux/mfd/ti-lmu.h
@@ -26,7 +26,6 @@ enum ti_lmu_id {
 	LM3632,
 	LM3633,
 	LM3695,
-	LM3697,
 	LMU_MAX_ID,
 };
 
-- 
2.19.0


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

* [RFC PATCH 4/9] dt-bindings: leds: Add bindings for lm3697 driver
  2018-09-26 13:09 ` Dan Murphy
@ 2018-09-26 13:09   ` Dan Murphy
  -1 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 13:09 UTC (permalink / raw)
  To: robh+dt, jacek.anaszewski, pavel
  Cc: devicetree, linux-kernel, lee.jones, linux-omap, linux-leds, Dan Murphy

Add the device tree bindings for the lm3697
LED driver for backlighting and display.

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
 .../devicetree/bindings/leds/leds-lm3697.txt  | 98 +++++++++++++++++++
 1 file changed, 98 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-lm3697.txt

diff --git a/Documentation/devicetree/bindings/leds/leds-lm3697.txt b/Documentation/devicetree/bindings/leds/leds-lm3697.txt
new file mode 100644
index 000000000000..4bb2ed51025b
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-lm3697.txt
@@ -0,0 +1,98 @@
+* Texas Instruments - LM3697 Highly Efficient White LED Driver
+
+The LM3697 11-bit LED driver provides high-
+performance backlight dimming for 1, 2, or 3 series
+LED strings while delivering up to 90% efficiency.
+
+This device is suitable for display and keypad Lighting
+
+Required properties:
+	- compatible:
+		"ti,lm3697"
+	- reg :  I2C slave address
+	- #address-cells : 1
+	- #size-cells : 0
+
+Optional properties:
+	- enable-gpios : GPIO pin to enable/disable the device
+	- vled-supply : LED supply
+
+Required child properties:
+	- reg : 0 - LED is Controlled by bank A
+		1 - LED is Controlled by bank B
+	- led-sources : Indicates which HVLED string is associated to which
+			control bank.  Each element in the array is associated
+			with a specific HVLED string.  Element 0 is HVLED1,
+			element 1 is HVLED2 and element 2 HVLED3.
+			Additional information is contained
+			in Documentation/devicetree/bindings/leds/common.txt
+			0 - HVLED is not active in this control bank
+			1 - HVLED string is controlled by this control bank
+
+Optional child properties:
+	- runtime-ramp-up-msec: Current ramping from one brightness level to
+				the a higher brightness level.
+				Range from 2048 us - 117.44 s
+	- runtime-ramp-down-msec: Current ramping from one brightness level to
+				  the a lower brightness level.
+				  Range from 2048 us - 117.44 s
+	- label : see Documentation/devicetree/bindings/leds/common.txt
+	- linux,default-trigger :
+	   see Documentation/devicetree/bindings/leds/common.txt
+
+Example:
+
+HVLED string 1 and 3 are controlled by control bank A and HVLED 2 string is
+controlled by control bank B.
+
+led-controller@36 {
+	compatible = "ti,lm3697";
+	reg = <0x36>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	enable-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>;
+	vled-supply = <&vbatt>;
+
+	led@0 {
+		reg = <0>;
+		led-sources = <1 0 1>;
+		runtime-ramp-up-msec = <5000>;
+		runtime-ramp-down-msec = <1000>;
+		label = "white:first_backlight_cluster";
+		linux,default-trigger = "backlight";
+	};
+
+	led@1 {
+		reg = <1>;
+		led-sources = <0 1 0>;
+		runtime-ramp-up-msec = <500>;
+		runtime-ramp-down-msec = <1000>;
+		label = "white:second_backlight_cluster";
+		linux,default-trigger = "backlight";
+	};
+}
+
+All HVLED strings controlled by control bank A
+
+led-controller@36 {
+	compatible = "ti,lm3697";
+	reg = <0x36>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	enable-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>;
+	vled-supply = <&vbatt>;
+
+	led@0 {
+		reg = <0>;
+		led-sources = <1 1 1>;
+		runtime-ramp-up-msec = <500>;
+		runtime-ramp-down-msec = <1000>;
+		label = "white:backlight_cluster";
+		linux,default-trigger = "backlight";
+	};
+}
+
+For more product information please see the link below:
+http://www.ti.com/lit/ds/symlink/lm3697.pdf
-- 
2.19.0

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

* [RFC PATCH 4/9] dt-bindings: leds: Add bindings for lm3697 driver
@ 2018-09-26 13:09   ` Dan Murphy
  0 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 13:09 UTC (permalink / raw)
  To: robh+dt, jacek.anaszewski, pavel
  Cc: devicetree, linux-kernel, lee.jones, linux-omap, linux-leds, Dan Murphy

Add the device tree bindings for the lm3697
LED driver for backlighting and display.

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
 .../devicetree/bindings/leds/leds-lm3697.txt  | 98 +++++++++++++++++++
 1 file changed, 98 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-lm3697.txt

diff --git a/Documentation/devicetree/bindings/leds/leds-lm3697.txt b/Documentation/devicetree/bindings/leds/leds-lm3697.txt
new file mode 100644
index 000000000000..4bb2ed51025b
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-lm3697.txt
@@ -0,0 +1,98 @@
+* Texas Instruments - LM3697 Highly Efficient White LED Driver
+
+The LM3697 11-bit LED driver provides high-
+performance backlight dimming for 1, 2, or 3 series
+LED strings while delivering up to 90% efficiency.
+
+This device is suitable for display and keypad Lighting
+
+Required properties:
+	- compatible:
+		"ti,lm3697"
+	- reg :  I2C slave address
+	- #address-cells : 1
+	- #size-cells : 0
+
+Optional properties:
+	- enable-gpios : GPIO pin to enable/disable the device
+	- vled-supply : LED supply
+
+Required child properties:
+	- reg : 0 - LED is Controlled by bank A
+		1 - LED is Controlled by bank B
+	- led-sources : Indicates which HVLED string is associated to which
+			control bank.  Each element in the array is associated
+			with a specific HVLED string.  Element 0 is HVLED1,
+			element 1 is HVLED2 and element 2 HVLED3.
+			Additional information is contained
+			in Documentation/devicetree/bindings/leds/common.txt
+			0 - HVLED is not active in this control bank
+			1 - HVLED string is controlled by this control bank
+
+Optional child properties:
+	- runtime-ramp-up-msec: Current ramping from one brightness level to
+				the a higher brightness level.
+				Range from 2048 us - 117.44 s
+	- runtime-ramp-down-msec: Current ramping from one brightness level to
+				  the a lower brightness level.
+				  Range from 2048 us - 117.44 s
+	- label : see Documentation/devicetree/bindings/leds/common.txt
+	- linux,default-trigger :
+	   see Documentation/devicetree/bindings/leds/common.txt
+
+Example:
+
+HVLED string 1 and 3 are controlled by control bank A and HVLED 2 string is
+controlled by control bank B.
+
+led-controller@36 {
+	compatible = "ti,lm3697";
+	reg = <0x36>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	enable-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>;
+	vled-supply = <&vbatt>;
+
+	led@0 {
+		reg = <0>;
+		led-sources = <1 0 1>;
+		runtime-ramp-up-msec = <5000>;
+		runtime-ramp-down-msec = <1000>;
+		label = "white:first_backlight_cluster";
+		linux,default-trigger = "backlight";
+	};
+
+	led@1 {
+		reg = <1>;
+		led-sources = <0 1 0>;
+		runtime-ramp-up-msec = <500>;
+		runtime-ramp-down-msec = <1000>;
+		label = "white:second_backlight_cluster";
+		linux,default-trigger = "backlight";
+	};
+}
+
+All HVLED strings controlled by control bank A
+
+led-controller@36 {
+	compatible = "ti,lm3697";
+	reg = <0x36>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	enable-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>;
+	vled-supply = <&vbatt>;
+
+	led@0 {
+		reg = <0>;
+		led-sources = <1 1 1>;
+		runtime-ramp-up-msec = <500>;
+		runtime-ramp-down-msec = <1000>;
+		label = "white:backlight_cluster";
+		linux,default-trigger = "backlight";
+	};
+}
+
+For more product information please see the link below:
+http://www.ti.com/lit/ds/symlink/lm3697.pdf
-- 
2.19.0


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

* [RFC PATCH 5/9] leds: lm3697: Introduce the lm3697 driver
  2018-09-26 13:09 ` Dan Murphy
@ 2018-09-26 13:09   ` Dan Murphy
  -1 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 13:09 UTC (permalink / raw)
  To: robh+dt, jacek.anaszewski, pavel
  Cc: devicetree, linux-kernel, lee.jones, linux-omap, linux-leds, Dan Murphy

Introduce the lm3697 LED driver for
backlighting and display.

Datasheet location:
http://www.ti.com/lit/ds/symlink/lm3697.pdf

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
 drivers/leds/Kconfig       |   8 +-
 drivers/leds/Makefile      |   1 +
 drivers/leds/leds-lm3697.c | 389 +++++++++++++++++++++++++++++++++++++
 3 files changed, 397 insertions(+), 1 deletion(-)
 create mode 100644 drivers/leds/leds-lm3697.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index dc717b30d9d3..1de4cbb13bd2 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -756,9 +756,15 @@ config LEDS_NIC78BX
 	  To compile this driver as a module, choose M here: the module
 	  will be called leds-nic78bx.
 
+config LEDS_LM3697
+	tristate "LED driver for LM3697"
+ 	depends on LEDS_TI_LMU_COMMON
+	help
+          Say Y to enable the LED driver for TI LMU devices.
+          This supports the LED device LM3697.
+
 config LEDS_TI_LMU_COMMON
 	tristate "LED driver for TI LMU"
-	depends on REGMAP
 	help
           Say Y to enable the LED driver for TI LMU devices.
           This supports common features between the TI LM3532, LM3631, LM3632,
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index e09bb27bc7ea..6fbce7cfc41c 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -78,6 +78,7 @@ obj-$(CONFIG_LEDS_MT6323)		+= leds-mt6323.o
 obj-$(CONFIG_LEDS_LM3692X)		+= leds-lm3692x.o
 obj-$(CONFIG_LEDS_SC27XX_BLTC)		+= leds-sc27xx-bltc.o
 obj-$(CONFIG_LEDS_LM3601X)		+= leds-lm3601x.o
+obj-$(CONFIG_LEDS_LM3697)		+= leds-lm3697.o
 obj-$(CONFIG_LEDS_TI_LMU_COMMON)	+= ti-lmu-led-common.o
 
 # LED SPI Drivers
diff --git a/drivers/leds/leds-lm3697.c b/drivers/leds/leds-lm3697.c
new file mode 100644
index 000000000000..9d3d39c6c649
--- /dev/null
+++ b/drivers/leds/leds-lm3697.c
@@ -0,0 +1,389 @@
+// SPDX-License-Identifier: GPL-2.0
+// TI LM3697 LED chip family driver
+// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <uapi/linux/uleds.h>
+
+#include "ti-lmu-led-common.h"
+
+#define LM3697_REV			0x0
+#define LM3697_RESET			0x1
+#define LM3697_OUTPUT_CONFIG		0x10
+#define LM3697_CTRL_A_RAMP		0x11
+#define LM3697_CTRL_B_RAMP		0x12
+#define LM3697_CTRL_A_B_RT_RAMP		0x13
+#define LM3697_CTRL_A_B_RAMP_CFG	0x14
+#define LM3697_CTRL_A_B_BRT_CFG		0x16
+#define LM3697_CTRL_A_FS_CURR_CFG	0x17
+#define LM3697_CTRL_B_FS_CURR_CFG	0x18
+#define LM3697_PWM_CFG			0x1c
+#define LM3697_CTRL_A_BRT_LSB		0x20
+#define LM3697_CTRL_A_BRT_MSB		0x21
+#define LM3697_CTRL_B_BRT_LSB		0x22
+#define LM3697_CTRL_B_BRT_MSB		0x23
+#define LM3697_CTRL_ENABLE		0x24
+
+#define LM3697_SW_RESET		BIT(0)
+
+#define LM3697_CTRL_A_EN	BIT(0)
+#define LM3697_CTRL_B_EN	BIT(1)
+#define LM3697_CTRL_A_B_EN	(LM3697_CTRL_A_EN | LM3697_CTRL_B_EN)
+
+#define LM3697_MAX_LED_STRINGS	3
+
+#define LM3697_CONTROL_A	0
+#define LM3697_CONTROL_B	1
+#define LM3697_MAX_CONTROL_BANKS 2
+
+#define LM3697_HVLED_ASSIGNMENT	1
+
+/**
+ * struct lm3697_led -
+ * @hvled_strings - Array of LED strings associated with a control bank
+ * @label - LED label
+ * @led_dev - LED class device
+ * @priv - Pointer to the device struct
+ * @control_bank - Control bank the LED is associated to. 0 is control bank A
+ *		   1 is control bank B
+ */
+struct lm3697_led {
+	u32 hvled_strings[LM3697_MAX_LED_STRINGS];
+	char label[LED_MAX_NAME_SIZE];
+	struct led_classdev led_dev;
+	struct lm3697 *priv;
+	struct ti_lmu_bank lmu_data;
+	int control_bank;
+};
+
+/**
+ * struct lm3697 -
+ * @enable_gpio - Hardware enable gpio
+ * @regulator - LED supply regulator pointer
+ * @client - Pointer to the I2C client
+ * @regmap - Devices register map
+ * @dev - Pointer to the devices device struct
+ * @lock - Lock for reading/writing the device
+ * @leds - Array of LED strings
+ */
+struct lm3697 {
+	struct gpio_desc *enable_gpio;
+	struct regulator *regulator;
+	struct i2c_client *client;
+	struct regmap *regmap;
+	struct device *dev;
+	struct mutex lock;
+	struct lm3697_led leds[];
+};
+
+static const struct reg_default lm3697_reg_defs[] = {
+	{LM3697_OUTPUT_CONFIG, 0x6},
+	{LM3697_CTRL_A_RAMP, 0x0},
+	{LM3697_CTRL_B_RAMP, 0x0},
+	{LM3697_CTRL_A_B_RT_RAMP, 0x0},
+	{LM3697_CTRL_A_B_RAMP_CFG, 0x0},
+	{LM3697_CTRL_A_B_BRT_CFG, 0x0},
+	{LM3697_CTRL_A_FS_CURR_CFG, 0x13},
+	{LM3697_CTRL_B_FS_CURR_CFG, 0x13},
+	{LM3697_PWM_CFG, 0xc},
+	{LM3697_CTRL_A_BRT_LSB, 0x0},
+	{LM3697_CTRL_A_BRT_MSB, 0x0},
+	{LM3697_CTRL_B_BRT_LSB, 0x0},
+	{LM3697_CTRL_B_BRT_MSB, 0x0},
+	{LM3697_CTRL_ENABLE, 0x0},
+};
+
+static const struct regmap_config lm3697_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = LM3697_CTRL_ENABLE,
+	.reg_defaults = lm3697_reg_defs,
+	.num_reg_defaults = ARRAY_SIZE(lm3697_reg_defs),
+	.cache_type = REGCACHE_FLAT,
+};
+
+static int lm3697_brightness_set(struct led_classdev *led_cdev,
+				enum led_brightness brt_val)
+{
+	struct lm3697_led *led = container_of(led_cdev, struct lm3697_led,
+					      led_dev);
+	int ctrl_en_val;
+	int ret;
+
+	mutex_lock(&led->priv->lock);
+
+	if (led->control_bank == LM3697_CONTROL_A) {
+		led->lmu_data.msb_brightness_reg = LM3697_CTRL_A_BRT_MSB;
+		led->lmu_data.lsb_brightness_reg = LM3697_CTRL_A_BRT_LSB;
+		ctrl_en_val = LM3697_CTRL_A_EN;
+	} else {
+		led->lmu_data.msb_brightness_reg = LM3697_CTRL_B_BRT_MSB;
+		led->lmu_data.lsb_brightness_reg = LM3697_CTRL_B_BRT_LSB;
+		ctrl_en_val = LM3697_CTRL_B_EN;
+	}
+
+	if (brt_val == LED_OFF)
+		ret = regmap_update_bits(led->priv->regmap, LM3697_CTRL_ENABLE,
+					 ctrl_en_val, ~ctrl_en_val);
+	else
+		ret = regmap_update_bits(led->priv->regmap, LM3697_CTRL_ENABLE,
+					 ctrl_en_val, ctrl_en_val);
+
+	ret = ti_lmu_common_set_brightness(&led->lmu_data, brt_val);
+	if (ret)
+		dev_err(&led->priv->client->dev, "Cannot write brightness\n");
+
+	mutex_unlock(&led->priv->lock);
+	return ret;
+}
+
+static int lm3697_set_control_bank(struct lm3697 *priv)
+{
+	u8 control_bank_config = 0;
+	struct lm3697_led *led;
+	int ret, i;
+
+	led = &priv->leds[0];
+	if (led->control_bank == LM3697_CONTROL_A)
+		led = &priv->leds[1];
+
+	for (i = 0; i < LM3697_MAX_LED_STRINGS; i++)
+		if (led->hvled_strings[i] == LM3697_HVLED_ASSIGNMENT)
+			control_bank_config |= 1 << i;
+
+	ret = regmap_write(priv->regmap, LM3697_OUTPUT_CONFIG,
+			   control_bank_config);
+	if (ret)
+		dev_err(&priv->client->dev, "Cannot write OUTPUT config\n");
+
+	return ret;
+}
+
+static int lm3697_init(struct lm3697 *priv)
+{
+	struct lm3697_led *led;
+	int i, ret;
+
+	if (priv->enable_gpio) {
+		gpiod_direction_output(priv->enable_gpio, 1);
+	} else {
+		ret = regmap_write(priv->regmap, LM3697_RESET, LM3697_SW_RESET);
+		if (ret) {
+			dev_err(&priv->client->dev, "Cannot reset the device\n");
+			goto out;
+		}
+	}
+
+	ret = regmap_write(priv->regmap, LM3697_CTRL_ENABLE, 0x0);
+	if (ret) {
+		dev_err(&priv->client->dev, "Cannot write ctrl enable\n");
+		goto out;
+	}
+
+	ret = lm3697_set_control_bank(priv);
+	if (ret)
+		dev_err(&priv->client->dev, "Setting the CRTL bank failed\n");
+
+	for (i = 0; i < LM3697_MAX_CONTROL_BANKS; i++) {
+		led = &priv->leds[i];
+		ti_lmu_common_set_ramp(&led->lmu_data);
+		if (ret)
+			dev_err(&priv->client->dev, "Setting the ramp rate failed\n");
+	}
+out:
+	return ret;
+}
+
+static int lm3697_probe_dt(struct lm3697 *priv)
+{
+	struct fwnode_handle *child = NULL;
+	struct lm3697_led *led;
+	const char *name;
+	int control_bank;
+	size_t i = 0;
+	int ret;
+
+	priv->enable_gpio = devm_gpiod_get_optional(&priv->client->dev,
+						   "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(priv->enable_gpio)) {
+		ret = PTR_ERR(priv->enable_gpio);
+		dev_err(&priv->client->dev, "Failed to get enable gpio: %d\n",
+			ret);
+		return ret;
+	}
+
+	priv->regulator = devm_regulator_get(&priv->client->dev, "vled");
+	if (IS_ERR(priv->regulator))
+		priv->regulator = NULL;
+
+	device_for_each_child_node(priv->dev, child) {
+		ret = fwnode_property_read_u32(child, "reg", &control_bank);
+		if (ret) {
+			dev_err(&priv->client->dev, "reg property missing\n");
+			fwnode_handle_put(child);
+			goto child_out;
+		}
+
+		if (control_bank > LM3697_CONTROL_B) {
+			dev_err(&priv->client->dev, "reg property is invalid\n");
+			ret = -EINVAL;
+			fwnode_handle_put(child);
+			goto child_out;
+		}
+
+		led = &priv->leds[i];
+
+		led->control_bank = control_bank;
+		led->lmu_data.bank_id = control_bank;
+		led->lmu_data.enable_reg = LM3697_CTRL_ENABLE;
+		led->lmu_data.regmap = priv->regmap;
+		if (control_bank == LM3697_CONTROL_A)
+			led->lmu_data.runtime_ramp_reg = LM3697_CTRL_A_RAMP;
+		else
+			led->lmu_data.runtime_ramp_reg = LM3697_CTRL_B_RAMP;
+
+		ret = fwnode_property_read_u32_array(child, "led-sources",
+						    led->hvled_strings,
+						    LM3697_MAX_LED_STRINGS);
+		if (ret) {
+			dev_err(&priv->client->dev, "led-sources property missing\n");
+			fwnode_handle_put(child);
+			goto child_out;
+		}
+
+		ret = ti_lmu_common_get_ramp_params(&priv->client->dev, child, &led->lmu_data);
+		if (ret)
+			dev_warn(&priv->client->dev, "runtime-ramp properties missing\n");
+
+		fwnode_property_read_string(child, "linux,default-trigger",
+					    &led->led_dev.default_trigger);
+
+		ret = fwnode_property_read_string(child, "label", &name);
+		if (ret)
+			snprintf(led->label, sizeof(led->label),
+				"%s::", priv->client->name);
+		else
+			snprintf(led->label, sizeof(led->label),
+				 "%s:%s", priv->client->name, name);
+
+		led->priv = priv;
+		led->led_dev.name = led->label;
+		led->led_dev.brightness_set_blocking = lm3697_brightness_set;
+
+		ret = devm_led_classdev_register(priv->dev, &led->led_dev);
+		if (ret) {
+			dev_err(&priv->client->dev, "led register err: %d\n",
+				ret);
+			fwnode_handle_put(child);
+			goto child_out;
+		}
+
+		i++;
+	}
+
+child_out:
+	return ret;
+}
+
+static int lm3697_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct lm3697 *led;
+	int count;
+	int ret;
+
+	count = device_get_child_node_count(&client->dev);
+	if (!count) {
+		dev_err(&client->dev, "LEDs are not defined in device tree!");
+		return -ENODEV;
+	}
+
+	led = devm_kzalloc(&client->dev, struct_size(led, leds, count),
+			   GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	mutex_init(&led->lock);
+	i2c_set_clientdata(client, led);
+
+	led->client = client;
+	led->dev = &client->dev;
+	led->regmap = devm_regmap_init_i2c(client, &lm3697_regmap_config);
+	if (IS_ERR(led->regmap)) {
+		ret = PTR_ERR(led->regmap);
+		dev_err(&client->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = lm3697_probe_dt(led);
+	if (ret)
+		return ret;
+
+	return lm3697_init(led);
+}
+
+static int lm3697_remove(struct i2c_client *client)
+{
+	struct lm3697 *led = i2c_get_clientdata(client);
+	int ret;
+
+	ret = regmap_update_bits(led->regmap, LM3697_CTRL_ENABLE,
+				 LM3697_CTRL_A_B_EN, 0);
+	if (ret) {
+		dev_err(&led->client->dev, "Failed to disable the device\n");
+		return ret;
+	}
+
+	if (led->enable_gpio)
+		gpiod_direction_output(led->enable_gpio, 0);
+
+	if (led->regulator) {
+		ret = regulator_disable(led->regulator);
+		if (ret)
+			dev_err(&led->client->dev,
+				"Failed to disable regulator\n");
+	}
+
+	mutex_destroy(&led->lock);
+
+	return 0;
+}
+
+static const struct i2c_device_id lm3697_id[] = {
+	{ "lm3697", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, lm3697_id);
+
+static const struct of_device_id of_lm3697_leds_match[] = {
+	{ .compatible = "ti,lm3697", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, of_lm3697_leds_match);
+
+static struct i2c_driver lm3697_driver = {
+	.driver = {
+		.name	= "lm3697",
+		.of_match_table = of_lm3697_leds_match,
+	},
+	.probe		= lm3697_probe,
+	.remove		= lm3697_remove,
+	.id_table	= lm3697_id,
+};
+module_i2c_driver(lm3697_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LM3697 LED driver");
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.19.0

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

* [RFC PATCH 5/9] leds: lm3697: Introduce the lm3697 driver
@ 2018-09-26 13:09   ` Dan Murphy
  0 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 13:09 UTC (permalink / raw)
  To: robh+dt, jacek.anaszewski, pavel
  Cc: devicetree, linux-kernel, lee.jones, linux-omap, linux-leds, Dan Murphy

Introduce the lm3697 LED driver for
backlighting and display.

Datasheet location:
http://www.ti.com/lit/ds/symlink/lm3697.pdf

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
 drivers/leds/Kconfig       |   8 +-
 drivers/leds/Makefile      |   1 +
 drivers/leds/leds-lm3697.c | 389 +++++++++++++++++++++++++++++++++++++
 3 files changed, 397 insertions(+), 1 deletion(-)
 create mode 100644 drivers/leds/leds-lm3697.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index dc717b30d9d3..1de4cbb13bd2 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -756,9 +756,15 @@ config LEDS_NIC78BX
 	  To compile this driver as a module, choose M here: the module
 	  will be called leds-nic78bx.
 
+config LEDS_LM3697
+	tristate "LED driver for LM3697"
+ 	depends on LEDS_TI_LMU_COMMON
+	help
+          Say Y to enable the LED driver for TI LMU devices.
+          This supports the LED device LM3697.
+
 config LEDS_TI_LMU_COMMON
 	tristate "LED driver for TI LMU"
-	depends on REGMAP
 	help
           Say Y to enable the LED driver for TI LMU devices.
           This supports common features between the TI LM3532, LM3631, LM3632,
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index e09bb27bc7ea..6fbce7cfc41c 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -78,6 +78,7 @@ obj-$(CONFIG_LEDS_MT6323)		+= leds-mt6323.o
 obj-$(CONFIG_LEDS_LM3692X)		+= leds-lm3692x.o
 obj-$(CONFIG_LEDS_SC27XX_BLTC)		+= leds-sc27xx-bltc.o
 obj-$(CONFIG_LEDS_LM3601X)		+= leds-lm3601x.o
+obj-$(CONFIG_LEDS_LM3697)		+= leds-lm3697.o
 obj-$(CONFIG_LEDS_TI_LMU_COMMON)	+= ti-lmu-led-common.o
 
 # LED SPI Drivers
diff --git a/drivers/leds/leds-lm3697.c b/drivers/leds/leds-lm3697.c
new file mode 100644
index 000000000000..9d3d39c6c649
--- /dev/null
+++ b/drivers/leds/leds-lm3697.c
@@ -0,0 +1,389 @@
+// SPDX-License-Identifier: GPL-2.0
+// TI LM3697 LED chip family driver
+// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <uapi/linux/uleds.h>
+
+#include "ti-lmu-led-common.h"
+
+#define LM3697_REV			0x0
+#define LM3697_RESET			0x1
+#define LM3697_OUTPUT_CONFIG		0x10
+#define LM3697_CTRL_A_RAMP		0x11
+#define LM3697_CTRL_B_RAMP		0x12
+#define LM3697_CTRL_A_B_RT_RAMP		0x13
+#define LM3697_CTRL_A_B_RAMP_CFG	0x14
+#define LM3697_CTRL_A_B_BRT_CFG		0x16
+#define LM3697_CTRL_A_FS_CURR_CFG	0x17
+#define LM3697_CTRL_B_FS_CURR_CFG	0x18
+#define LM3697_PWM_CFG			0x1c
+#define LM3697_CTRL_A_BRT_LSB		0x20
+#define LM3697_CTRL_A_BRT_MSB		0x21
+#define LM3697_CTRL_B_BRT_LSB		0x22
+#define LM3697_CTRL_B_BRT_MSB		0x23
+#define LM3697_CTRL_ENABLE		0x24
+
+#define LM3697_SW_RESET		BIT(0)
+
+#define LM3697_CTRL_A_EN	BIT(0)
+#define LM3697_CTRL_B_EN	BIT(1)
+#define LM3697_CTRL_A_B_EN	(LM3697_CTRL_A_EN | LM3697_CTRL_B_EN)
+
+#define LM3697_MAX_LED_STRINGS	3
+
+#define LM3697_CONTROL_A	0
+#define LM3697_CONTROL_B	1
+#define LM3697_MAX_CONTROL_BANKS 2
+
+#define LM3697_HVLED_ASSIGNMENT	1
+
+/**
+ * struct lm3697_led -
+ * @hvled_strings - Array of LED strings associated with a control bank
+ * @label - LED label
+ * @led_dev - LED class device
+ * @priv - Pointer to the device struct
+ * @control_bank - Control bank the LED is associated to. 0 is control bank A
+ *		   1 is control bank B
+ */
+struct lm3697_led {
+	u32 hvled_strings[LM3697_MAX_LED_STRINGS];
+	char label[LED_MAX_NAME_SIZE];
+	struct led_classdev led_dev;
+	struct lm3697 *priv;
+	struct ti_lmu_bank lmu_data;
+	int control_bank;
+};
+
+/**
+ * struct lm3697 -
+ * @enable_gpio - Hardware enable gpio
+ * @regulator - LED supply regulator pointer
+ * @client - Pointer to the I2C client
+ * @regmap - Devices register map
+ * @dev - Pointer to the devices device struct
+ * @lock - Lock for reading/writing the device
+ * @leds - Array of LED strings
+ */
+struct lm3697 {
+	struct gpio_desc *enable_gpio;
+	struct regulator *regulator;
+	struct i2c_client *client;
+	struct regmap *regmap;
+	struct device *dev;
+	struct mutex lock;
+	struct lm3697_led leds[];
+};
+
+static const struct reg_default lm3697_reg_defs[] = {
+	{LM3697_OUTPUT_CONFIG, 0x6},
+	{LM3697_CTRL_A_RAMP, 0x0},
+	{LM3697_CTRL_B_RAMP, 0x0},
+	{LM3697_CTRL_A_B_RT_RAMP, 0x0},
+	{LM3697_CTRL_A_B_RAMP_CFG, 0x0},
+	{LM3697_CTRL_A_B_BRT_CFG, 0x0},
+	{LM3697_CTRL_A_FS_CURR_CFG, 0x13},
+	{LM3697_CTRL_B_FS_CURR_CFG, 0x13},
+	{LM3697_PWM_CFG, 0xc},
+	{LM3697_CTRL_A_BRT_LSB, 0x0},
+	{LM3697_CTRL_A_BRT_MSB, 0x0},
+	{LM3697_CTRL_B_BRT_LSB, 0x0},
+	{LM3697_CTRL_B_BRT_MSB, 0x0},
+	{LM3697_CTRL_ENABLE, 0x0},
+};
+
+static const struct regmap_config lm3697_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = LM3697_CTRL_ENABLE,
+	.reg_defaults = lm3697_reg_defs,
+	.num_reg_defaults = ARRAY_SIZE(lm3697_reg_defs),
+	.cache_type = REGCACHE_FLAT,
+};
+
+static int lm3697_brightness_set(struct led_classdev *led_cdev,
+				enum led_brightness brt_val)
+{
+	struct lm3697_led *led = container_of(led_cdev, struct lm3697_led,
+					      led_dev);
+	int ctrl_en_val;
+	int ret;
+
+	mutex_lock(&led->priv->lock);
+
+	if (led->control_bank == LM3697_CONTROL_A) {
+		led->lmu_data.msb_brightness_reg = LM3697_CTRL_A_BRT_MSB;
+		led->lmu_data.lsb_brightness_reg = LM3697_CTRL_A_BRT_LSB;
+		ctrl_en_val = LM3697_CTRL_A_EN;
+	} else {
+		led->lmu_data.msb_brightness_reg = LM3697_CTRL_B_BRT_MSB;
+		led->lmu_data.lsb_brightness_reg = LM3697_CTRL_B_BRT_LSB;
+		ctrl_en_val = LM3697_CTRL_B_EN;
+	}
+
+	if (brt_val == LED_OFF)
+		ret = regmap_update_bits(led->priv->regmap, LM3697_CTRL_ENABLE,
+					 ctrl_en_val, ~ctrl_en_val);
+	else
+		ret = regmap_update_bits(led->priv->regmap, LM3697_CTRL_ENABLE,
+					 ctrl_en_val, ctrl_en_val);
+
+	ret = ti_lmu_common_set_brightness(&led->lmu_data, brt_val);
+	if (ret)
+		dev_err(&led->priv->client->dev, "Cannot write brightness\n");
+
+	mutex_unlock(&led->priv->lock);
+	return ret;
+}
+
+static int lm3697_set_control_bank(struct lm3697 *priv)
+{
+	u8 control_bank_config = 0;
+	struct lm3697_led *led;
+	int ret, i;
+
+	led = &priv->leds[0];
+	if (led->control_bank == LM3697_CONTROL_A)
+		led = &priv->leds[1];
+
+	for (i = 0; i < LM3697_MAX_LED_STRINGS; i++)
+		if (led->hvled_strings[i] == LM3697_HVLED_ASSIGNMENT)
+			control_bank_config |= 1 << i;
+
+	ret = regmap_write(priv->regmap, LM3697_OUTPUT_CONFIG,
+			   control_bank_config);
+	if (ret)
+		dev_err(&priv->client->dev, "Cannot write OUTPUT config\n");
+
+	return ret;
+}
+
+static int lm3697_init(struct lm3697 *priv)
+{
+	struct lm3697_led *led;
+	int i, ret;
+
+	if (priv->enable_gpio) {
+		gpiod_direction_output(priv->enable_gpio, 1);
+	} else {
+		ret = regmap_write(priv->regmap, LM3697_RESET, LM3697_SW_RESET);
+		if (ret) {
+			dev_err(&priv->client->dev, "Cannot reset the device\n");
+			goto out;
+		}
+	}
+
+	ret = regmap_write(priv->regmap, LM3697_CTRL_ENABLE, 0x0);
+	if (ret) {
+		dev_err(&priv->client->dev, "Cannot write ctrl enable\n");
+		goto out;
+	}
+
+	ret = lm3697_set_control_bank(priv);
+	if (ret)
+		dev_err(&priv->client->dev, "Setting the CRTL bank failed\n");
+
+	for (i = 0; i < LM3697_MAX_CONTROL_BANKS; i++) {
+		led = &priv->leds[i];
+		ti_lmu_common_set_ramp(&led->lmu_data);
+		if (ret)
+			dev_err(&priv->client->dev, "Setting the ramp rate failed\n");
+	}
+out:
+	return ret;
+}
+
+static int lm3697_probe_dt(struct lm3697 *priv)
+{
+	struct fwnode_handle *child = NULL;
+	struct lm3697_led *led;
+	const char *name;
+	int control_bank;
+	size_t i = 0;
+	int ret;
+
+	priv->enable_gpio = devm_gpiod_get_optional(&priv->client->dev,
+						   "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(priv->enable_gpio)) {
+		ret = PTR_ERR(priv->enable_gpio);
+		dev_err(&priv->client->dev, "Failed to get enable gpio: %d\n",
+			ret);
+		return ret;
+	}
+
+	priv->regulator = devm_regulator_get(&priv->client->dev, "vled");
+	if (IS_ERR(priv->regulator))
+		priv->regulator = NULL;
+
+	device_for_each_child_node(priv->dev, child) {
+		ret = fwnode_property_read_u32(child, "reg", &control_bank);
+		if (ret) {
+			dev_err(&priv->client->dev, "reg property missing\n");
+			fwnode_handle_put(child);
+			goto child_out;
+		}
+
+		if (control_bank > LM3697_CONTROL_B) {
+			dev_err(&priv->client->dev, "reg property is invalid\n");
+			ret = -EINVAL;
+			fwnode_handle_put(child);
+			goto child_out;
+		}
+
+		led = &priv->leds[i];
+
+		led->control_bank = control_bank;
+		led->lmu_data.bank_id = control_bank;
+		led->lmu_data.enable_reg = LM3697_CTRL_ENABLE;
+		led->lmu_data.regmap = priv->regmap;
+		if (control_bank == LM3697_CONTROL_A)
+			led->lmu_data.runtime_ramp_reg = LM3697_CTRL_A_RAMP;
+		else
+			led->lmu_data.runtime_ramp_reg = LM3697_CTRL_B_RAMP;
+
+		ret = fwnode_property_read_u32_array(child, "led-sources",
+						    led->hvled_strings,
+						    LM3697_MAX_LED_STRINGS);
+		if (ret) {
+			dev_err(&priv->client->dev, "led-sources property missing\n");
+			fwnode_handle_put(child);
+			goto child_out;
+		}
+
+		ret = ti_lmu_common_get_ramp_params(&priv->client->dev, child, &led->lmu_data);
+		if (ret)
+			dev_warn(&priv->client->dev, "runtime-ramp properties missing\n");
+
+		fwnode_property_read_string(child, "linux,default-trigger",
+					    &led->led_dev.default_trigger);
+
+		ret = fwnode_property_read_string(child, "label", &name);
+		if (ret)
+			snprintf(led->label, sizeof(led->label),
+				"%s::", priv->client->name);
+		else
+			snprintf(led->label, sizeof(led->label),
+				 "%s:%s", priv->client->name, name);
+
+		led->priv = priv;
+		led->led_dev.name = led->label;
+		led->led_dev.brightness_set_blocking = lm3697_brightness_set;
+
+		ret = devm_led_classdev_register(priv->dev, &led->led_dev);
+		if (ret) {
+			dev_err(&priv->client->dev, "led register err: %d\n",
+				ret);
+			fwnode_handle_put(child);
+			goto child_out;
+		}
+
+		i++;
+	}
+
+child_out:
+	return ret;
+}
+
+static int lm3697_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct lm3697 *led;
+	int count;
+	int ret;
+
+	count = device_get_child_node_count(&client->dev);
+	if (!count) {
+		dev_err(&client->dev, "LEDs are not defined in device tree!");
+		return -ENODEV;
+	}
+
+	led = devm_kzalloc(&client->dev, struct_size(led, leds, count),
+			   GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	mutex_init(&led->lock);
+	i2c_set_clientdata(client, led);
+
+	led->client = client;
+	led->dev = &client->dev;
+	led->regmap = devm_regmap_init_i2c(client, &lm3697_regmap_config);
+	if (IS_ERR(led->regmap)) {
+		ret = PTR_ERR(led->regmap);
+		dev_err(&client->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = lm3697_probe_dt(led);
+	if (ret)
+		return ret;
+
+	return lm3697_init(led);
+}
+
+static int lm3697_remove(struct i2c_client *client)
+{
+	struct lm3697 *led = i2c_get_clientdata(client);
+	int ret;
+
+	ret = regmap_update_bits(led->regmap, LM3697_CTRL_ENABLE,
+				 LM3697_CTRL_A_B_EN, 0);
+	if (ret) {
+		dev_err(&led->client->dev, "Failed to disable the device\n");
+		return ret;
+	}
+
+	if (led->enable_gpio)
+		gpiod_direction_output(led->enable_gpio, 0);
+
+	if (led->regulator) {
+		ret = regulator_disable(led->regulator);
+		if (ret)
+			dev_err(&led->client->dev,
+				"Failed to disable regulator\n");
+	}
+
+	mutex_destroy(&led->lock);
+
+	return 0;
+}
+
+static const struct i2c_device_id lm3697_id[] = {
+	{ "lm3697", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, lm3697_id);
+
+static const struct of_device_id of_lm3697_leds_match[] = {
+	{ .compatible = "ti,lm3697", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, of_lm3697_leds_match);
+
+static struct i2c_driver lm3697_driver = {
+	.driver = {
+		.name	= "lm3697",
+		.of_match_table = of_lm3697_leds_match,
+	},
+	.probe		= lm3697_probe,
+	.remove		= lm3697_remove,
+	.id_table	= lm3697_id,
+};
+module_i2c_driver(lm3697_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LM3697 LED driver");
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.19.0


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

* [RFC PATCH 6/9] dt-bindings: leds: Add support for the LM3633
  2018-09-26 13:09 ` Dan Murphy
@ 2018-09-26 13:09   ` Dan Murphy
  -1 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 13:09 UTC (permalink / raw)
  To: robh+dt, jacek.anaszewski, pavel
  Cc: devicetree, linux-kernel, lee.jones, linux-omap, linux-leds, Dan Murphy

Add support for the TI LM3633 LED driver.
This device has 9 control banks 3 high voltage
LED channels and 6 low voltage LED channels.

Data sheet:
http://www.ti.com/lit/ds/symlink/lm3633.pdf

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
 .../devicetree/bindings/leds/leds-lm3633.txt  | 69 +++++++++++++++++++
 1 file changed, 69 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-lm3633.txt

diff --git a/Documentation/devicetree/bindings/leds/leds-lm3633.txt b/Documentation/devicetree/bindings/leds/leds-lm3633.txt
new file mode 100644
index 000000000000..19571ad251ea
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-lm3633.txt
@@ -0,0 +1,69 @@
+* Texas Instruments - LM3633 Lighting Power Solution for Smartphone Handsets
+
+The LM3633 is a complete power source for
+backlight, keypad, and indicator LEDs in smartphone
+handsets.
+
+This device is suitable for display and keypad Lighting
+
+Required properties:
+	- compatible:
+		"ti,lm3633"
+	- reg :  I2C slave address
+	- #address-cells : 1
+	- #size-cells : 0
+
+Optional properties:
+	- enable-gpios : GPIO pin to enable/disable the device
+	- vled-supply : LED supply
+
+Required child properties:
+	- reg : 0 - HVLED is Controlled by bank A
+		1 - HVLED is Controlled by bank B
+		2,3,4 - LVLED1, 2 and 3 are Controlled by bank C, D and E
+		5,6,7 - LVLED4, 5 and 6 are Controlled by bank F, G and H
+	- led-sources : Indicates which LED string is associated to which
+			control bank.
+			0 - LED is not active in this control bank
+			1 - LED string is controlled by this control bank
+
+Optional child properties:
+	- label : see Documentation/devicetree/bindings/leds/common.txt
+	- linux,default-trigger :
+	   see Documentation/devicetree/bindings/leds/common.txt
+
+Example:
+
+led-controller@36 {
+	compatible = "ti,lm3633";
+	reg = <0x36>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	enable-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>;
+	vled-supply = <&vbatt>;
+
+	led@0 {
+		reg = <0>;
+		led-sources = <1 0 1>;
+		label = "white:first_backlight_cluster";
+		linux,default-trigger = "backlight";
+	};
+
+	led@1 {
+		reg = <1>;
+		led-sources = <0 1 0>;
+		label = "white:second_backlight_cluster";
+		linux,default-trigger = "backlight";
+	};
+
+	led@2 {
+		reg = <2>;
+		led-sources = <0 1 0>;
+		label = "white:indicator1";
+	};
+
+}
+
+For more product information please see the link below:
+http://www.ti.com/lit/ds/symlink/lm3633.pdf
-- 
2.19.0

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

* [RFC PATCH 6/9] dt-bindings: leds: Add support for the LM3633
@ 2018-09-26 13:09   ` Dan Murphy
  0 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 13:09 UTC (permalink / raw)
  To: robh+dt, jacek.anaszewski, pavel
  Cc: devicetree, linux-kernel, lee.jones, linux-omap, linux-leds, Dan Murphy

Add support for the TI LM3633 LED driver.
This device has 9 control banks 3 high voltage
LED channels and 6 low voltage LED channels.

Data sheet:
http://www.ti.com/lit/ds/symlink/lm3633.pdf

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
 .../devicetree/bindings/leds/leds-lm3633.txt  | 69 +++++++++++++++++++
 1 file changed, 69 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-lm3633.txt

diff --git a/Documentation/devicetree/bindings/leds/leds-lm3633.txt b/Documentation/devicetree/bindings/leds/leds-lm3633.txt
new file mode 100644
index 000000000000..19571ad251ea
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-lm3633.txt
@@ -0,0 +1,69 @@
+* Texas Instruments - LM3633 Lighting Power Solution for Smartphone Handsets
+
+The LM3633 is a complete power source for
+backlight, keypad, and indicator LEDs in smartphone
+handsets.
+
+This device is suitable for display and keypad Lighting
+
+Required properties:
+	- compatible:
+		"ti,lm3633"
+	- reg :  I2C slave address
+	- #address-cells : 1
+	- #size-cells : 0
+
+Optional properties:
+	- enable-gpios : GPIO pin to enable/disable the device
+	- vled-supply : LED supply
+
+Required child properties:
+	- reg : 0 - HVLED is Controlled by bank A
+		1 - HVLED is Controlled by bank B
+		2,3,4 - LVLED1, 2 and 3 are Controlled by bank C, D and E
+		5,6,7 - LVLED4, 5 and 6 are Controlled by bank F, G and H
+	- led-sources : Indicates which LED string is associated to which
+			control bank.
+			0 - LED is not active in this control bank
+			1 - LED string is controlled by this control bank
+
+Optional child properties:
+	- label : see Documentation/devicetree/bindings/leds/common.txt
+	- linux,default-trigger :
+	   see Documentation/devicetree/bindings/leds/common.txt
+
+Example:
+
+led-controller@36 {
+	compatible = "ti,lm3633";
+	reg = <0x36>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	enable-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>;
+	vled-supply = <&vbatt>;
+
+	led@0 {
+		reg = <0>;
+		led-sources = <1 0 1>;
+		label = "white:first_backlight_cluster";
+		linux,default-trigger = "backlight";
+	};
+
+	led@1 {
+		reg = <1>;
+		led-sources = <0 1 0>;
+		label = "white:second_backlight_cluster";
+		linux,default-trigger = "backlight";
+	};
+
+	led@2 {
+		reg = <2>;
+		led-sources = <0 1 0>;
+		label = "white:indicator1";
+	};
+
+}
+
+For more product information please see the link below:
+http://www.ti.com/lit/ds/symlink/lm3633.pdf
-- 
2.19.0


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

* [RFC PATCH 7/9] leds: lm3633: Introduce the lm3633 driver
  2018-09-26 13:09 ` Dan Murphy
@ 2018-09-26 13:09   ` Dan Murphy
  -1 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 13:09 UTC (permalink / raw)
  To: robh+dt, jacek.anaszewski, pavel
  Cc: devicetree, linux-kernel, lee.jones, linux-omap, linux-leds, Dan Murphy

Introduce the LED LM3633 driver.  This LED
driver has 9 total LED outputs with runtime
internal ramp configurations.

Data sheet:
http://www.ti.com/lit/ds/symlink/lm3633.pdf

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
 drivers/leds/Kconfig       |   8 +
 drivers/leds/Makefile      |   1 +
 drivers/leds/leds-lm3633.c | 430 +++++++++++++++++++++++++++++++++++++
 3 files changed, 439 insertions(+)
 create mode 100644 drivers/leds/leds-lm3633.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 1de4cbb13bd2..1a78588d9806 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -763,8 +763,16 @@ config LEDS_LM3697
           Say Y to enable the LED driver for TI LMU devices.
           This supports the LED device LM3697.
 
+config LEDS_LM3633
+	tristate "LED driver for LM3633"
+ 	depends on LEDS_TI_LMU_COMMON
+	help
+          Say Y to enable the LED driver for TI LMU devices.
+          This supports the LED device LM3633.
+
 config LEDS_TI_LMU_COMMON
 	tristate "LED driver for TI LMU"
+	depends on REGMAP
 	help
           Say Y to enable the LED driver for TI LMU devices.
           This supports common features between the TI LM3532, LM3631, LM3632,
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 6fbce7cfc41c..6ec006892fc0 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -79,6 +79,7 @@ obj-$(CONFIG_LEDS_LM3692X)		+= leds-lm3692x.o
 obj-$(CONFIG_LEDS_SC27XX_BLTC)		+= leds-sc27xx-bltc.o
 obj-$(CONFIG_LEDS_LM3601X)		+= leds-lm3601x.o
 obj-$(CONFIG_LEDS_LM3697)		+= leds-lm3697.o
+obj-$(CONFIG_LEDS_LM3633)		+= leds-lm3633.o
 obj-$(CONFIG_LEDS_TI_LMU_COMMON)	+= ti-lmu-led-common.o
 
 # LED SPI Drivers
diff --git a/drivers/leds/leds-lm3633.c b/drivers/leds/leds-lm3633.c
new file mode 100644
index 000000000000..3d29b0ed67d3
--- /dev/null
+++ b/drivers/leds/leds-lm3633.c
@@ -0,0 +1,430 @@
+// SPDX-License-Identifier: GPL-2.0
+// TI LM3633 LED chip family driver
+// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <uapi/linux/uleds.h>
+
+#include "ti-lmu-led-common.h"
+
+#define LM3633_REV			0x0
+#define LM3633_RESET			0x1
+#define LM3633_HVLED_OUTPUT_CONFIG	0x10
+#define LM3633_LVLED_OUTPUT_CONFIG	0x11
+
+#define LM3633_CTRL_A_RAMP		0x12
+#define LM3633_CTRL_B_RAMP		0x13
+#define LM3633_CTRL_C_RAMP		0x14
+#define LM3633_CTRL_D_RAMP		0x15
+#define LM3633_CTRL_E_RAMP		0x16
+#define LM3633_CTRL_F_RAMP		0x17
+#define LM3633_CTRL_G_RAMP		0x18
+#define LM3633_CTRL_H_RAMP		0x19
+
+#define LM3633_CTRL_A_B_RT_RAMP		0x1a
+#define LM3633_CTRL_A_B_RAMP_CFG	0x1b
+#define LM3633_CTRL_C_E_RT_RAMP		0x1c
+#define LM3633_CTRL_F_H_RT_RAMP		0x1d
+
+#define LM3633_CTRL_A_B_BRT_CFG		0x16
+#define LM3633_CTRL_A_FS_CURR_CFG	0x17
+#define LM3633_CTRL_B_FS_CURR_CFG	0x18
+#define LM3633_PWM_CFG			0x1c
+
+#define LM3633_CTRL_ENABLE		0x2b
+
+#define LM3633_CTRL_A_BRT_LSB		0x40
+#define LM3633_CTRL_A_BRT_MSB		0x41
+#define LM3633_CTRL_B_BRT_LSB		0x42
+#define LM3633_CTRL_B_BRT_MSB		0x43
+#define LM3633_CTRL_C_BRT		0x44
+#define LM3633_CTRL_D_BRT		0x45
+#define LM3633_CTRL_E_BRT		0x46
+#define LM3633_CTRL_F_BRT		0x47
+#define LM3633_CTRL_G_BRT		0x48
+#define LM3633_CTRL_H_BRT		0x49
+
+#define LM3633_SW_RESET		BIT(0)
+
+#define LM3633_CTRL_A_EN	BIT(0)
+#define LM3633_CTRL_B_EN	BIT(1)
+#define LM3633_CTRL_C_EN	BIT(2)
+#define LM3633_CTRL_D_EN	BIT(3)
+#define LM3633_CTRL_E_EN	BIT(4)
+#define LM3633_CTRL_F_EN	BIT(5)
+#define LM3633_CTRL_G_EN	BIT(6)
+#define LM3633_CTRL_H_EN	BIT(7)
+#define LM3633_CTRL_A_B_EN	(LM3633_CTRL_A_EN | LM3633_CTRL_B_EN)
+
+#define LM3633_MAX_HVLED_STRINGS	3
+#define LM3633_MAX_LVLED_STRINGS	6
+
+#define LM3633_CONTROL_A	0
+#define LM3633_CONTROL_B	1
+#define LM3633_CONTROL_C	0
+#define LM3633_CONTROL_D	1
+#define LM3633_CONTROL_E	1
+#define LM3633_CONTROL_F	0
+#define LM3633_CONTROL_G	1
+#define LM3633_CONTROL_H	1
+
+#define LM3633_MAX_CONTROL_BANKS 8
+
+#define LM3633_HVLED_ASSIGNMENT	1
+
+/**
+ * struct lm3633_led -
+ * @hvled_strings - Array of high voltage LED strings associated with a control bank
+ * @hvled_strings - Array of low voltage LED strings associated with a control bank
+ * @label - LED label
+ * @led_dev - LED class device
+ * @priv - Pointer to the device struct
+ * @lmu_data - Register and setting values for common code
+ * @control_bank - Control bank the LED is associated to
+ */
+struct lm3633_led {
+	u32 hvled_strings[LM3633_MAX_HVLED_STRINGS];
+	u32 lvled_strings[LM3633_MAX_LVLED_STRINGS];
+	char label[LED_MAX_NAME_SIZE];
+	struct led_classdev led_dev;
+	struct lm3633 *priv;
+	struct ti_lmu_bank lmu_data;
+	int control_bank;
+};
+
+/**
+ * struct lm3633 -
+ * @enable_gpio - Hardware enable gpio
+ * @regulator - LED supply regulator pointer
+ * @client - Pointer to the I2C client
+ * @regmap - Devices register map
+ * @dev - Pointer to the devices device struct
+ * @lock - Lock for reading/writing the device
+ * @leds - Array of LED strings
+ */
+struct lm3633 {
+	struct gpio_desc *enable_gpio;
+	struct regulator *regulator;
+	struct i2c_client *client;
+	struct regmap *regmap;
+	struct device *dev;
+	struct mutex lock;
+	struct lm3633_led leds[];
+};
+
+static const struct reg_default lm3633_reg_defs[] = {
+	{LM3633_HVLED_OUTPUT_CONFIG, 0x6},
+	{LM3633_CTRL_A_RAMP, 0x0},
+	{LM3633_CTRL_B_RAMP, 0x0},
+	{LM3633_CTRL_A_B_RT_RAMP, 0x0},
+	{LM3633_CTRL_A_B_RAMP_CFG, 0x0},
+	{LM3633_CTRL_A_B_BRT_CFG, 0x0},
+	{LM3633_CTRL_A_FS_CURR_CFG, 0x13},
+	{LM3633_CTRL_B_FS_CURR_CFG, 0x13},
+	{LM3633_PWM_CFG, 0xc},
+	{LM3633_CTRL_A_BRT_LSB, 0x0},
+	{LM3633_CTRL_A_BRT_MSB, 0x0},
+	{LM3633_CTRL_B_BRT_LSB, 0x0},
+	{LM3633_CTRL_B_BRT_MSB, 0x0},
+	{LM3633_CTRL_ENABLE, 0x0},
+};
+
+static const struct regmap_config lm3633_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = LM3633_CTRL_ENABLE,
+	.reg_defaults = lm3633_reg_defs,
+	.num_reg_defaults = ARRAY_SIZE(lm3633_reg_defs),
+	.cache_type = REGCACHE_FLAT,
+};
+
+static int lm3633_brightness_set(struct led_classdev *led_cdev,
+				enum led_brightness brt_val)
+{
+	struct lm3633_led *led = container_of(led_cdev, struct lm3633_led,
+					      led_dev);
+	int ctrl_en_val;
+	int ret;
+
+	mutex_lock(&led->priv->lock);
+
+	if (led->control_bank == LM3633_CONTROL_A) {
+		led->lmu_data.msb_brightness_reg = LM3633_CTRL_A_BRT_MSB;
+		led->lmu_data.lsb_brightness_reg = LM3633_CTRL_A_BRT_LSB;
+		ctrl_en_val = LM3633_CTRL_A_EN;
+	} else {
+		led->lmu_data.msb_brightness_reg = LM3633_CTRL_B_BRT_MSB;
+		led->lmu_data.lsb_brightness_reg = LM3633_CTRL_B_BRT_LSB;
+		ctrl_en_val = LM3633_CTRL_B_EN;
+	}
+
+	if (brt_val == LED_OFF)
+		ret = regmap_update_bits(led->priv->regmap, LM3633_CTRL_ENABLE,
+					 ctrl_en_val, ~ctrl_en_val);
+	else
+		ret = regmap_update_bits(led->priv->regmap, LM3633_CTRL_ENABLE,
+					 ctrl_en_val, ctrl_en_val);
+
+	ret = ti_lmu_common_set_brightness(&led->lmu_data, brt_val);
+	if (ret)
+		dev_err(&led->priv->client->dev, "Cannot write brightness\n");
+
+	mutex_unlock(&led->priv->lock);
+	return ret;
+}
+
+static int lm3633_set_control_bank(struct lm3633 *priv)
+{
+	u8 control_bank_config = 0;
+	struct lm3633_led *led;
+	int ret, i;
+
+	led = &priv->leds[0];
+	if (led->control_bank == LM3633_CONTROL_A)
+		led = &priv->leds[1];
+
+	for (i = 0; i < LM3633_MAX_HVLED_STRINGS; i++)
+		if (led->hvled_strings[i] == LM3633_HVLED_ASSIGNMENT)
+			control_bank_config |= 1 << i;
+
+	ret = regmap_write(priv->regmap, LM3633_HVLED_OUTPUT_CONFIG,
+			   control_bank_config);
+	if (ret)
+		dev_err(&priv->client->dev, "Cannot write OUTPUT config\n");
+
+	return ret;
+}
+
+static int lm3633_init(struct lm3633 *priv)
+{
+	struct lm3633_led *led;
+	int i, ret;
+
+	if (priv->enable_gpio) {
+		gpiod_direction_output(priv->enable_gpio, 1);
+	} else {
+		ret = regmap_write(priv->regmap, LM3633_RESET, LM3633_SW_RESET);
+		if (ret) {
+			dev_err(&priv->client->dev, "Cannot reset the device\n");
+			goto out;
+		}
+	}
+
+	ret = regmap_write(priv->regmap, LM3633_CTRL_ENABLE, 0x0);
+	if (ret) {
+		dev_err(&priv->client->dev, "Cannot write ctrl enable\n");
+		goto out;
+	}
+
+	ret = lm3633_set_control_bank(priv);
+	if (ret)
+		dev_err(&priv->client->dev, "Setting the CRTL bank failed\n");
+
+	for (i = 0; i < LM3633_MAX_CONTROL_BANKS; i++) {
+		led = &priv->leds[i];
+		ti_lmu_common_set_ramp(&led->lmu_data);
+		if (ret)
+			dev_err(&priv->client->dev, "Setting the ramp rate failed\n");
+	}
+out:
+	return ret;
+}
+
+static int lm3633_probe_dt(struct lm3633 *priv)
+{
+	struct fwnode_handle *child = NULL;
+	struct lm3633_led *led;
+	const char *name;
+	int control_bank;
+	size_t i = 0;
+	int ret;
+
+	priv->enable_gpio = devm_gpiod_get_optional(&priv->client->dev,
+						   "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(priv->enable_gpio)) {
+		ret = PTR_ERR(priv->enable_gpio);
+		dev_err(&priv->client->dev, "Failed to get enable gpio: %d\n",
+			ret);
+		return ret;
+	}
+
+	priv->regulator = devm_regulator_get(&priv->client->dev, "vled");
+	if (IS_ERR(priv->regulator))
+		priv->regulator = NULL;
+
+	device_for_each_child_node(priv->dev, child) {
+		ret = fwnode_property_read_u32(child, "reg", &control_bank);
+		if (ret) {
+			dev_err(&priv->client->dev, "reg property missing\n");
+			fwnode_handle_put(child);
+			goto child_out;
+		}
+
+		if (control_bank > LM3633_CONTROL_B) {
+			dev_err(&priv->client->dev, "reg property is invalid\n");
+			ret = -EINVAL;
+			fwnode_handle_put(child);
+			goto child_out;
+		}
+
+		led = &priv->leds[i];
+
+		led->control_bank = control_bank;
+		led->lmu_data.bank_id = control_bank;
+		led->lmu_data.enable_reg = LM3633_CTRL_ENABLE;
+		led->lmu_data.regmap = priv->regmap;
+		if (control_bank == LM3633_CONTROL_A)
+			led->lmu_data.runtime_ramp_reg = LM3633_CTRL_A_RAMP;
+		else
+			led->lmu_data.runtime_ramp_reg = LM3633_CTRL_B_RAMP;
+
+		if (control_bank <= LM3633_CONTROL_B)
+			ret = fwnode_property_read_u32_array(child, "led-sources",
+						    led->hvled_strings,
+						    LM3633_MAX_HVLED_STRINGS);
+		else
+			ret = fwnode_property_read_u32_array(child, "led-sources",
+						    led->lvled_strings,
+						    LM3633_MAX_LVLED_STRINGS);
+		if (ret) {
+			dev_err(&priv->client->dev, "led-sources property missing\n");
+			fwnode_handle_put(child);
+			goto child_out;
+		}
+
+		ret = ti_lmu_common_get_ramp_params(&priv->client->dev, child, &led->lmu_data);
+		if (ret)
+			dev_warn(&priv->client->dev, "runtime-ramp properties missing\n");
+
+		fwnode_property_read_string(child, "linux,default-trigger",
+					    &led->led_dev.default_trigger);
+
+		ret = fwnode_property_read_string(child, "label", &name);
+		if (ret)
+			snprintf(led->label, sizeof(led->label),
+				"%s::", priv->client->name);
+		else
+			snprintf(led->label, sizeof(led->label),
+				 "%s:%s", priv->client->name, name);
+
+		led->priv = priv;
+		led->led_dev.name = led->label;
+		led->led_dev.brightness_set_blocking = lm3633_brightness_set;
+
+		ret = devm_led_classdev_register(priv->dev, &led->led_dev);
+		if (ret) {
+			dev_err(&priv->client->dev, "led register err: %d\n",
+				ret);
+			fwnode_handle_put(child);
+			goto child_out;
+		}
+
+		i++;
+	}
+
+child_out:
+	return ret;
+}
+
+static int lm3633_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct lm3633 *led;
+	int count;
+	int ret;
+
+	count = device_get_child_node_count(&client->dev);
+	if (!count) {
+		dev_err(&client->dev, "LEDs are not defined in device tree!");
+		return -ENODEV;
+	}
+
+	led = devm_kzalloc(&client->dev, struct_size(led, leds, count),
+			   GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	mutex_init(&led->lock);
+	i2c_set_clientdata(client, led);
+
+	led->client = client;
+	led->dev = &client->dev;
+	led->regmap = devm_regmap_init_i2c(client, &lm3633_regmap_config);
+	if (IS_ERR(led->regmap)) {
+		ret = PTR_ERR(led->regmap);
+		dev_err(&client->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = lm3633_probe_dt(led);
+	if (ret)
+		return ret;
+
+	return lm3633_init(led);
+}
+
+static int lm3633_remove(struct i2c_client *client)
+{
+	struct lm3633 *led = i2c_get_clientdata(client);
+	int ret;
+
+	ret = regmap_update_bits(led->regmap, LM3633_CTRL_ENABLE,
+				 LM3633_CTRL_A_B_EN, 0);
+	if (ret) {
+		dev_err(&led->client->dev, "Failed to disable the device\n");
+		return ret;
+	}
+
+	if (led->enable_gpio)
+		gpiod_direction_output(led->enable_gpio, 0);
+
+	if (led->regulator) {
+		ret = regulator_disable(led->regulator);
+		if (ret)
+			dev_err(&led->client->dev,
+				"Failed to disable regulator\n");
+	}
+
+	mutex_destroy(&led->lock);
+
+	return 0;
+}
+
+static const struct i2c_device_id lm3633_id[] = {
+	{ "lm3633", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, lm3633_id);
+
+static const struct of_device_id of_lm3633_leds_match[] = {
+	{ .compatible = "ti,lm3633", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, of_lm3633_leds_match);
+
+static struct i2c_driver lm3633_driver = {
+	.driver = {
+		.name	= "lm3633",
+		.of_match_table = of_lm3633_leds_match,
+	},
+	.probe		= lm3633_probe,
+	.remove		= lm3633_remove,
+	.id_table	= lm3633_id,
+};
+module_i2c_driver(lm3633_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LM3633 LED driver");
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.19.0

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

* [RFC PATCH 7/9] leds: lm3633: Introduce the lm3633 driver
@ 2018-09-26 13:09   ` Dan Murphy
  0 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 13:09 UTC (permalink / raw)
  To: robh+dt, jacek.anaszewski, pavel
  Cc: devicetree, linux-kernel, lee.jones, linux-omap, linux-leds, Dan Murphy

Introduce the LED LM3633 driver.  This LED
driver has 9 total LED outputs with runtime
internal ramp configurations.

Data sheet:
http://www.ti.com/lit/ds/symlink/lm3633.pdf

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
 drivers/leds/Kconfig       |   8 +
 drivers/leds/Makefile      |   1 +
 drivers/leds/leds-lm3633.c | 430 +++++++++++++++++++++++++++++++++++++
 3 files changed, 439 insertions(+)
 create mode 100644 drivers/leds/leds-lm3633.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 1de4cbb13bd2..1a78588d9806 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -763,8 +763,16 @@ config LEDS_LM3697
           Say Y to enable the LED driver for TI LMU devices.
           This supports the LED device LM3697.
 
+config LEDS_LM3633
+	tristate "LED driver for LM3633"
+ 	depends on LEDS_TI_LMU_COMMON
+	help
+          Say Y to enable the LED driver for TI LMU devices.
+          This supports the LED device LM3633.
+
 config LEDS_TI_LMU_COMMON
 	tristate "LED driver for TI LMU"
+	depends on REGMAP
 	help
           Say Y to enable the LED driver for TI LMU devices.
           This supports common features between the TI LM3532, LM3631, LM3632,
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 6fbce7cfc41c..6ec006892fc0 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -79,6 +79,7 @@ obj-$(CONFIG_LEDS_LM3692X)		+= leds-lm3692x.o
 obj-$(CONFIG_LEDS_SC27XX_BLTC)		+= leds-sc27xx-bltc.o
 obj-$(CONFIG_LEDS_LM3601X)		+= leds-lm3601x.o
 obj-$(CONFIG_LEDS_LM3697)		+= leds-lm3697.o
+obj-$(CONFIG_LEDS_LM3633)		+= leds-lm3633.o
 obj-$(CONFIG_LEDS_TI_LMU_COMMON)	+= ti-lmu-led-common.o
 
 # LED SPI Drivers
diff --git a/drivers/leds/leds-lm3633.c b/drivers/leds/leds-lm3633.c
new file mode 100644
index 000000000000..3d29b0ed67d3
--- /dev/null
+++ b/drivers/leds/leds-lm3633.c
@@ -0,0 +1,430 @@
+// SPDX-License-Identifier: GPL-2.0
+// TI LM3633 LED chip family driver
+// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <uapi/linux/uleds.h>
+
+#include "ti-lmu-led-common.h"
+
+#define LM3633_REV			0x0
+#define LM3633_RESET			0x1
+#define LM3633_HVLED_OUTPUT_CONFIG	0x10
+#define LM3633_LVLED_OUTPUT_CONFIG	0x11
+
+#define LM3633_CTRL_A_RAMP		0x12
+#define LM3633_CTRL_B_RAMP		0x13
+#define LM3633_CTRL_C_RAMP		0x14
+#define LM3633_CTRL_D_RAMP		0x15
+#define LM3633_CTRL_E_RAMP		0x16
+#define LM3633_CTRL_F_RAMP		0x17
+#define LM3633_CTRL_G_RAMP		0x18
+#define LM3633_CTRL_H_RAMP		0x19
+
+#define LM3633_CTRL_A_B_RT_RAMP		0x1a
+#define LM3633_CTRL_A_B_RAMP_CFG	0x1b
+#define LM3633_CTRL_C_E_RT_RAMP		0x1c
+#define LM3633_CTRL_F_H_RT_RAMP		0x1d
+
+#define LM3633_CTRL_A_B_BRT_CFG		0x16
+#define LM3633_CTRL_A_FS_CURR_CFG	0x17
+#define LM3633_CTRL_B_FS_CURR_CFG	0x18
+#define LM3633_PWM_CFG			0x1c
+
+#define LM3633_CTRL_ENABLE		0x2b
+
+#define LM3633_CTRL_A_BRT_LSB		0x40
+#define LM3633_CTRL_A_BRT_MSB		0x41
+#define LM3633_CTRL_B_BRT_LSB		0x42
+#define LM3633_CTRL_B_BRT_MSB		0x43
+#define LM3633_CTRL_C_BRT		0x44
+#define LM3633_CTRL_D_BRT		0x45
+#define LM3633_CTRL_E_BRT		0x46
+#define LM3633_CTRL_F_BRT		0x47
+#define LM3633_CTRL_G_BRT		0x48
+#define LM3633_CTRL_H_BRT		0x49
+
+#define LM3633_SW_RESET		BIT(0)
+
+#define LM3633_CTRL_A_EN	BIT(0)
+#define LM3633_CTRL_B_EN	BIT(1)
+#define LM3633_CTRL_C_EN	BIT(2)
+#define LM3633_CTRL_D_EN	BIT(3)
+#define LM3633_CTRL_E_EN	BIT(4)
+#define LM3633_CTRL_F_EN	BIT(5)
+#define LM3633_CTRL_G_EN	BIT(6)
+#define LM3633_CTRL_H_EN	BIT(7)
+#define LM3633_CTRL_A_B_EN	(LM3633_CTRL_A_EN | LM3633_CTRL_B_EN)
+
+#define LM3633_MAX_HVLED_STRINGS	3
+#define LM3633_MAX_LVLED_STRINGS	6
+
+#define LM3633_CONTROL_A	0
+#define LM3633_CONTROL_B	1
+#define LM3633_CONTROL_C	0
+#define LM3633_CONTROL_D	1
+#define LM3633_CONTROL_E	1
+#define LM3633_CONTROL_F	0
+#define LM3633_CONTROL_G	1
+#define LM3633_CONTROL_H	1
+
+#define LM3633_MAX_CONTROL_BANKS 8
+
+#define LM3633_HVLED_ASSIGNMENT	1
+
+/**
+ * struct lm3633_led -
+ * @hvled_strings - Array of high voltage LED strings associated with a control bank
+ * @hvled_strings - Array of low voltage LED strings associated with a control bank
+ * @label - LED label
+ * @led_dev - LED class device
+ * @priv - Pointer to the device struct
+ * @lmu_data - Register and setting values for common code
+ * @control_bank - Control bank the LED is associated to
+ */
+struct lm3633_led {
+	u32 hvled_strings[LM3633_MAX_HVLED_STRINGS];
+	u32 lvled_strings[LM3633_MAX_LVLED_STRINGS];
+	char label[LED_MAX_NAME_SIZE];
+	struct led_classdev led_dev;
+	struct lm3633 *priv;
+	struct ti_lmu_bank lmu_data;
+	int control_bank;
+};
+
+/**
+ * struct lm3633 -
+ * @enable_gpio - Hardware enable gpio
+ * @regulator - LED supply regulator pointer
+ * @client - Pointer to the I2C client
+ * @regmap - Devices register map
+ * @dev - Pointer to the devices device struct
+ * @lock - Lock for reading/writing the device
+ * @leds - Array of LED strings
+ */
+struct lm3633 {
+	struct gpio_desc *enable_gpio;
+	struct regulator *regulator;
+	struct i2c_client *client;
+	struct regmap *regmap;
+	struct device *dev;
+	struct mutex lock;
+	struct lm3633_led leds[];
+};
+
+static const struct reg_default lm3633_reg_defs[] = {
+	{LM3633_HVLED_OUTPUT_CONFIG, 0x6},
+	{LM3633_CTRL_A_RAMP, 0x0},
+	{LM3633_CTRL_B_RAMP, 0x0},
+	{LM3633_CTRL_A_B_RT_RAMP, 0x0},
+	{LM3633_CTRL_A_B_RAMP_CFG, 0x0},
+	{LM3633_CTRL_A_B_BRT_CFG, 0x0},
+	{LM3633_CTRL_A_FS_CURR_CFG, 0x13},
+	{LM3633_CTRL_B_FS_CURR_CFG, 0x13},
+	{LM3633_PWM_CFG, 0xc},
+	{LM3633_CTRL_A_BRT_LSB, 0x0},
+	{LM3633_CTRL_A_BRT_MSB, 0x0},
+	{LM3633_CTRL_B_BRT_LSB, 0x0},
+	{LM3633_CTRL_B_BRT_MSB, 0x0},
+	{LM3633_CTRL_ENABLE, 0x0},
+};
+
+static const struct regmap_config lm3633_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = LM3633_CTRL_ENABLE,
+	.reg_defaults = lm3633_reg_defs,
+	.num_reg_defaults = ARRAY_SIZE(lm3633_reg_defs),
+	.cache_type = REGCACHE_FLAT,
+};
+
+static int lm3633_brightness_set(struct led_classdev *led_cdev,
+				enum led_brightness brt_val)
+{
+	struct lm3633_led *led = container_of(led_cdev, struct lm3633_led,
+					      led_dev);
+	int ctrl_en_val;
+	int ret;
+
+	mutex_lock(&led->priv->lock);
+
+	if (led->control_bank == LM3633_CONTROL_A) {
+		led->lmu_data.msb_brightness_reg = LM3633_CTRL_A_BRT_MSB;
+		led->lmu_data.lsb_brightness_reg = LM3633_CTRL_A_BRT_LSB;
+		ctrl_en_val = LM3633_CTRL_A_EN;
+	} else {
+		led->lmu_data.msb_brightness_reg = LM3633_CTRL_B_BRT_MSB;
+		led->lmu_data.lsb_brightness_reg = LM3633_CTRL_B_BRT_LSB;
+		ctrl_en_val = LM3633_CTRL_B_EN;
+	}
+
+	if (brt_val == LED_OFF)
+		ret = regmap_update_bits(led->priv->regmap, LM3633_CTRL_ENABLE,
+					 ctrl_en_val, ~ctrl_en_val);
+	else
+		ret = regmap_update_bits(led->priv->regmap, LM3633_CTRL_ENABLE,
+					 ctrl_en_val, ctrl_en_val);
+
+	ret = ti_lmu_common_set_brightness(&led->lmu_data, brt_val);
+	if (ret)
+		dev_err(&led->priv->client->dev, "Cannot write brightness\n");
+
+	mutex_unlock(&led->priv->lock);
+	return ret;
+}
+
+static int lm3633_set_control_bank(struct lm3633 *priv)
+{
+	u8 control_bank_config = 0;
+	struct lm3633_led *led;
+	int ret, i;
+
+	led = &priv->leds[0];
+	if (led->control_bank == LM3633_CONTROL_A)
+		led = &priv->leds[1];
+
+	for (i = 0; i < LM3633_MAX_HVLED_STRINGS; i++)
+		if (led->hvled_strings[i] == LM3633_HVLED_ASSIGNMENT)
+			control_bank_config |= 1 << i;
+
+	ret = regmap_write(priv->regmap, LM3633_HVLED_OUTPUT_CONFIG,
+			   control_bank_config);
+	if (ret)
+		dev_err(&priv->client->dev, "Cannot write OUTPUT config\n");
+
+	return ret;
+}
+
+static int lm3633_init(struct lm3633 *priv)
+{
+	struct lm3633_led *led;
+	int i, ret;
+
+	if (priv->enable_gpio) {
+		gpiod_direction_output(priv->enable_gpio, 1);
+	} else {
+		ret = regmap_write(priv->regmap, LM3633_RESET, LM3633_SW_RESET);
+		if (ret) {
+			dev_err(&priv->client->dev, "Cannot reset the device\n");
+			goto out;
+		}
+	}
+
+	ret = regmap_write(priv->regmap, LM3633_CTRL_ENABLE, 0x0);
+	if (ret) {
+		dev_err(&priv->client->dev, "Cannot write ctrl enable\n");
+		goto out;
+	}
+
+	ret = lm3633_set_control_bank(priv);
+	if (ret)
+		dev_err(&priv->client->dev, "Setting the CRTL bank failed\n");
+
+	for (i = 0; i < LM3633_MAX_CONTROL_BANKS; i++) {
+		led = &priv->leds[i];
+		ti_lmu_common_set_ramp(&led->lmu_data);
+		if (ret)
+			dev_err(&priv->client->dev, "Setting the ramp rate failed\n");
+	}
+out:
+	return ret;
+}
+
+static int lm3633_probe_dt(struct lm3633 *priv)
+{
+	struct fwnode_handle *child = NULL;
+	struct lm3633_led *led;
+	const char *name;
+	int control_bank;
+	size_t i = 0;
+	int ret;
+
+	priv->enable_gpio = devm_gpiod_get_optional(&priv->client->dev,
+						   "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(priv->enable_gpio)) {
+		ret = PTR_ERR(priv->enable_gpio);
+		dev_err(&priv->client->dev, "Failed to get enable gpio: %d\n",
+			ret);
+		return ret;
+	}
+
+	priv->regulator = devm_regulator_get(&priv->client->dev, "vled");
+	if (IS_ERR(priv->regulator))
+		priv->regulator = NULL;
+
+	device_for_each_child_node(priv->dev, child) {
+		ret = fwnode_property_read_u32(child, "reg", &control_bank);
+		if (ret) {
+			dev_err(&priv->client->dev, "reg property missing\n");
+			fwnode_handle_put(child);
+			goto child_out;
+		}
+
+		if (control_bank > LM3633_CONTROL_B) {
+			dev_err(&priv->client->dev, "reg property is invalid\n");
+			ret = -EINVAL;
+			fwnode_handle_put(child);
+			goto child_out;
+		}
+
+		led = &priv->leds[i];
+
+		led->control_bank = control_bank;
+		led->lmu_data.bank_id = control_bank;
+		led->lmu_data.enable_reg = LM3633_CTRL_ENABLE;
+		led->lmu_data.regmap = priv->regmap;
+		if (control_bank == LM3633_CONTROL_A)
+			led->lmu_data.runtime_ramp_reg = LM3633_CTRL_A_RAMP;
+		else
+			led->lmu_data.runtime_ramp_reg = LM3633_CTRL_B_RAMP;
+
+		if (control_bank <= LM3633_CONTROL_B)
+			ret = fwnode_property_read_u32_array(child, "led-sources",
+						    led->hvled_strings,
+						    LM3633_MAX_HVLED_STRINGS);
+		else
+			ret = fwnode_property_read_u32_array(child, "led-sources",
+						    led->lvled_strings,
+						    LM3633_MAX_LVLED_STRINGS);
+		if (ret) {
+			dev_err(&priv->client->dev, "led-sources property missing\n");
+			fwnode_handle_put(child);
+			goto child_out;
+		}
+
+		ret = ti_lmu_common_get_ramp_params(&priv->client->dev, child, &led->lmu_data);
+		if (ret)
+			dev_warn(&priv->client->dev, "runtime-ramp properties missing\n");
+
+		fwnode_property_read_string(child, "linux,default-trigger",
+					    &led->led_dev.default_trigger);
+
+		ret = fwnode_property_read_string(child, "label", &name);
+		if (ret)
+			snprintf(led->label, sizeof(led->label),
+				"%s::", priv->client->name);
+		else
+			snprintf(led->label, sizeof(led->label),
+				 "%s:%s", priv->client->name, name);
+
+		led->priv = priv;
+		led->led_dev.name = led->label;
+		led->led_dev.brightness_set_blocking = lm3633_brightness_set;
+
+		ret = devm_led_classdev_register(priv->dev, &led->led_dev);
+		if (ret) {
+			dev_err(&priv->client->dev, "led register err: %d\n",
+				ret);
+			fwnode_handle_put(child);
+			goto child_out;
+		}
+
+		i++;
+	}
+
+child_out:
+	return ret;
+}
+
+static int lm3633_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct lm3633 *led;
+	int count;
+	int ret;
+
+	count = device_get_child_node_count(&client->dev);
+	if (!count) {
+		dev_err(&client->dev, "LEDs are not defined in device tree!");
+		return -ENODEV;
+	}
+
+	led = devm_kzalloc(&client->dev, struct_size(led, leds, count),
+			   GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	mutex_init(&led->lock);
+	i2c_set_clientdata(client, led);
+
+	led->client = client;
+	led->dev = &client->dev;
+	led->regmap = devm_regmap_init_i2c(client, &lm3633_regmap_config);
+	if (IS_ERR(led->regmap)) {
+		ret = PTR_ERR(led->regmap);
+		dev_err(&client->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = lm3633_probe_dt(led);
+	if (ret)
+		return ret;
+
+	return lm3633_init(led);
+}
+
+static int lm3633_remove(struct i2c_client *client)
+{
+	struct lm3633 *led = i2c_get_clientdata(client);
+	int ret;
+
+	ret = regmap_update_bits(led->regmap, LM3633_CTRL_ENABLE,
+				 LM3633_CTRL_A_B_EN, 0);
+	if (ret) {
+		dev_err(&led->client->dev, "Failed to disable the device\n");
+		return ret;
+	}
+
+	if (led->enable_gpio)
+		gpiod_direction_output(led->enable_gpio, 0);
+
+	if (led->regulator) {
+		ret = regulator_disable(led->regulator);
+		if (ret)
+			dev_err(&led->client->dev,
+				"Failed to disable regulator\n");
+	}
+
+	mutex_destroy(&led->lock);
+
+	return 0;
+}
+
+static const struct i2c_device_id lm3633_id[] = {
+	{ "lm3633", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, lm3633_id);
+
+static const struct of_device_id of_lm3633_leds_match[] = {
+	{ .compatible = "ti,lm3633", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, of_lm3633_leds_match);
+
+static struct i2c_driver lm3633_driver = {
+	.driver = {
+		.name	= "lm3633",
+		.of_match_table = of_lm3633_leds_match,
+	},
+	.probe		= lm3633_probe,
+	.remove		= lm3633_remove,
+	.id_table	= lm3633_id,
+};
+module_i2c_driver(lm3633_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LM3633 LED driver");
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.19.0


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

* [RFC PATCH 8/9] dt-bindings: leds: Add the LM3632 LED dt binding
  2018-09-26 13:09 ` Dan Murphy
@ 2018-09-26 13:09   ` Dan Murphy
  -1 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 13:09 UTC (permalink / raw)
  To: robh+dt, jacek.anaszewski, pavel
  Cc: devicetree, linux-kernel, lee.jones, linux-omap, linux-leds, Dan Murphy

Add the TI LM3632 LED binding.

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
 .../devicetree/bindings/leds/leds-lm3632.txt  | 53 +++++++++++++++++++
 1 file changed, 53 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-lm3632.txt

diff --git a/Documentation/devicetree/bindings/leds/leds-lm3632.txt b/Documentation/devicetree/bindings/leds/leds-lm3632.txt
new file mode 100644
index 000000000000..1befd17174ac
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-lm3632.txt
@@ -0,0 +1,53 @@
+* Texas Instruments - lm3632 Single-Chip Backlight and 1.5-A Flash LED Driver
+
+The LM3632A integrates LED drivers for both
+the backlight of the LCD panel and the camera flash
+along with the bias power for the LCD panel into one
+device.
+
+Required properties:
+	- compatible : Can be one of the following
+		"ti,lm3632"
+	- reg : I2C slave address
+	- #address-cells : 1
+	- #size-cells : 0
+
+Required child properties:
+	- reg : 0 - Indicates a backlight mode
+		1 - Indicates a Torch/Strobe (white LED) mode
+
+Required properties for flash LED child nodes:
+	See Documentation/devicetree/bindings/leds/common.txt
+	- flash-max-microamp : Range from 100mA - 1.5A
+	- flash-max-timeout-us : Range from 32ms - 1024ms
+	- led-max-microamp : Range from 25mA - 375mA
+
+Optional child properties:
+	- label : see Documentation/devicetree/bindings/leds/common.txt
+	- linux,default-trigger :
+	   see Documentation/devicetree/bindings/leds/common.txt
+
+Example:
+led-controller@11 {
+	compatible = "ti,lm3632";
+	#address-cells = <1>;
+	#size-cells = <0>;
+	reg = <0x11>;
+
+	led@0 {
+		reg = <0>;
+		label = "white:backlight";
+		linux,default-trigger = "backlight";
+	};
+
+	led@1 {
+		reg = <1>;
+		label = "white:torch";
+		led-max-microamp = <375000>;
+		flash-max-microamp = <1500000>;
+		flash-max-timeout-us = <1000000>;
+	};
+}
+
+For more product information please see the links below:
+http://www.ti.com/product/LM3632
-- 
2.19.0

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

* [RFC PATCH 8/9] dt-bindings: leds: Add the LM3632 LED dt binding
@ 2018-09-26 13:09   ` Dan Murphy
  0 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 13:09 UTC (permalink / raw)
  To: robh+dt, jacek.anaszewski, pavel
  Cc: devicetree, linux-kernel, lee.jones, linux-omap, linux-leds, Dan Murphy

Add the TI LM3632 LED binding.

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
 .../devicetree/bindings/leds/leds-lm3632.txt  | 53 +++++++++++++++++++
 1 file changed, 53 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/leds-lm3632.txt

diff --git a/Documentation/devicetree/bindings/leds/leds-lm3632.txt b/Documentation/devicetree/bindings/leds/leds-lm3632.txt
new file mode 100644
index 000000000000..1befd17174ac
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-lm3632.txt
@@ -0,0 +1,53 @@
+* Texas Instruments - lm3632 Single-Chip Backlight and 1.5-A Flash LED Driver
+
+The LM3632A integrates LED drivers for both
+the backlight of the LCD panel and the camera flash
+along with the bias power for the LCD panel into one
+device.
+
+Required properties:
+	- compatible : Can be one of the following
+		"ti,lm3632"
+	- reg : I2C slave address
+	- #address-cells : 1
+	- #size-cells : 0
+
+Required child properties:
+	- reg : 0 - Indicates a backlight mode
+		1 - Indicates a Torch/Strobe (white LED) mode
+
+Required properties for flash LED child nodes:
+	See Documentation/devicetree/bindings/leds/common.txt
+	- flash-max-microamp : Range from 100mA - 1.5A
+	- flash-max-timeout-us : Range from 32ms - 1024ms
+	- led-max-microamp : Range from 25mA - 375mA
+
+Optional child properties:
+	- label : see Documentation/devicetree/bindings/leds/common.txt
+	- linux,default-trigger :
+	   see Documentation/devicetree/bindings/leds/common.txt
+
+Example:
+led-controller@11 {
+	compatible = "ti,lm3632";
+	#address-cells = <1>;
+	#size-cells = <0>;
+	reg = <0x11>;
+
+	led@0 {
+		reg = <0>;
+		label = "white:backlight";
+		linux,default-trigger = "backlight";
+	};
+
+	led@1 {
+		reg = <1>;
+		label = "white:torch";
+		led-max-microamp = <375000>;
+		flash-max-microamp = <1500000>;
+		flash-max-timeout-us = <1000000>;
+	};
+}
+
+For more product information please see the links below:
+http://www.ti.com/product/LM3632
-- 
2.19.0


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

* [RFC PATCH 9/9] leds: lm3632: Introduce the TI LM3632 driver
  2018-09-26 13:09 ` Dan Murphy
@ 2018-09-26 13:09   ` Dan Murphy
  -1 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 13:09 UTC (permalink / raw)
  To: robh+dt, jacek.anaszewski, pavel
  Cc: devicetree, linux-kernel, lee.jones, linux-omap, linux-leds, Dan Murphy

Add the dedicated TI LM3632 LED driver.  This
LED device is capable of driving a backlight display.

In addition to the backlight the device has control
of a strobe and torch output.

Data sheet:
http://www.ti.com/lit/ds/symlink/lm3632a.pdf

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
 drivers/leds/Kconfig       |   9 +-
 drivers/leds/Makefile      |   1 +
 drivers/leds/leds-lm3632.c | 547 +++++++++++++++++++++++++++++++++++++
 3 files changed, 556 insertions(+), 1 deletion(-)
 create mode 100644 drivers/leds/leds-lm3632.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 1a78588d9806..7879b9048332 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -764,8 +764,15 @@ config LEDS_LM3697
           This supports the LED device LM3697.
 
 config LEDS_LM3633
-	tristate "LED driver for LM3633"
+	tristate "LED driver for LM3632"
  	depends on LEDS_TI_LMU_COMMON
+	help
+          Say Y to enable the LED driver for TI LMU devices.
+          This supports the LED device LM3632.
+
+config LEDS_LM3632
+	tristate "LED driver for LM3632"
+	depends on LEDS_TI_LMU_COMMON
 	help
           Say Y to enable the LED driver for TI LMU devices.
           This supports the LED device LM3633.
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 6ec006892fc0..53d02d7f1a0b 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -79,6 +79,7 @@ obj-$(CONFIG_LEDS_LM3692X)		+= leds-lm3692x.o
 obj-$(CONFIG_LEDS_SC27XX_BLTC)		+= leds-sc27xx-bltc.o
 obj-$(CONFIG_LEDS_LM3601X)		+= leds-lm3601x.o
 obj-$(CONFIG_LEDS_LM3697)		+= leds-lm3697.o
+obj-$(CONFIG_LEDS_LM3632)		+= leds-lm3632.o
 obj-$(CONFIG_LEDS_LM3633)		+= leds-lm3633.o
 obj-$(CONFIG_LEDS_TI_LMU_COMMON)	+= ti-lmu-led-common.o
 
diff --git a/drivers/leds/leds-lm3632.c b/drivers/leds/leds-lm3632.c
new file mode 100644
index 000000000000..7d487cf423bc
--- /dev/null
+++ b/drivers/leds/leds-lm3632.c
@@ -0,0 +1,547 @@
+// SPDX-License-Identifier: GPL-2.0
+// Flash and torch driver for Texas Instruments LM3632 LED
+// Flash driver chip family
+// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/leds.h>
+#include <linux/led-class-flash.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <uapi/linux/uleds.h>
+
+#include "ti-lmu-led-common.h"
+
+#define LM3632_MODE_BL		0x0
+#define LM3632_MODE_TORCH	0x1
+
+/* Registers */
+#define LM3632_REV_REG		0x01
+#define LM3632_CFG1_REG		0x02
+#define LM3632_CFG2_REG		0x03
+#define LM3633_BL_BRT_LSB	0x04
+#define LM3633_BL_BRT_MSB	0x05
+#define LM3632_FLASH_TORCH_BRT	0x06
+#define LM3632_FLASH_CFG	0x07
+#define LM3632_ENABLE_REG	0x0a
+#define LM3632_FLAGS1_REG	0x0b
+#define LM3632_FLAGS2_REG	0x10
+
+/* Enable Mode bits */
+#define LM3632_BL_EN		BIT(0)
+#define LM3632_STROBE_EN	BIT(1)
+#define LM3632_FLASH_MODE	BIT(2)
+#define LM3632_BLED1_2_EN	BIT(3)
+#define LM3632_BLED1_EN		BIT(4)
+#define LM3632_BL_OVP_EN	BIT(6)
+#define LM3632_SW_RESET		BIT(7)
+
+/* Flags 1 Mask */
+#define LM3632_THERM_SHUTDOWN	BIT(0)
+#define LM3632_FLASH_TIME_OUT	BIT(1)
+#define LM3632_FLED_SHORT_FAULT	BIT(2)
+#define LM3632_VINM_SHORT_FAULT	BIT(4)
+#define LM3632_FOUT_SHORT_FAULT	BIT(5)
+#define LM3632_FLASH_OVP_FAULT	BIT(6)
+#define LM3632_BL_OVP_FAULT	BIT(7)
+
+/* Flags 2 Mask */
+#define LM3632_BL_OCP_FAULT	BIT(0)
+#define LM3632_FLASH_OCP_FAULT	BIT(1)
+#define LM3632_VNEG_SHORT_FAULT	BIT(2)
+#define LM3632_VPOS_SHORT_FAULT	BIT(3)
+#define LM3632_VNEG_OVP_FAULT	BIT(4)
+#define LM3632_LCM_OVP_FAULT	BIT(5)
+
+#define LM3632_TORCH_BRT_SHIFT	4
+#define LM3632_STROBE_REG_DIV	16
+#define LM3632_STROBE_STEP_UA	100000
+
+#define LM3632_MAX_TORCH_I_UA	375000
+#define LM3632_MIN_TORCH_I_UA	2500
+
+#define LM3632_MAX_STROBE_I_UA	1500000
+#define LM3632_MIN_STROBE_I_UA	10000
+
+#define LM3632_TIMEOUT_MASK	0x1e
+#define LM3632_ENABLE_MASK	(LM3632_BL_EN | LM3632_STROBE_EN)
+
+#define LM3632_TIMEOUT_STEP_US	32000
+#define LM3632_MIN_TIMEOUT_US	32000
+#define LM3632_MAX_TIMEOUT_US	1024000
+
+#define LM3632_TORCH_BRT_MASK	0x0f
+#define LM3632_FLASH_BRT_MASK	0xf0
+
+/**
+ * struct lm3632_led -
+ * @fled_cdev: flash LED class device pointer
+ * @led_name - LED label
+ * @led_dev - LED class device
+ * @priv - Pointer to the lm3632 struct
+ * @lmu_data - Register and setting values for common code
+ * @led_name: LED label for the Torch or IR LED
+ * @flash_timeout: the timeout for the flash
+ * @last_flag: last known flags register value
+ * @torch_current_max: maximum current for the torch
+ * @flash_current_max: maximum current for the flash
+ * @max_flash_timeout: maximum timeout for the flash
+ */
+struct lm3632_led {
+	struct led_classdev_flash fled_cdev;
+	char led_name[LED_MAX_NAME_SIZE];
+	struct led_classdev led_dev;
+	struct ti_lmu_bank lmu_data;
+	struct lm3632 *priv;
+
+	unsigned int flash_timeout;
+	unsigned int last_flag;
+
+	u32 torch_current_max;
+	u32 flash_current_max;
+	u32 max_flash_timeout;
+};
+
+/**
+ * struct lm3632 -
+ * @client: Pointer to the I2C client
+ * @regmap: Devices register map
+ * @lock: Lock for reading/writing the device
+ * @leds - Array of LED strings
+ */
+struct lm3632 {
+	struct i2c_client *client;
+	struct regmap *regmap;
+	struct device *dev;
+	struct mutex lock;
+
+	struct lm3632_led leds[];
+};
+
+static const struct reg_default lm3632_regmap_defs[] = {
+	{ LM3632_CFG1_REG, 0x30 },
+	{ LM3632_CFG2_REG, 0x0d },
+	{ LM3632_FLASH_CFG, 0x2f },
+	{ LM3632_ENABLE_REG, 0x00 },
+};
+
+static bool lm3632_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case LM3632_FLAGS1_REG:
+	case LM3632_FLAGS2_REG:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_config lm3632_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = LM3632_FLAGS2_REG,
+	.reg_defaults = lm3632_regmap_defs,
+	.num_reg_defaults = ARRAY_SIZE(lm3632_regmap_defs),
+	.cache_type = REGCACHE_RBTREE,
+	.volatile_reg = lm3632_volatile_reg,
+};
+
+static struct lm3632_led *fled_cdev_to_led(struct led_classdev_flash *fled_cdev)
+{
+	return container_of(fled_cdev, struct lm3632_led, fled_cdev);
+}
+
+static int lm3632_read_faults(struct lm3632_led *led)
+{
+	struct lm3632 *priv = led->priv;
+	int flags_val;
+	int ret;
+
+	ret = regmap_read(priv->regmap, LM3632_FLAGS1_REG, &flags_val);
+	if (ret < 0)
+		return -EIO;
+
+	led->last_flag = 0;
+
+	if (flags_val & LM3632_FLASH_OVP_FAULT)
+		led->last_flag |= LED_FAULT_OVER_VOLTAGE;
+
+	if (flags_val & LM3632_THERM_SHUTDOWN)
+		led->last_flag |= LED_FAULT_OVER_TEMPERATURE;
+
+	if (flags_val & (LM3632_FLED_SHORT_FAULT | LM3632_VINM_SHORT_FAULT |
+	    LM3632_FOUT_SHORT_FAULT))
+		led->last_flag |= LED_FAULT_SHORT_CIRCUIT;
+
+	if (flags_val & LM3632_THERM_SHUTDOWN)
+		led->last_flag |= LED_FAULT_LED_OVER_TEMPERATURE;
+
+	if (flags_val & LM3632_FLASH_TIME_OUT)
+		led->last_flag |= LED_FAULT_TIMEOUT;
+
+	ret = regmap_read(priv->regmap, LM3632_FLAGS2_REG, &flags_val);
+	if (ret < 0)
+		return -EIO;
+
+	if (flags_val & (LM3632_BL_OCP_FAULT | LM3632_FLASH_OCP_FAULT))
+		led->last_flag |= LED_FAULT_OVER_CURRENT;
+
+	if (flags_val & LM3632_FLASH_OCP_FAULT)
+		led->last_flag |= LED_FAULT_OVER_CURRENT;
+
+	return led->last_flag;
+}
+
+static int lm3632_backlight_brightness_set(struct led_classdev *cdev,
+					enum led_brightness brightness)
+{
+	struct lm3632_led *led = container_of(cdev, struct lm3632_led,
+					      led_dev);
+	struct lm3632 *priv = led->priv;
+	int ret;
+
+	mutex_lock(&priv->lock);
+
+	ret = ti_lmu_common_set_brightness(&led->lmu_data, brightness);
+	if (ret)
+		dev_err(&priv->client->dev, "Cannot write brightness\n");
+
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static int lm3632_strobe_flash_brightness_set(struct lm3632_led *led,
+					      u8 brightness_val)
+{
+	struct lm3632 *priv = led->priv;
+	int ret;
+
+	if (brightness_val == LED_OFF) {
+		ret = regmap_update_bits(priv->regmap, LM3632_ENABLE_REG,
+					LM3632_STROBE_EN, LED_OFF);
+		return ret;
+	}
+
+	ret = regmap_update_bits(priv->regmap, LM3632_FLASH_TORCH_BRT,
+				LM3632_TORCH_BRT_MASK, brightness_val);
+	if (ret < 0)
+		return ret;
+
+	return regmap_update_bits(priv->regmap, LM3632_ENABLE_REG,
+				LM3632_STROBE_EN,
+				LM3632_STROBE_EN);
+}
+
+static int lm3632_torch_brightness_set(struct led_classdev *cdev,
+					enum led_brightness brightness)
+{
+	struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(cdev);
+	struct lm3632_led *led = fled_cdev_to_led(fled_cdev);
+	struct lm3632 *priv = led->priv;
+	int ret, brightness_val;
+
+	mutex_lock(&priv->lock);
+
+	ret = lm3632_read_faults(led);
+	if (ret < 0)
+		goto out;
+
+	brightness_val = (brightness / 16);
+
+	ret = lm3632_strobe_flash_brightness_set(led, brightness_val);
+
+out:
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static int lm3632_strobe_set(struct led_classdev_flash *fled_cdev,
+				bool state)
+{
+	struct lm3632_led *led = fled_cdev_to_led(fled_cdev);
+	struct lm3632 *priv = led->priv;
+	int timeout_reg_val;
+	int current_timeout;
+	int ret;
+
+	mutex_lock(&priv->lock);
+
+	ret = regmap_read(priv->regmap, LM3632_FLASH_CFG, &current_timeout);
+	if (ret < 0)
+		goto out;
+
+	if (led->flash_timeout != current_timeout)
+		ret = regmap_update_bits(priv->regmap, LM3632_FLASH_CFG,
+					LM3632_TIMEOUT_MASK, timeout_reg_val);
+
+	if (state)
+		ret = regmap_update_bits(priv->regmap, LM3632_ENABLE_REG,
+					LM3632_STROBE_EN, LM3632_STROBE_EN);
+	else
+		ret = regmap_update_bits(priv->regmap, LM3632_ENABLE_REG,
+					LM3632_STROBE_EN, LED_OFF);
+
+	ret = lm3632_read_faults(led);
+out:
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static int lm3632_flash_brightness_set(struct led_classdev_flash *fled_cdev,
+					u32 brightness)
+{
+	struct lm3632_led *led = fled_cdev_to_led(fled_cdev);
+	struct lm3632 *priv = led->priv;
+	u8 brightness_val;
+	int ret;
+
+	mutex_lock(&priv->lock);
+	ret = lm3632_read_faults(led);
+	if (ret < 0)
+		goto out;
+
+	brightness_val = (brightness / 16) << LM3632_TORCH_BRT_SHIFT;
+
+	ret = lm3632_strobe_flash_brightness_set(led, brightness_val);
+out:
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static int lm3632_flash_timeout_set(struct led_classdev_flash *fled_cdev,
+				u32 timeout)
+{
+	struct lm3632_led *led = fled_cdev_to_led(fled_cdev);
+	struct lm3632 *priv = led->priv;
+
+	mutex_lock(&priv->lock);
+
+	led->flash_timeout = timeout;
+
+	mutex_unlock(&priv->lock);
+
+	return 0;
+}
+
+static int lm3632_strobe_get(struct led_classdev_flash *fled_cdev, bool *state)
+{
+	struct lm3632_led *led = fled_cdev_to_led(fled_cdev);
+	struct lm3632 *priv = led->priv;
+	int strobe_state;
+	int ret;
+
+	mutex_lock(&priv->lock);
+
+	ret = regmap_read(priv->regmap, LM3632_ENABLE_REG, &strobe_state);
+	if (ret < 0)
+		goto out;
+
+	*state = strobe_state & LM3632_STROBE_EN;
+
+out:
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static int lm3632_flash_fault_get(struct led_classdev_flash *fled_cdev,
+				u32 *fault)
+{
+	struct lm3632_led *led = fled_cdev_to_led(fled_cdev);
+
+	lm3632_read_faults(led);
+
+	*fault = led->last_flag;
+
+	return 0;
+}
+
+static const struct led_flash_ops flash_ops = {
+	.flash_brightness_set	= lm3632_flash_brightness_set,
+	.strobe_set		= lm3632_strobe_set,
+	.strobe_get		= lm3632_strobe_get,
+	.timeout_set		= lm3632_flash_timeout_set,
+	.fault_get		= lm3632_flash_fault_get,
+};
+
+static int lm3632_register_strobe_leds(struct lm3632_led *led)
+{
+	struct led_classdev *led_cdev;
+	struct led_flash_setting *setting;
+
+	led->fled_cdev.ops = &flash_ops;
+
+	setting = &led->fled_cdev.timeout;
+	setting->min = LM3632_MIN_TIMEOUT_US;
+	setting->max = led->max_flash_timeout;
+	setting->step = LM3632_TIMEOUT_STEP_US;
+	setting->val = led->max_flash_timeout;
+
+	setting = &led->fled_cdev.brightness;
+	setting->min = LM3632_MIN_STROBE_I_UA;
+	setting->max = led->flash_current_max;
+	setting->step = LM3632_STROBE_STEP_UA;
+	setting->val = led->flash_current_max;
+
+	led_cdev = &led->fled_cdev.led_cdev;
+	led_cdev->name = led->led_name;
+	led_cdev->brightness_set_blocking = lm3632_torch_brightness_set;
+	led_cdev->max_brightness = led->torch_current_max;
+
+	led_cdev->flags |= LED_DEV_CAP_FLASH;
+
+	return led_classdev_flash_register(&led->priv->client->dev, &led->fled_cdev);
+}
+
+static int lm3632_parse_node(struct lm3632 *priv)
+{
+	struct fwnode_handle *child = NULL;
+	struct lm3632_led *led;
+	int ret = -ENODEV;
+	const char *name;
+	u32 led_mode;
+	int i;
+
+	device_for_each_child_node(priv->dev, child) {
+		ret = fwnode_property_read_u32(child, "reg", &led_mode);
+		if (ret) {
+			dev_err(&priv->client->dev, "reg DT property missing\n");
+			goto out_err;
+		}
+
+		if (led_mode < LM3632_MODE_TORCH ||
+		    led_mode > LM3632_MODE_BL) {
+			dev_warn(&priv->client->dev, "Invalid led mode requested\n");
+			ret = -EINVAL;
+			goto out_err;
+		}
+
+		led = &priv->leds[i];
+		led->priv = priv;
+
+		ret = fwnode_property_read_string(child, "label", &name);
+		if (ret) {
+			if (led_mode == LM3632_MODE_TORCH)
+				name = "torch";
+			else
+				name = "backlight";
+		}
+
+		snprintf(led->led_name, sizeof(led->led_name),
+			"%s:%s", priv->client->name, name);
+
+		if (led_mode == LM3632_MODE_TORCH) {
+			ret = fwnode_property_read_u32(child, "led-max-microamp",
+							&led->torch_current_max);
+			if (ret) {
+				dev_warn(&priv->client->dev,
+					"led-max-microamp DT property missing\n");
+				goto out_err;
+			}
+
+			ret = fwnode_property_read_u32(child, "flash-max-microamp",
+						&led->flash_current_max);
+			if (ret) {
+				dev_warn(&priv->client->dev,
+					 "flash-max-microamp DT property missing\n");
+				goto out_err;
+			}
+
+			ret = fwnode_property_read_u32(child, "flash-max-timeout-us",
+						&led->max_flash_timeout);
+			if (ret) {
+				dev_warn(&priv->client->dev,
+					 "flash-max-timeout-us DT property missing\n");
+				goto out_err;
+			}
+
+			ret = lm3632_register_strobe_leds(led);
+			if (ret) {
+				dev_warn(&priv->client->dev,
+					 "Failed to register flash LEDs\n");
+				goto out_err;
+			}
+
+		} else {
+			led->led_dev.name = led->led_name;
+			led->led_dev.brightness_set_blocking = lm3632_backlight_brightness_set;
+			led->lmu_data.regmap = priv->regmap;
+			led->lmu_data.max_brightness = MAX_BRIGHTNESS_11BIT;
+			led->lmu_data.lsb_brightness_reg = LM3633_BL_BRT_LSB;
+			led->lmu_data.msb_brightness_reg = LM3633_BL_BRT_MSB;
+
+			ret = devm_led_classdev_register(priv->dev, &led->led_dev);
+		}
+
+		i++;
+	}
+out_err:
+	fwnode_handle_put(child);
+	return ret;
+}
+
+static int lm3632_probe(struct i2c_client *client)
+{
+	struct lm3632 *led;
+	int ret;
+
+	led = devm_kzalloc(&client->dev, sizeof(*led), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	led->client = client;
+	led->dev = &client->dev;
+	i2c_set_clientdata(client, led);
+
+	led->regmap = devm_regmap_init_i2c(client, &lm3632_regmap);
+	if (IS_ERR(led->regmap)) {
+		ret = PTR_ERR(led->regmap);
+		dev_err(&client->dev,
+			"Failed to allocate register map: %d\n", ret);
+		return ret;
+	}
+
+	mutex_init(&led->lock);
+
+	return lm3632_parse_node(led);
+}
+
+static int lm3632_remove(struct i2c_client *client)
+{
+	struct lm3632 *led = i2c_get_clientdata(client);
+
+/*	led_classdev_flash_unregister(&led->fled_cdev);*/
+	mutex_destroy(&led->lock);
+
+	return regmap_update_bits(led->regmap, LM3632_ENABLE_REG,
+			   LM3632_ENABLE_MASK, 0x00);
+}
+
+static const struct i2c_device_id lm3632_id[] = {
+	{ "LM3632", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, lm3632_id);
+
+static const struct of_device_id of_lm3632_leds_match[] = {
+	{ .compatible = "ti,lm3632", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, of_lm3632_leds_match);
+
+static struct i2c_driver lm3632_i2c_driver = {
+	.driver = {
+		.name = "lm3632",
+		.of_match_table = of_lm3632_leds_match,
+	},
+	.probe_new = lm3632_probe,
+	.remove = lm3632_remove,
+	.id_table = lm3632_id,
+};
+module_i2c_driver(lm3632_i2c_driver);
+
+MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM3632");
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.19.0

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

* [RFC PATCH 9/9] leds: lm3632: Introduce the TI LM3632 driver
@ 2018-09-26 13:09   ` Dan Murphy
  0 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 13:09 UTC (permalink / raw)
  To: robh+dt, jacek.anaszewski, pavel
  Cc: devicetree, linux-kernel, lee.jones, linux-omap, linux-leds, Dan Murphy

Add the dedicated TI LM3632 LED driver.  This
LED device is capable of driving a backlight display.

In addition to the backlight the device has control
of a strobe and torch output.

Data sheet:
http://www.ti.com/lit/ds/symlink/lm3632a.pdf

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
 drivers/leds/Kconfig       |   9 +-
 drivers/leds/Makefile      |   1 +
 drivers/leds/leds-lm3632.c | 547 +++++++++++++++++++++++++++++++++++++
 3 files changed, 556 insertions(+), 1 deletion(-)
 create mode 100644 drivers/leds/leds-lm3632.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 1a78588d9806..7879b9048332 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -764,8 +764,15 @@ config LEDS_LM3697
           This supports the LED device LM3697.
 
 config LEDS_LM3633
-	tristate "LED driver for LM3633"
+	tristate "LED driver for LM3632"
  	depends on LEDS_TI_LMU_COMMON
+	help
+          Say Y to enable the LED driver for TI LMU devices.
+          This supports the LED device LM3632.
+
+config LEDS_LM3632
+	tristate "LED driver for LM3632"
+	depends on LEDS_TI_LMU_COMMON
 	help
           Say Y to enable the LED driver for TI LMU devices.
           This supports the LED device LM3633.
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 6ec006892fc0..53d02d7f1a0b 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -79,6 +79,7 @@ obj-$(CONFIG_LEDS_LM3692X)		+= leds-lm3692x.o
 obj-$(CONFIG_LEDS_SC27XX_BLTC)		+= leds-sc27xx-bltc.o
 obj-$(CONFIG_LEDS_LM3601X)		+= leds-lm3601x.o
 obj-$(CONFIG_LEDS_LM3697)		+= leds-lm3697.o
+obj-$(CONFIG_LEDS_LM3632)		+= leds-lm3632.o
 obj-$(CONFIG_LEDS_LM3633)		+= leds-lm3633.o
 obj-$(CONFIG_LEDS_TI_LMU_COMMON)	+= ti-lmu-led-common.o
 
diff --git a/drivers/leds/leds-lm3632.c b/drivers/leds/leds-lm3632.c
new file mode 100644
index 000000000000..7d487cf423bc
--- /dev/null
+++ b/drivers/leds/leds-lm3632.c
@@ -0,0 +1,547 @@
+// SPDX-License-Identifier: GPL-2.0
+// Flash and torch driver for Texas Instruments LM3632 LED
+// Flash driver chip family
+// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/leds.h>
+#include <linux/led-class-flash.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <uapi/linux/uleds.h>
+
+#include "ti-lmu-led-common.h"
+
+#define LM3632_MODE_BL		0x0
+#define LM3632_MODE_TORCH	0x1
+
+/* Registers */
+#define LM3632_REV_REG		0x01
+#define LM3632_CFG1_REG		0x02
+#define LM3632_CFG2_REG		0x03
+#define LM3633_BL_BRT_LSB	0x04
+#define LM3633_BL_BRT_MSB	0x05
+#define LM3632_FLASH_TORCH_BRT	0x06
+#define LM3632_FLASH_CFG	0x07
+#define LM3632_ENABLE_REG	0x0a
+#define LM3632_FLAGS1_REG	0x0b
+#define LM3632_FLAGS2_REG	0x10
+
+/* Enable Mode bits */
+#define LM3632_BL_EN		BIT(0)
+#define LM3632_STROBE_EN	BIT(1)
+#define LM3632_FLASH_MODE	BIT(2)
+#define LM3632_BLED1_2_EN	BIT(3)
+#define LM3632_BLED1_EN		BIT(4)
+#define LM3632_BL_OVP_EN	BIT(6)
+#define LM3632_SW_RESET		BIT(7)
+
+/* Flags 1 Mask */
+#define LM3632_THERM_SHUTDOWN	BIT(0)
+#define LM3632_FLASH_TIME_OUT	BIT(1)
+#define LM3632_FLED_SHORT_FAULT	BIT(2)
+#define LM3632_VINM_SHORT_FAULT	BIT(4)
+#define LM3632_FOUT_SHORT_FAULT	BIT(5)
+#define LM3632_FLASH_OVP_FAULT	BIT(6)
+#define LM3632_BL_OVP_FAULT	BIT(7)
+
+/* Flags 2 Mask */
+#define LM3632_BL_OCP_FAULT	BIT(0)
+#define LM3632_FLASH_OCP_FAULT	BIT(1)
+#define LM3632_VNEG_SHORT_FAULT	BIT(2)
+#define LM3632_VPOS_SHORT_FAULT	BIT(3)
+#define LM3632_VNEG_OVP_FAULT	BIT(4)
+#define LM3632_LCM_OVP_FAULT	BIT(5)
+
+#define LM3632_TORCH_BRT_SHIFT	4
+#define LM3632_STROBE_REG_DIV	16
+#define LM3632_STROBE_STEP_UA	100000
+
+#define LM3632_MAX_TORCH_I_UA	375000
+#define LM3632_MIN_TORCH_I_UA	2500
+
+#define LM3632_MAX_STROBE_I_UA	1500000
+#define LM3632_MIN_STROBE_I_UA	10000
+
+#define LM3632_TIMEOUT_MASK	0x1e
+#define LM3632_ENABLE_MASK	(LM3632_BL_EN | LM3632_STROBE_EN)
+
+#define LM3632_TIMEOUT_STEP_US	32000
+#define LM3632_MIN_TIMEOUT_US	32000
+#define LM3632_MAX_TIMEOUT_US	1024000
+
+#define LM3632_TORCH_BRT_MASK	0x0f
+#define LM3632_FLASH_BRT_MASK	0xf0
+
+/**
+ * struct lm3632_led -
+ * @fled_cdev: flash LED class device pointer
+ * @led_name - LED label
+ * @led_dev - LED class device
+ * @priv - Pointer to the lm3632 struct
+ * @lmu_data - Register and setting values for common code
+ * @led_name: LED label for the Torch or IR LED
+ * @flash_timeout: the timeout for the flash
+ * @last_flag: last known flags register value
+ * @torch_current_max: maximum current for the torch
+ * @flash_current_max: maximum current for the flash
+ * @max_flash_timeout: maximum timeout for the flash
+ */
+struct lm3632_led {
+	struct led_classdev_flash fled_cdev;
+	char led_name[LED_MAX_NAME_SIZE];
+	struct led_classdev led_dev;
+	struct ti_lmu_bank lmu_data;
+	struct lm3632 *priv;
+
+	unsigned int flash_timeout;
+	unsigned int last_flag;
+
+	u32 torch_current_max;
+	u32 flash_current_max;
+	u32 max_flash_timeout;
+};
+
+/**
+ * struct lm3632 -
+ * @client: Pointer to the I2C client
+ * @regmap: Devices register map
+ * @lock: Lock for reading/writing the device
+ * @leds - Array of LED strings
+ */
+struct lm3632 {
+	struct i2c_client *client;
+	struct regmap *regmap;
+	struct device *dev;
+	struct mutex lock;
+
+	struct lm3632_led leds[];
+};
+
+static const struct reg_default lm3632_regmap_defs[] = {
+	{ LM3632_CFG1_REG, 0x30 },
+	{ LM3632_CFG2_REG, 0x0d },
+	{ LM3632_FLASH_CFG, 0x2f },
+	{ LM3632_ENABLE_REG, 0x00 },
+};
+
+static bool lm3632_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case LM3632_FLAGS1_REG:
+	case LM3632_FLAGS2_REG:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_config lm3632_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = LM3632_FLAGS2_REG,
+	.reg_defaults = lm3632_regmap_defs,
+	.num_reg_defaults = ARRAY_SIZE(lm3632_regmap_defs),
+	.cache_type = REGCACHE_RBTREE,
+	.volatile_reg = lm3632_volatile_reg,
+};
+
+static struct lm3632_led *fled_cdev_to_led(struct led_classdev_flash *fled_cdev)
+{
+	return container_of(fled_cdev, struct lm3632_led, fled_cdev);
+}
+
+static int lm3632_read_faults(struct lm3632_led *led)
+{
+	struct lm3632 *priv = led->priv;
+	int flags_val;
+	int ret;
+
+	ret = regmap_read(priv->regmap, LM3632_FLAGS1_REG, &flags_val);
+	if (ret < 0)
+		return -EIO;
+
+	led->last_flag = 0;
+
+	if (flags_val & LM3632_FLASH_OVP_FAULT)
+		led->last_flag |= LED_FAULT_OVER_VOLTAGE;
+
+	if (flags_val & LM3632_THERM_SHUTDOWN)
+		led->last_flag |= LED_FAULT_OVER_TEMPERATURE;
+
+	if (flags_val & (LM3632_FLED_SHORT_FAULT | LM3632_VINM_SHORT_FAULT |
+	    LM3632_FOUT_SHORT_FAULT))
+		led->last_flag |= LED_FAULT_SHORT_CIRCUIT;
+
+	if (flags_val & LM3632_THERM_SHUTDOWN)
+		led->last_flag |= LED_FAULT_LED_OVER_TEMPERATURE;
+
+	if (flags_val & LM3632_FLASH_TIME_OUT)
+		led->last_flag |= LED_FAULT_TIMEOUT;
+
+	ret = regmap_read(priv->regmap, LM3632_FLAGS2_REG, &flags_val);
+	if (ret < 0)
+		return -EIO;
+
+	if (flags_val & (LM3632_BL_OCP_FAULT | LM3632_FLASH_OCP_FAULT))
+		led->last_flag |= LED_FAULT_OVER_CURRENT;
+
+	if (flags_val & LM3632_FLASH_OCP_FAULT)
+		led->last_flag |= LED_FAULT_OVER_CURRENT;
+
+	return led->last_flag;
+}
+
+static int lm3632_backlight_brightness_set(struct led_classdev *cdev,
+					enum led_brightness brightness)
+{
+	struct lm3632_led *led = container_of(cdev, struct lm3632_led,
+					      led_dev);
+	struct lm3632 *priv = led->priv;
+	int ret;
+
+	mutex_lock(&priv->lock);
+
+	ret = ti_lmu_common_set_brightness(&led->lmu_data, brightness);
+	if (ret)
+		dev_err(&priv->client->dev, "Cannot write brightness\n");
+
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static int lm3632_strobe_flash_brightness_set(struct lm3632_led *led,
+					      u8 brightness_val)
+{
+	struct lm3632 *priv = led->priv;
+	int ret;
+
+	if (brightness_val == LED_OFF) {
+		ret = regmap_update_bits(priv->regmap, LM3632_ENABLE_REG,
+					LM3632_STROBE_EN, LED_OFF);
+		return ret;
+	}
+
+	ret = regmap_update_bits(priv->regmap, LM3632_FLASH_TORCH_BRT,
+				LM3632_TORCH_BRT_MASK, brightness_val);
+	if (ret < 0)
+		return ret;
+
+	return regmap_update_bits(priv->regmap, LM3632_ENABLE_REG,
+				LM3632_STROBE_EN,
+				LM3632_STROBE_EN);
+}
+
+static int lm3632_torch_brightness_set(struct led_classdev *cdev,
+					enum led_brightness brightness)
+{
+	struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(cdev);
+	struct lm3632_led *led = fled_cdev_to_led(fled_cdev);
+	struct lm3632 *priv = led->priv;
+	int ret, brightness_val;
+
+	mutex_lock(&priv->lock);
+
+	ret = lm3632_read_faults(led);
+	if (ret < 0)
+		goto out;
+
+	brightness_val = (brightness / 16);
+
+	ret = lm3632_strobe_flash_brightness_set(led, brightness_val);
+
+out:
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static int lm3632_strobe_set(struct led_classdev_flash *fled_cdev,
+				bool state)
+{
+	struct lm3632_led *led = fled_cdev_to_led(fled_cdev);
+	struct lm3632 *priv = led->priv;
+	int timeout_reg_val;
+	int current_timeout;
+	int ret;
+
+	mutex_lock(&priv->lock);
+
+	ret = regmap_read(priv->regmap, LM3632_FLASH_CFG, &current_timeout);
+	if (ret < 0)
+		goto out;
+
+	if (led->flash_timeout != current_timeout)
+		ret = regmap_update_bits(priv->regmap, LM3632_FLASH_CFG,
+					LM3632_TIMEOUT_MASK, timeout_reg_val);
+
+	if (state)
+		ret = regmap_update_bits(priv->regmap, LM3632_ENABLE_REG,
+					LM3632_STROBE_EN, LM3632_STROBE_EN);
+	else
+		ret = regmap_update_bits(priv->regmap, LM3632_ENABLE_REG,
+					LM3632_STROBE_EN, LED_OFF);
+
+	ret = lm3632_read_faults(led);
+out:
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static int lm3632_flash_brightness_set(struct led_classdev_flash *fled_cdev,
+					u32 brightness)
+{
+	struct lm3632_led *led = fled_cdev_to_led(fled_cdev);
+	struct lm3632 *priv = led->priv;
+	u8 brightness_val;
+	int ret;
+
+	mutex_lock(&priv->lock);
+	ret = lm3632_read_faults(led);
+	if (ret < 0)
+		goto out;
+
+	brightness_val = (brightness / 16) << LM3632_TORCH_BRT_SHIFT;
+
+	ret = lm3632_strobe_flash_brightness_set(led, brightness_val);
+out:
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static int lm3632_flash_timeout_set(struct led_classdev_flash *fled_cdev,
+				u32 timeout)
+{
+	struct lm3632_led *led = fled_cdev_to_led(fled_cdev);
+	struct lm3632 *priv = led->priv;
+
+	mutex_lock(&priv->lock);
+
+	led->flash_timeout = timeout;
+
+	mutex_unlock(&priv->lock);
+
+	return 0;
+}
+
+static int lm3632_strobe_get(struct led_classdev_flash *fled_cdev, bool *state)
+{
+	struct lm3632_led *led = fled_cdev_to_led(fled_cdev);
+	struct lm3632 *priv = led->priv;
+	int strobe_state;
+	int ret;
+
+	mutex_lock(&priv->lock);
+
+	ret = regmap_read(priv->regmap, LM3632_ENABLE_REG, &strobe_state);
+	if (ret < 0)
+		goto out;
+
+	*state = strobe_state & LM3632_STROBE_EN;
+
+out:
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static int lm3632_flash_fault_get(struct led_classdev_flash *fled_cdev,
+				u32 *fault)
+{
+	struct lm3632_led *led = fled_cdev_to_led(fled_cdev);
+
+	lm3632_read_faults(led);
+
+	*fault = led->last_flag;
+
+	return 0;
+}
+
+static const struct led_flash_ops flash_ops = {
+	.flash_brightness_set	= lm3632_flash_brightness_set,
+	.strobe_set		= lm3632_strobe_set,
+	.strobe_get		= lm3632_strobe_get,
+	.timeout_set		= lm3632_flash_timeout_set,
+	.fault_get		= lm3632_flash_fault_get,
+};
+
+static int lm3632_register_strobe_leds(struct lm3632_led *led)
+{
+	struct led_classdev *led_cdev;
+	struct led_flash_setting *setting;
+
+	led->fled_cdev.ops = &flash_ops;
+
+	setting = &led->fled_cdev.timeout;
+	setting->min = LM3632_MIN_TIMEOUT_US;
+	setting->max = led->max_flash_timeout;
+	setting->step = LM3632_TIMEOUT_STEP_US;
+	setting->val = led->max_flash_timeout;
+
+	setting = &led->fled_cdev.brightness;
+	setting->min = LM3632_MIN_STROBE_I_UA;
+	setting->max = led->flash_current_max;
+	setting->step = LM3632_STROBE_STEP_UA;
+	setting->val = led->flash_current_max;
+
+	led_cdev = &led->fled_cdev.led_cdev;
+	led_cdev->name = led->led_name;
+	led_cdev->brightness_set_blocking = lm3632_torch_brightness_set;
+	led_cdev->max_brightness = led->torch_current_max;
+
+	led_cdev->flags |= LED_DEV_CAP_FLASH;
+
+	return led_classdev_flash_register(&led->priv->client->dev, &led->fled_cdev);
+}
+
+static int lm3632_parse_node(struct lm3632 *priv)
+{
+	struct fwnode_handle *child = NULL;
+	struct lm3632_led *led;
+	int ret = -ENODEV;
+	const char *name;
+	u32 led_mode;
+	int i;
+
+	device_for_each_child_node(priv->dev, child) {
+		ret = fwnode_property_read_u32(child, "reg", &led_mode);
+		if (ret) {
+			dev_err(&priv->client->dev, "reg DT property missing\n");
+			goto out_err;
+		}
+
+		if (led_mode < LM3632_MODE_TORCH ||
+		    led_mode > LM3632_MODE_BL) {
+			dev_warn(&priv->client->dev, "Invalid led mode requested\n");
+			ret = -EINVAL;
+			goto out_err;
+		}
+
+		led = &priv->leds[i];
+		led->priv = priv;
+
+		ret = fwnode_property_read_string(child, "label", &name);
+		if (ret) {
+			if (led_mode == LM3632_MODE_TORCH)
+				name = "torch";
+			else
+				name = "backlight";
+		}
+
+		snprintf(led->led_name, sizeof(led->led_name),
+			"%s:%s", priv->client->name, name);
+
+		if (led_mode == LM3632_MODE_TORCH) {
+			ret = fwnode_property_read_u32(child, "led-max-microamp",
+							&led->torch_current_max);
+			if (ret) {
+				dev_warn(&priv->client->dev,
+					"led-max-microamp DT property missing\n");
+				goto out_err;
+			}
+
+			ret = fwnode_property_read_u32(child, "flash-max-microamp",
+						&led->flash_current_max);
+			if (ret) {
+				dev_warn(&priv->client->dev,
+					 "flash-max-microamp DT property missing\n");
+				goto out_err;
+			}
+
+			ret = fwnode_property_read_u32(child, "flash-max-timeout-us",
+						&led->max_flash_timeout);
+			if (ret) {
+				dev_warn(&priv->client->dev,
+					 "flash-max-timeout-us DT property missing\n");
+				goto out_err;
+			}
+
+			ret = lm3632_register_strobe_leds(led);
+			if (ret) {
+				dev_warn(&priv->client->dev,
+					 "Failed to register flash LEDs\n");
+				goto out_err;
+			}
+
+		} else {
+			led->led_dev.name = led->led_name;
+			led->led_dev.brightness_set_blocking = lm3632_backlight_brightness_set;
+			led->lmu_data.regmap = priv->regmap;
+			led->lmu_data.max_brightness = MAX_BRIGHTNESS_11BIT;
+			led->lmu_data.lsb_brightness_reg = LM3633_BL_BRT_LSB;
+			led->lmu_data.msb_brightness_reg = LM3633_BL_BRT_MSB;
+
+			ret = devm_led_classdev_register(priv->dev, &led->led_dev);
+		}
+
+		i++;
+	}
+out_err:
+	fwnode_handle_put(child);
+	return ret;
+}
+
+static int lm3632_probe(struct i2c_client *client)
+{
+	struct lm3632 *led;
+	int ret;
+
+	led = devm_kzalloc(&client->dev, sizeof(*led), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	led->client = client;
+	led->dev = &client->dev;
+	i2c_set_clientdata(client, led);
+
+	led->regmap = devm_regmap_init_i2c(client, &lm3632_regmap);
+	if (IS_ERR(led->regmap)) {
+		ret = PTR_ERR(led->regmap);
+		dev_err(&client->dev,
+			"Failed to allocate register map: %d\n", ret);
+		return ret;
+	}
+
+	mutex_init(&led->lock);
+
+	return lm3632_parse_node(led);
+}
+
+static int lm3632_remove(struct i2c_client *client)
+{
+	struct lm3632 *led = i2c_get_clientdata(client);
+
+/*	led_classdev_flash_unregister(&led->fled_cdev);*/
+	mutex_destroy(&led->lock);
+
+	return regmap_update_bits(led->regmap, LM3632_ENABLE_REG,
+			   LM3632_ENABLE_MASK, 0x00);
+}
+
+static const struct i2c_device_id lm3632_id[] = {
+	{ "LM3632", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, lm3632_id);
+
+static const struct of_device_id of_lm3632_leds_match[] = {
+	{ .compatible = "ti,lm3632", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, of_lm3632_leds_match);
+
+static struct i2c_driver lm3632_i2c_driver = {
+	.driver = {
+		.name = "lm3632",
+		.of_match_table = of_lm3632_leds_match,
+	},
+	.probe_new = lm3632_probe,
+	.remove = lm3632_remove,
+	.id_table = lm3632_id,
+};
+module_i2c_driver(lm3632_i2c_driver);
+
+MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM3632");
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.19.0


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

* Re: [RFC PATCH 1/9] leds: add TI LMU backlight driver
  2018-09-26 13:09   ` Dan Murphy
  (?)
@ 2018-09-26 15:01   ` Tony Lindgren
  2018-09-26 15:30       ` Dan Murphy
  -1 siblings, 1 reply; 32+ messages in thread
From: Tony Lindgren @ 2018-09-26 15:01 UTC (permalink / raw)
  To: Dan Murphy
  Cc: robh+dt, jacek.anaszewski, pavel, devicetree, linux-kernel,
	lee.jones, linux-omap, linux-leds, Milo Kim, Sebastian Reichel

* Dan Murphy <dmurphy@ti.com> [180926 13:14]:
> --- /dev/null
> +++ b/drivers/leds/ti-lmu-led-common.c
> +static int ti_lmu_common_enable(struct ti_lmu_bank *lmu_bank, bool enable)
> +{
> +	struct regmap *regmap = lmu_bank->regmap;
> +	unsigned long enable_time = lmu_bank->enable_usec;
> +	u8 reg = lmu_bank->enable_reg;
> +	u8 mask = BIT(lmu_bank->bank_id);
> +	u8 val = (enable == true) ? mask : 0;
> +	int ret;
> +
> +	return 0;

Hmm this early return probably needs to be left out on real hardawre?

> +	if (!reg)
> +		return -EINVAL;
> +
> +	ret = regmap_update_bits(regmap, reg, mask, val);
> +	if (ret)
> +		return ret;
> +
> +	if (enable_time > 0)
> +		usleep_range(enable_time, enable_time + 100);
> +
> +	return 0;
> +}

Regards,

Tony

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

* Re: [RFC PATCH 1/9] leds: add TI LMU backlight driver
  2018-09-26 15:01   ` Tony Lindgren
@ 2018-09-26 15:30       ` Dan Murphy
  0 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 15:30 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: robh+dt, jacek.anaszewski, pavel, devicetree, linux-kernel,
	lee.jones, linux-omap, linux-leds, Sebastian Reichel

Tony

On 09/26/2018 10:01 AM, Tony Lindgren wrote:
> * Dan Murphy <dmurphy@ti.com> [180926 13:14]:
>> --- /dev/null
>> +++ b/drivers/leds/ti-lmu-led-common.c
>> +static int ti_lmu_common_enable(struct ti_lmu_bank *lmu_bank, bool enable)
>> +{
>> +	struct regmap *regmap = lmu_bank->regmap;
>> +	unsigned long enable_time = lmu_bank->enable_usec;
>> +	u8 reg = lmu_bank->enable_reg;
>> +	u8 mask = BIT(lmu_bank->bank_id);
>> +	u8 val = (enable == true) ? mask : 0;
>> +	int ret;
>> +
>> +	return 0;
> 
> Hmm this early return probably needs to be left out on real hardawre?
> 

Yes it does.  I have not debugged or completed this function yet.

>> +	if (!reg)
>> +		return -EINVAL;
>> +
>> +	ret = regmap_update_bits(regmap, reg, mask, val);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (enable_time > 0)
>> +		usleep_range(enable_time, enable_time + 100);
>> +
>> +	return 0;
>> +}
> 
> Regards,
> 
> Tony
> 


-- 
------------------
Dan Murphy

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

* Re: [RFC PATCH 1/9] leds: add TI LMU backlight driver
@ 2018-09-26 15:30       ` Dan Murphy
  0 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-26 15:30 UTC (permalink / raw)
  To: Tony Lindgren
  Cc: robh+dt, jacek.anaszewski, pavel, devicetree, linux-kernel,
	lee.jones, linux-omap, linux-leds, Sebastian Reichel

Tony

On 09/26/2018 10:01 AM, Tony Lindgren wrote:
> * Dan Murphy <dmurphy@ti.com> [180926 13:14]:
>> --- /dev/null
>> +++ b/drivers/leds/ti-lmu-led-common.c
>> +static int ti_lmu_common_enable(struct ti_lmu_bank *lmu_bank, bool enable)
>> +{
>> +	struct regmap *regmap = lmu_bank->regmap;
>> +	unsigned long enable_time = lmu_bank->enable_usec;
>> +	u8 reg = lmu_bank->enable_reg;
>> +	u8 mask = BIT(lmu_bank->bank_id);
>> +	u8 val = (enable == true) ? mask : 0;
>> +	int ret;
>> +
>> +	return 0;
> 
> Hmm this early return probably needs to be left out on real hardawre?
> 

Yes it does.  I have not debugged or completed this function yet.

>> +	if (!reg)
>> +		return -EINVAL;
>> +
>> +	ret = regmap_update_bits(regmap, reg, mask, val);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (enable_time > 0)
>> +		usleep_range(enable_time, enable_time + 100);
>> +
>> +	return 0;
>> +}
> 
> Regards,
> 
> Tony
> 


-- 
------------------
Dan Murphy

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

* Re: [RFC PATCH 7/9] leds: lm3633: Introduce the lm3633 driver
  2018-09-26 13:09   ` Dan Murphy
  (?)
@ 2018-09-26 22:36   ` Pavel Machek
  2018-09-27 12:04       ` Dan Murphy
  -1 siblings, 1 reply; 32+ messages in thread
From: Pavel Machek @ 2018-09-26 22:36 UTC (permalink / raw)
  To: Dan Murphy
  Cc: robh+dt, jacek.anaszewski, devicetree, linux-kernel, lee.jones,
	linux-omap, linux-leds

[-- Attachment #1: Type: text/plain, Size: 10009 bytes --]

Hi!

> Introduce the LED LM3633 driver.  This LED
> driver has 9 total LED outputs with runtime
> internal ramp configurations.
> 
> Data sheet:
> http://www.ti.com/lit/ds/symlink/lm3633.pdf
> 
> Signed-off-by: Dan Murphy <dmurphy@ti.com>

I did some editing... and this code looks very similar to 3697 code.

> diff --git a/drivers/leds/leds-lm3633.c b/drivers/leds/leds-lm3633.c
> new file mode 100644
> index 000000000000..3d29b0ed67d3
> --- /dev/null
> +++ b/drivers/leds/leds-lm3633.c
> @@ -0,0 +1,430 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// TI LM3633 LED chip family driver
> +// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
> +
> +#include <linux/gpio/consumer.h>
> +#include <linux/i2c.h>
> +#include <linux/init.h>
> +#include <linux/leds.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/slab.h>
> +#include <uapi/linux/uleds.h>
> +
> +#include "ti-lmu-led-common.h"

You might want to move includes to ti-lmu-led-common.h, so these are
not repeated?

> +/**
> + * struct lm3633 -
> + * @enable_gpio - Hardware enable gpio
> + * @regulator - LED supply regulator pointer
> + * @client - Pointer to the I2C client
> + * @regmap - Devices register map
> + * @dev - Pointer to the devices device struct
> + * @lock - Lock for reading/writing the device
> + * @leds - Array of LED strings
> + */
> +struct lm3633 {
> +	struct gpio_desc *enable_gpio;
> +	struct regulator *regulator;
> +	struct i2c_client *client;
> +	struct regmap *regmap;
> +	struct device *dev;
> +	struct mutex lock;
> +	struct lm3633_led leds[];
> +};

Exactly same structure is used for 3697. Worth sharing?

> +static const struct regmap_config lm3633_regmap_config = {
> +	.reg_bits = 8,
> +	.val_bits = 8,
> +
> +	.max_register = LM3633_CTRL_ENABLE,
> +	.reg_defaults = lm3633_reg_defs,
> +	.num_reg_defaults = ARRAY_SIZE(lm3633_reg_defs),
> +	.cache_type = REGCACHE_FLAT,
> +};

Same regmap config. Maybe difficult to share.

> +static int lm3633_brightness_set(struct led_classdev *led_cdev,
> +				enum led_brightness brt_val)
> +{
> +	struct lm3633_led *led = container_of(led_cdev, struct lm3633_led,
> +					      led_dev);
> +	int ctrl_en_val;
> +	int ret;
> +
> +	mutex_lock(&led->priv->lock);
> +
> +	if (led->control_bank == LM3633_CONTROL_A) {
> +		led->lmu_data.msb_brightness_reg = LM3633_CTRL_A_BRT_MSB;
> +		led->lmu_data.lsb_brightness_reg = LM3633_CTRL_A_BRT_LSB;
> +		ctrl_en_val = LM3633_CTRL_A_EN;
> +	} else {
> +		led->lmu_data.msb_brightness_reg = LM3633_CTRL_B_BRT_MSB;
> +		led->lmu_data.lsb_brightness_reg = LM3633_CTRL_B_BRT_LSB;
> +		ctrl_en_val = LM3633_CTRL_B_EN;
> +	}
> +
> +	if (brt_val == LED_OFF)
> +		ret = regmap_update_bits(led->priv->regmap, LM3633_CTRL_ENABLE,
> +					 ctrl_en_val, ~ctrl_en_val);
> +	else
> +		ret = regmap_update_bits(led->priv->regmap, LM3633_CTRL_ENABLE,
> +					 ctrl_en_val, ctrl_en_val);
> +
> +	ret = ti_lmu_common_set_brightness(&led->lmu_data, brt_val);
> +	if (ret)
> +		dev_err(&led->priv->client->dev, "Cannot write brightness\n");
> +
> +	mutex_unlock(&led->priv->lock);
> +	return ret;
> +}

Duplicate of 3697 function with different constants.

> +static int lm3633_set_control_bank(struct lm3633 *priv)
> +{
> +	u8 control_bank_config = 0;
> +	struct lm3633_led *led;
> +	int ret, i;
> +
> +	led = &priv->leds[0];
> +	if (led->control_bank == LM3633_CONTROL_A)
> +		led = &priv->leds[1];
> +
> +	for (i = 0; i < LM3633_MAX_HVLED_STRINGS; i++)
> +		if (led->hvled_strings[i] == LM3633_HVLED_ASSIGNMENT)
> +			control_bank_config |= 1 << i;
> +
> +	ret = regmap_write(priv->regmap, LM3633_HVLED_OUTPUT_CONFIG,
> +			   control_bank_config);
> +	if (ret)
> +		dev_err(&priv->client->dev, "Cannot write OUTPUT config\n");
> +
> +	return ret;
> +}

Duplicate of 3697 function.

> +static int lm3633_init(struct lm3633 *priv)
> +{
> +	struct lm3633_led *led;
> +	int i, ret;
> +
> +	if (priv->enable_gpio) {
> +		gpiod_direction_output(priv->enable_gpio, 1);
> +	} else {
> +		ret = regmap_write(priv->regmap, LM3633_RESET, LM3633_SW_RESET);
> +		if (ret) {
> +			dev_err(&priv->client->dev, "Cannot reset the device\n");
> +			goto out;
> +		}
> +	}
> +
> +	ret = regmap_write(priv->regmap, LM3633_CTRL_ENABLE, 0x0);
> +	if (ret) {
> +		dev_err(&priv->client->dev, "Cannot write ctrl enable\n");
> +		goto out;
> +	}
> +
> +	ret = lm3633_set_control_bank(priv);
> +	if (ret)
> +		dev_err(&priv->client->dev, "Setting the CRTL bank failed\n");
> +
> +	for (i = 0; i < LM3633_MAX_CONTROL_BANKS; i++) {
> +		led = &priv->leds[i];
> +		ti_lmu_common_set_ramp(&led->lmu_data);
> +		if (ret)
> +			dev_err(&priv->client->dev, "Setting the ramp rate failed\n");
> +	}
> +out:
> +	return ret;
> +}

Duplicate of 3697 function.

> +static int lm3633_probe_dt(struct lm3633 *priv)
> +{
> +	struct fwnode_handle *child = NULL;
> +	struct lm3633_led *led;
> +	const char *name;
> +	int control_bank;
> +	size_t i = 0;
> +	int ret;
> +
> +	priv->enable_gpio = devm_gpiod_get_optional(&priv->client->dev,
> +						   "enable", GPIOD_OUT_LOW);
> +	if (IS_ERR(priv->enable_gpio)) {
> +		ret = PTR_ERR(priv->enable_gpio);
> +		dev_err(&priv->client->dev, "Failed to get enable gpio: %d\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	priv->regulator = devm_regulator_get(&priv->client->dev, "vled");
> +	if (IS_ERR(priv->regulator))
> +		priv->regulator = NULL;
> +
> +	device_for_each_child_node(priv->dev, child) {
> +		ret = fwnode_property_read_u32(child, "reg", &control_bank);
> +		if (ret) {
> +			dev_err(&priv->client->dev, "reg property missing\n");
> +			fwnode_handle_put(child);
> +			goto child_out;
> +		}
> +
> +		if (control_bank > LM3633_CONTROL_B) {
> +			dev_err(&priv->client->dev, "reg property is invalid\n");
> +			ret = -EINVAL;
> +			fwnode_handle_put(child);
> +			goto child_out;
> +		}
> +
> +		led = &priv->leds[i];
> +
> +		led->control_bank = control_bank;
> +		led->lmu_data.bank_id = control_bank;
> +		led->lmu_data.enable_reg = LM3633_CTRL_ENABLE;
> +		led->lmu_data.regmap = priv->regmap;
> +		if (control_bank == LM3633_CONTROL_A)
> +			led->lmu_data.runtime_ramp_reg = LM3633_CTRL_A_RAMP;
> +		else
> +			led->lmu_data.runtime_ramp_reg = LM3633_CTRL_B_RAMP;
> +
> +		if (control_bank <= LM3633_CONTROL_B)
> +			ret = fwnode_property_read_u32_array(child, "led-sources",
> +						    led->hvled_strings,
> +						    LM3633_MAX_HVLED_STRINGS);
> +		else
> +			ret = fwnode_property_read_u32_array(child, "led-sources",
> +						    led->lvled_strings,
> +						    LM3633_MAX_LVLED_STRINGS);
> +		if (ret) {
> +			dev_err(&priv->client->dev, "led-sources property missing\n");
> +			fwnode_handle_put(child);
> +			goto child_out;
> +		}
> +
> +		ret = ti_lmu_common_get_ramp_params(&priv->client->dev, child, &led->lmu_data);
> +		if (ret)
> +			dev_warn(&priv->client->dev, "runtime-ramp properties missing\n");
> +
> +		fwnode_property_read_string(child, "linux,default-trigger",
> +					    &led->led_dev.default_trigger);
> +
> +		ret = fwnode_property_read_string(child, "label", &name);
> +		if (ret)
> +			snprintf(led->label, sizeof(led->label),
> +				"%s::", priv->client->name);
> +		else
> +			snprintf(led->label, sizeof(led->label),
> +				 "%s:%s", priv->client->name, name);
> +
> +		led->priv = priv;
> +		led->led_dev.name = led->label;
> +		led->led_dev.brightness_set_blocking = lm3633_brightness_set;
> +
> +		ret = devm_led_classdev_register(priv->dev, &led->led_dev);
> +		if (ret) {
> +			dev_err(&priv->client->dev, "led register err: %d\n",
> +				ret);
> +			fwnode_handle_put(child);
> +			goto child_out;
> +		}
> +
> +		i++;
> +	}
> +
> +child_out:
> +	return ret;
> +}

Very similar, but not quite same.

> +static int lm3633_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)
> +{
> +	struct lm3633 *led;
> +	int count;
> +	int ret;
> +
> +	count = device_get_child_node_count(&client->dev);
> +	if (!count) {
> +		dev_err(&client->dev, "LEDs are not defined in device tree!");
> +		return -ENODEV;
> +	}
> +
> +	led = devm_kzalloc(&client->dev, struct_size(led, leds, count),
> +			   GFP_KERNEL);
> +	if (!led)
> +		return -ENOMEM;
> +
> +	mutex_init(&led->lock);
> +	i2c_set_clientdata(client, led);
> +
> +	led->client = client;
> +	led->dev = &client->dev;
> +	led->regmap = devm_regmap_init_i2c(client, &lm3633_regmap_config);
> +	if (IS_ERR(led->regmap)) {
> +		ret = PTR_ERR(led->regmap);
> +		dev_err(&client->dev, "Failed to allocate register map: %d\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	ret = lm3633_probe_dt(led);
> +	if (ret)
> +		return ret;
> +
> +	return lm3633_init(led);
> +}

Duplicate.

> +static int lm3633_remove(struct i2c_client *client)
> +{
> +	struct lm3633 *led = i2c_get_clientdata(client);
> +	int ret;
> +
> +	ret = regmap_update_bits(led->regmap, LM3633_CTRL_ENABLE,
> +				 LM3633_CTRL_A_B_EN, 0);
> +	if (ret) {
> +		dev_err(&led->client->dev, "Failed to disable the device\n");
> +		return ret;
> +	}
> +
> +	if (led->enable_gpio)
> +		gpiod_direction_output(led->enable_gpio, 0);
> +
> +	if (led->regulator) {
> +		ret = regulator_disable(led->regulator);
> +		if (ret)
> +			dev_err(&led->client->dev,
> +				"Failed to disable regulator\n");
> +	}
> +
> +	mutex_destroy(&led->lock);
> +
> +	return 0;
> +}

Duplicate.

Can we get some more sharing? One way would be to have struct with
all the constants (instead of #defines) use that...?

Thanks,

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

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

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

* Re: [RFC PATCH 7/9] leds: lm3633: Introduce the lm3633 driver
  2018-09-26 22:36   ` Pavel Machek
@ 2018-09-27 12:04       ` Dan Murphy
  0 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-27 12:04 UTC (permalink / raw)
  To: Pavel Machek
  Cc: robh+dt, jacek.anaszewski, devicetree, linux-kernel, lee.jones,
	linux-omap, linux-leds

Pavel

Thanks for the review

On 09/26/2018 05:36 PM, Pavel Machek wrote:
> Hi!
> 
>> Introduce the LED LM3633 driver.  This LED
>> driver has 9 total LED outputs with runtime
>> internal ramp configurations.
>>
>> Data sheet:
>> http://www.ti.com/lit/ds/symlink/lm3633.pdf
>>
>> Signed-off-by: Dan Murphy <dmurphy@ti.com>
> 
> I did some editing... and this code looks very similar to 3697 code.
> 

Ok I will review each one.  Keep in mind these drivers are by no means complete
I anticipate this driver to look different then the 3697 code.

But I do expect a certain level of duplication of code as we see across all drivers.

>> diff --git a/drivers/leds/leds-lm3633.c b/drivers/leds/leds-lm3633.c
>> new file mode 100644
>> index 000000000000..3d29b0ed67d3
>> --- /dev/null
>> +++ b/drivers/leds/leds-lm3633.c
>> @@ -0,0 +1,430 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +// TI LM3633 LED chip family driver
>> +// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
>> +
>> +#include <linux/gpio/consumer.h>
>> +#include <linux/i2c.h>
>> +#include <linux/init.h>
>> +#include <linux/leds.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +#include <linux/of_gpio.h>
>> +#include <linux/regmap.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/slab.h>
>> +#include <uapi/linux/uleds.h>
>> +
>> +#include "ti-lmu-led-common.h"
> 
> You might want to move includes to ti-lmu-led-common.h, so these are
> not repeated?
> 

Sounds good

>> +/**
>> + * struct lm3633 -
>> + * @enable_gpio - Hardware enable gpio
>> + * @regulator - LED supply regulator pointer
>> + * @client - Pointer to the I2C client
>> + * @regmap - Devices register map
>> + * @dev - Pointer to the devices device struct
>> + * @lock - Lock for reading/writing the device
>> + * @leds - Array of LED strings
>> + */
>> +struct lm3633 {
>> +	struct gpio_desc *enable_gpio;
>> +	struct regulator *regulator;
>> +	struct i2c_client *client;
>> +	struct regmap *regmap;
>> +	struct device *dev;
>> +	struct mutex lock;
>> +	struct lm3633_led leds[];
>> +};
> 
> Exactly same structure is used for 3697. Worth sharing?

But it is not the same structure for the 3632.  I anticipate this to change
as I debug the code and add pwm support.

> 
>> +static const struct regmap_config lm3633_regmap_config = {
>> +	.reg_bits = 8,
>> +	.val_bits = 8,
>> +
>> +	.max_register = LM3633_CTRL_ENABLE,
>> +	.reg_defaults = lm3633_reg_defs,
>> +	.num_reg_defaults = ARRAY_SIZE(lm3633_reg_defs),
>> +	.cache_type = REGCACHE_FLAT,
>> +};
> 
> Same regmap config. Maybe difficult to share.

Agreed.

> 
>> +static int lm3633_brightness_set(struct led_classdev *led_cdev,
>> +				enum led_brightness brt_val)
>> +{
>> +	struct lm3633_led *led = container_of(led_cdev, struct lm3633_led,
>> +					      led_dev);
>> +	int ctrl_en_val;
>> +	int ret;
>> +
>> +	mutex_lock(&led->priv->lock);
>> +
>> +	if (led->control_bank == LM3633_CONTROL_A) {
>> +		led->lmu_data.msb_brightness_reg = LM3633_CTRL_A_BRT_MSB;
>> +		led->lmu_data.lsb_brightness_reg = LM3633_CTRL_A_BRT_LSB;
>> +		ctrl_en_val = LM3633_CTRL_A_EN;
>> +	} else {
>> +		led->lmu_data.msb_brightness_reg = LM3633_CTRL_B_BRT_MSB;
>> +		led->lmu_data.lsb_brightness_reg = LM3633_CTRL_B_BRT_LSB;
>> +		ctrl_en_val = LM3633_CTRL_B_EN;
>> +	}
>> +
>> +	if (brt_val == LED_OFF)
>> +		ret = regmap_update_bits(led->priv->regmap, LM3633_CTRL_ENABLE,
>> +					 ctrl_en_val, ~ctrl_en_val);
>> +	else
>> +		ret = regmap_update_bits(led->priv->regmap, LM3633_CTRL_ENABLE,
>> +					 ctrl_en_val, ctrl_en_val);
>> +
>> +	ret = ti_lmu_common_set_brightness(&led->lmu_data, brt_val);
>> +	if (ret)
>> +		dev_err(&led->priv->client->dev, "Cannot write brightness\n");
>> +
>> +	mutex_unlock(&led->priv->lock);
>> +	return ret;
>> +}
> 
> Duplicate of 3697 function with different constants.

This will change to support all the control banks.

> 
>> +static int lm3633_set_control_bank(struct lm3633 *priv)
>> +{
>> +	u8 control_bank_config = 0;
>> +	struct lm3633_led *led;
>> +	int ret, i;
>> +
>> +	led = &priv->leds[0];
>> +	if (led->control_bank == LM3633_CONTROL_A)
>> +		led = &priv->leds[1];
>> +
>> +	for (i = 0; i < LM3633_MAX_HVLED_STRINGS; i++)
>> +		if (led->hvled_strings[i] == LM3633_HVLED_ASSIGNMENT)
>> +			control_bank_config |= 1 << i;
>> +
>> +	ret = regmap_write(priv->regmap, LM3633_HVLED_OUTPUT_CONFIG,
>> +			   control_bank_config);
>> +	if (ret)
>> +		dev_err(&priv->client->dev, "Cannot write OUTPUT config\n");
>> +
>> +	return ret;
>> +}
> 
> Duplicate of 3697 function.

This will change to support all the control banks.

> 
>> +static int lm3633_init(struct lm3633 *priv)
>> +{
>> +	struct lm3633_led *led;
>> +	int i, ret;
>> +
>> +	if (priv->enable_gpio) {
>> +		gpiod_direction_output(priv->enable_gpio, 1);
>> +	} else {
>> +		ret = regmap_write(priv->regmap, LM3633_RESET, LM3633_SW_RESET);
>> +		if (ret) {
>> +			dev_err(&priv->client->dev, "Cannot reset the device\n");
>> +			goto out;
>> +		}
>> +	}
>> +
>> +	ret = regmap_write(priv->regmap, LM3633_CTRL_ENABLE, 0x0);
>> +	if (ret) {
>> +		dev_err(&priv->client->dev, "Cannot write ctrl enable\n");
>> +		goto out;
>> +	}
>> +
>> +	ret = lm3633_set_control_bank(priv);
>> +	if (ret)
>> +		dev_err(&priv->client->dev, "Setting the CRTL bank failed\n");
>> +
>> +	for (i = 0; i < LM3633_MAX_CONTROL_BANKS; i++) {
>> +		led = &priv->leds[i];
>> +		ti_lmu_common_set_ramp(&led->lmu_data);
>> +		if (ret)
>> +			dev_err(&priv->client->dev, "Setting the ramp rate failed\n");
>> +	}
>> +out:
>> +	return ret;
>> +}
> 
> Duplicate of 3697 function.


This will change to support all the control banks.

> 
>> +static int lm3633_probe_dt(struct lm3633 *priv)
>> +{
>> +	struct fwnode_handle *child = NULL;
>> +	struct lm3633_led *led;
>> +	const char *name;
>> +	int control_bank;
>> +	size_t i = 0;
>> +	int ret;
>> +
>> +	priv->enable_gpio = devm_gpiod_get_optional(&priv->client->dev,
>> +						   "enable", GPIOD_OUT_LOW);
>> +	if (IS_ERR(priv->enable_gpio)) {
>> +		ret = PTR_ERR(priv->enable_gpio);
>> +		dev_err(&priv->client->dev, "Failed to get enable gpio: %d\n",
>> +			ret);
>> +		return ret;
>> +	}
>> +
>> +	priv->regulator = devm_regulator_get(&priv->client->dev, "vled");
>> +	if (IS_ERR(priv->regulator))
>> +		priv->regulator = NULL;
>> +
>> +	device_for_each_child_node(priv->dev, child) {
>> +		ret = fwnode_property_read_u32(child, "reg", &control_bank);
>> +		if (ret) {
>> +			dev_err(&priv->client->dev, "reg property missing\n");
>> +			fwnode_handle_put(child);
>> +			goto child_out;
>> +		}
>> +
>> +		if (control_bank > LM3633_CONTROL_B) {
>> +			dev_err(&priv->client->dev, "reg property is invalid\n");
>> +			ret = -EINVAL;
>> +			fwnode_handle_put(child);
>> +			goto child_out;
>> +		}
>> +
>> +		led = &priv->leds[i];
>> +
>> +		led->control_bank = control_bank;
>> +		led->lmu_data.bank_id = control_bank;
>> +		led->lmu_data.enable_reg = LM3633_CTRL_ENABLE;
>> +		led->lmu_data.regmap = priv->regmap;
>> +		if (control_bank == LM3633_CONTROL_A)
>> +			led->lmu_data.runtime_ramp_reg = LM3633_CTRL_A_RAMP;
>> +		else
>> +			led->lmu_data.runtime_ramp_reg = LM3633_CTRL_B_RAMP;
>> +
>> +		if (control_bank <= LM3633_CONTROL_B)
>> +			ret = fwnode_property_read_u32_array(child, "led-sources",
>> +						    led->hvled_strings,
>> +						    LM3633_MAX_HVLED_STRINGS);
>> +		else
>> +			ret = fwnode_property_read_u32_array(child, "led-sources",
>> +						    led->lvled_strings,
>> +						    LM3633_MAX_LVLED_STRINGS);
>> +		if (ret) {
>> +			dev_err(&priv->client->dev, "led-sources property missing\n");
>> +			fwnode_handle_put(child);
>> +			goto child_out;
>> +		}
>> +
>> +		ret = ti_lmu_common_get_ramp_params(&priv->client->dev, child, &led->lmu_data);
>> +		if (ret)
>> +			dev_warn(&priv->client->dev, "runtime-ramp properties missing\n");
>> +
>> +		fwnode_property_read_string(child, "linux,default-trigger",
>> +					    &led->led_dev.default_trigger);
>> +
>> +		ret = fwnode_property_read_string(child, "label", &name);
>> +		if (ret)
>> +			snprintf(led->label, sizeof(led->label),
>> +				"%s::", priv->client->name);
>> +		else
>> +			snprintf(led->label, sizeof(led->label),
>> +				 "%s:%s", priv->client->name, name);
>> +
>> +		led->priv = priv;
>> +		led->led_dev.name = led->label;
>> +		led->led_dev.brightness_set_blocking = lm3633_brightness_set;
>> +
>> +		ret = devm_led_classdev_register(priv->dev, &led->led_dev);
>> +		if (ret) {
>> +			dev_err(&priv->client->dev, "led register err: %d\n",
>> +				ret);
>> +			fwnode_handle_put(child);
>> +			goto child_out;
>> +		}
>> +
>> +		i++;
>> +	}
>> +
>> +child_out:
>> +	return ret;
>> +}
> 
> Very similar, but not quite same.

I know this function will definitely change to support the LVLED control bank setting.
The main difference here is that the HVLED has a simple control bank configuration and the
LVLED is highly configurable with multiple permutations.

As well as maybe the PWM support

> 
>> +static int lm3633_probe(struct i2c_client *client,
>> +			const struct i2c_device_id *id)
>> +{
>> +	struct lm3633 *led;
>> +	int count;
>> +	int ret;
>> +
>> +	count = device_get_child_node_count(&client->dev);
>> +	if (!count) {
>> +		dev_err(&client->dev, "LEDs are not defined in device tree!");
>> +		return -ENODEV;
>> +	}
>> +
>> +	led = devm_kzalloc(&client->dev, struct_size(led, leds, count),
>> +			   GFP_KERNEL);
>> +	if (!led)
>> +		return -ENOMEM;
>> +
>> +	mutex_init(&led->lock);
>> +	i2c_set_clientdata(client, led);
>> +
>> +	led->client = client;
>> +	led->dev = &client->dev;
>> +	led->regmap = devm_regmap_init_i2c(client, &lm3633_regmap_config);
>> +	if (IS_ERR(led->regmap)) {
>> +		ret = PTR_ERR(led->regmap);
>> +		dev_err(&client->dev, "Failed to allocate register map: %d\n",
>> +			ret);
>> +		return ret;
>> +	}
>> +
>> +	ret = lm3633_probe_dt(led);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return lm3633_init(led);
>> +}
> 
> Duplicate.

I do expect the probes to be the same as in most cases.

> 
>> +static int lm3633_remove(struct i2c_client *client)
>> +{
>> +	struct lm3633 *led = i2c_get_clientdata(client);
>> +	int ret;
>> +
>> +	ret = regmap_update_bits(led->regmap, LM3633_CTRL_ENABLE,
>> +				 LM3633_CTRL_A_B_EN, 0);
>> +	if (ret) {
>> +		dev_err(&led->client->dev, "Failed to disable the device\n");
>> +		return ret;
>> +	}
>> +
>> +	if (led->enable_gpio)
>> +		gpiod_direction_output(led->enable_gpio, 0);
>> +
>> +	if (led->regulator) {
>> +		ret = regulator_disable(led->regulator);
>> +		if (ret)
>> +			dev_err(&led->client->dev,
>> +				"Failed to disable regulator\n");
>> +	}
>> +
>> +	mutex_destroy(&led->lock);
>> +
>> +	return 0;
>> +}
> 
> Duplicate.
> 
> Can we get some more sharing? One way would be to have struct with
> all the constants (instead of #defines) use that...?

I will look at the adding common constants to but they should be common across
more then just 2 devices.  As you can see the LM3632 code is quite different
when you add in the flash/torch support.

I am trying to keep the common as light weight as possible and not add code that
cannot be used across multiple devices.

Dan

> 
> Thanks,
> 
> 									Pavel
> 


-- 
------------------
Dan Murphy

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

* Re: [RFC PATCH 7/9] leds: lm3633: Introduce the lm3633 driver
@ 2018-09-27 12:04       ` Dan Murphy
  0 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-27 12:04 UTC (permalink / raw)
  To: Pavel Machek
  Cc: robh+dt, jacek.anaszewski, devicetree, linux-kernel, lee.jones,
	linux-omap, linux-leds

Pavel

Thanks for the review

On 09/26/2018 05:36 PM, Pavel Machek wrote:
> Hi!
> 
>> Introduce the LED LM3633 driver.  This LED
>> driver has 9 total LED outputs with runtime
>> internal ramp configurations.
>>
>> Data sheet:
>> http://www.ti.com/lit/ds/symlink/lm3633.pdf
>>
>> Signed-off-by: Dan Murphy <dmurphy@ti.com>
> 
> I did some editing... and this code looks very similar to 3697 code.
> 

Ok I will review each one.  Keep in mind these drivers are by no means complete
I anticipate this driver to look different then the 3697 code.

But I do expect a certain level of duplication of code as we see across all drivers.

>> diff --git a/drivers/leds/leds-lm3633.c b/drivers/leds/leds-lm3633.c
>> new file mode 100644
>> index 000000000000..3d29b0ed67d3
>> --- /dev/null
>> +++ b/drivers/leds/leds-lm3633.c
>> @@ -0,0 +1,430 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +// TI LM3633 LED chip family driver
>> +// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
>> +
>> +#include <linux/gpio/consumer.h>
>> +#include <linux/i2c.h>
>> +#include <linux/init.h>
>> +#include <linux/leds.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +#include <linux/of_gpio.h>
>> +#include <linux/regmap.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/slab.h>
>> +#include <uapi/linux/uleds.h>
>> +
>> +#include "ti-lmu-led-common.h"
> 
> You might want to move includes to ti-lmu-led-common.h, so these are
> not repeated?
> 

Sounds good

>> +/**
>> + * struct lm3633 -
>> + * @enable_gpio - Hardware enable gpio
>> + * @regulator - LED supply regulator pointer
>> + * @client - Pointer to the I2C client
>> + * @regmap - Devices register map
>> + * @dev - Pointer to the devices device struct
>> + * @lock - Lock for reading/writing the device
>> + * @leds - Array of LED strings
>> + */
>> +struct lm3633 {
>> +	struct gpio_desc *enable_gpio;
>> +	struct regulator *regulator;
>> +	struct i2c_client *client;
>> +	struct regmap *regmap;
>> +	struct device *dev;
>> +	struct mutex lock;
>> +	struct lm3633_led leds[];
>> +};
> 
> Exactly same structure is used for 3697. Worth sharing?

But it is not the same structure for the 3632.  I anticipate this to change
as I debug the code and add pwm support.

> 
>> +static const struct regmap_config lm3633_regmap_config = {
>> +	.reg_bits = 8,
>> +	.val_bits = 8,
>> +
>> +	.max_register = LM3633_CTRL_ENABLE,
>> +	.reg_defaults = lm3633_reg_defs,
>> +	.num_reg_defaults = ARRAY_SIZE(lm3633_reg_defs),
>> +	.cache_type = REGCACHE_FLAT,
>> +};
> 
> Same regmap config. Maybe difficult to share.

Agreed.

> 
>> +static int lm3633_brightness_set(struct led_classdev *led_cdev,
>> +				enum led_brightness brt_val)
>> +{
>> +	struct lm3633_led *led = container_of(led_cdev, struct lm3633_led,
>> +					      led_dev);
>> +	int ctrl_en_val;
>> +	int ret;
>> +
>> +	mutex_lock(&led->priv->lock);
>> +
>> +	if (led->control_bank == LM3633_CONTROL_A) {
>> +		led->lmu_data.msb_brightness_reg = LM3633_CTRL_A_BRT_MSB;
>> +		led->lmu_data.lsb_brightness_reg = LM3633_CTRL_A_BRT_LSB;
>> +		ctrl_en_val = LM3633_CTRL_A_EN;
>> +	} else {
>> +		led->lmu_data.msb_brightness_reg = LM3633_CTRL_B_BRT_MSB;
>> +		led->lmu_data.lsb_brightness_reg = LM3633_CTRL_B_BRT_LSB;
>> +		ctrl_en_val = LM3633_CTRL_B_EN;
>> +	}
>> +
>> +	if (brt_val == LED_OFF)
>> +		ret = regmap_update_bits(led->priv->regmap, LM3633_CTRL_ENABLE,
>> +					 ctrl_en_val, ~ctrl_en_val);
>> +	else
>> +		ret = regmap_update_bits(led->priv->regmap, LM3633_CTRL_ENABLE,
>> +					 ctrl_en_val, ctrl_en_val);
>> +
>> +	ret = ti_lmu_common_set_brightness(&led->lmu_data, brt_val);
>> +	if (ret)
>> +		dev_err(&led->priv->client->dev, "Cannot write brightness\n");
>> +
>> +	mutex_unlock(&led->priv->lock);
>> +	return ret;
>> +}
> 
> Duplicate of 3697 function with different constants.

This will change to support all the control banks.

> 
>> +static int lm3633_set_control_bank(struct lm3633 *priv)
>> +{
>> +	u8 control_bank_config = 0;
>> +	struct lm3633_led *led;
>> +	int ret, i;
>> +
>> +	led = &priv->leds[0];
>> +	if (led->control_bank == LM3633_CONTROL_A)
>> +		led = &priv->leds[1];
>> +
>> +	for (i = 0; i < LM3633_MAX_HVLED_STRINGS; i++)
>> +		if (led->hvled_strings[i] == LM3633_HVLED_ASSIGNMENT)
>> +			control_bank_config |= 1 << i;
>> +
>> +	ret = regmap_write(priv->regmap, LM3633_HVLED_OUTPUT_CONFIG,
>> +			   control_bank_config);
>> +	if (ret)
>> +		dev_err(&priv->client->dev, "Cannot write OUTPUT config\n");
>> +
>> +	return ret;
>> +}
> 
> Duplicate of 3697 function.

This will change to support all the control banks.

> 
>> +static int lm3633_init(struct lm3633 *priv)
>> +{
>> +	struct lm3633_led *led;
>> +	int i, ret;
>> +
>> +	if (priv->enable_gpio) {
>> +		gpiod_direction_output(priv->enable_gpio, 1);
>> +	} else {
>> +		ret = regmap_write(priv->regmap, LM3633_RESET, LM3633_SW_RESET);
>> +		if (ret) {
>> +			dev_err(&priv->client->dev, "Cannot reset the device\n");
>> +			goto out;
>> +		}
>> +	}
>> +
>> +	ret = regmap_write(priv->regmap, LM3633_CTRL_ENABLE, 0x0);
>> +	if (ret) {
>> +		dev_err(&priv->client->dev, "Cannot write ctrl enable\n");
>> +		goto out;
>> +	}
>> +
>> +	ret = lm3633_set_control_bank(priv);
>> +	if (ret)
>> +		dev_err(&priv->client->dev, "Setting the CRTL bank failed\n");
>> +
>> +	for (i = 0; i < LM3633_MAX_CONTROL_BANKS; i++) {
>> +		led = &priv->leds[i];
>> +		ti_lmu_common_set_ramp(&led->lmu_data);
>> +		if (ret)
>> +			dev_err(&priv->client->dev, "Setting the ramp rate failed\n");
>> +	}
>> +out:
>> +	return ret;
>> +}
> 
> Duplicate of 3697 function.


This will change to support all the control banks.

> 
>> +static int lm3633_probe_dt(struct lm3633 *priv)
>> +{
>> +	struct fwnode_handle *child = NULL;
>> +	struct lm3633_led *led;
>> +	const char *name;
>> +	int control_bank;
>> +	size_t i = 0;
>> +	int ret;
>> +
>> +	priv->enable_gpio = devm_gpiod_get_optional(&priv->client->dev,
>> +						   "enable", GPIOD_OUT_LOW);
>> +	if (IS_ERR(priv->enable_gpio)) {
>> +		ret = PTR_ERR(priv->enable_gpio);
>> +		dev_err(&priv->client->dev, "Failed to get enable gpio: %d\n",
>> +			ret);
>> +		return ret;
>> +	}
>> +
>> +	priv->regulator = devm_regulator_get(&priv->client->dev, "vled");
>> +	if (IS_ERR(priv->regulator))
>> +		priv->regulator = NULL;
>> +
>> +	device_for_each_child_node(priv->dev, child) {
>> +		ret = fwnode_property_read_u32(child, "reg", &control_bank);
>> +		if (ret) {
>> +			dev_err(&priv->client->dev, "reg property missing\n");
>> +			fwnode_handle_put(child);
>> +			goto child_out;
>> +		}
>> +
>> +		if (control_bank > LM3633_CONTROL_B) {
>> +			dev_err(&priv->client->dev, "reg property is invalid\n");
>> +			ret = -EINVAL;
>> +			fwnode_handle_put(child);
>> +			goto child_out;
>> +		}
>> +
>> +		led = &priv->leds[i];
>> +
>> +		led->control_bank = control_bank;
>> +		led->lmu_data.bank_id = control_bank;
>> +		led->lmu_data.enable_reg = LM3633_CTRL_ENABLE;
>> +		led->lmu_data.regmap = priv->regmap;
>> +		if (control_bank == LM3633_CONTROL_A)
>> +			led->lmu_data.runtime_ramp_reg = LM3633_CTRL_A_RAMP;
>> +		else
>> +			led->lmu_data.runtime_ramp_reg = LM3633_CTRL_B_RAMP;
>> +
>> +		if (control_bank <= LM3633_CONTROL_B)
>> +			ret = fwnode_property_read_u32_array(child, "led-sources",
>> +						    led->hvled_strings,
>> +						    LM3633_MAX_HVLED_STRINGS);
>> +		else
>> +			ret = fwnode_property_read_u32_array(child, "led-sources",
>> +						    led->lvled_strings,
>> +						    LM3633_MAX_LVLED_STRINGS);
>> +		if (ret) {
>> +			dev_err(&priv->client->dev, "led-sources property missing\n");
>> +			fwnode_handle_put(child);
>> +			goto child_out;
>> +		}
>> +
>> +		ret = ti_lmu_common_get_ramp_params(&priv->client->dev, child, &led->lmu_data);
>> +		if (ret)
>> +			dev_warn(&priv->client->dev, "runtime-ramp properties missing\n");
>> +
>> +		fwnode_property_read_string(child, "linux,default-trigger",
>> +					    &led->led_dev.default_trigger);
>> +
>> +		ret = fwnode_property_read_string(child, "label", &name);
>> +		if (ret)
>> +			snprintf(led->label, sizeof(led->label),
>> +				"%s::", priv->client->name);
>> +		else
>> +			snprintf(led->label, sizeof(led->label),
>> +				 "%s:%s", priv->client->name, name);
>> +
>> +		led->priv = priv;
>> +		led->led_dev.name = led->label;
>> +		led->led_dev.brightness_set_blocking = lm3633_brightness_set;
>> +
>> +		ret = devm_led_classdev_register(priv->dev, &led->led_dev);
>> +		if (ret) {
>> +			dev_err(&priv->client->dev, "led register err: %d\n",
>> +				ret);
>> +			fwnode_handle_put(child);
>> +			goto child_out;
>> +		}
>> +
>> +		i++;
>> +	}
>> +
>> +child_out:
>> +	return ret;
>> +}
> 
> Very similar, but not quite same.

I know this function will definitely change to support the LVLED control bank setting.
The main difference here is that the HVLED has a simple control bank configuration and the
LVLED is highly configurable with multiple permutations.

As well as maybe the PWM support

> 
>> +static int lm3633_probe(struct i2c_client *client,
>> +			const struct i2c_device_id *id)
>> +{
>> +	struct lm3633 *led;
>> +	int count;
>> +	int ret;
>> +
>> +	count = device_get_child_node_count(&client->dev);
>> +	if (!count) {
>> +		dev_err(&client->dev, "LEDs are not defined in device tree!");
>> +		return -ENODEV;
>> +	}
>> +
>> +	led = devm_kzalloc(&client->dev, struct_size(led, leds, count),
>> +			   GFP_KERNEL);
>> +	if (!led)
>> +		return -ENOMEM;
>> +
>> +	mutex_init(&led->lock);
>> +	i2c_set_clientdata(client, led);
>> +
>> +	led->client = client;
>> +	led->dev = &client->dev;
>> +	led->regmap = devm_regmap_init_i2c(client, &lm3633_regmap_config);
>> +	if (IS_ERR(led->regmap)) {
>> +		ret = PTR_ERR(led->regmap);
>> +		dev_err(&client->dev, "Failed to allocate register map: %d\n",
>> +			ret);
>> +		return ret;
>> +	}
>> +
>> +	ret = lm3633_probe_dt(led);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return lm3633_init(led);
>> +}
> 
> Duplicate.

I do expect the probes to be the same as in most cases.

> 
>> +static int lm3633_remove(struct i2c_client *client)
>> +{
>> +	struct lm3633 *led = i2c_get_clientdata(client);
>> +	int ret;
>> +
>> +	ret = regmap_update_bits(led->regmap, LM3633_CTRL_ENABLE,
>> +				 LM3633_CTRL_A_B_EN, 0);
>> +	if (ret) {
>> +		dev_err(&led->client->dev, "Failed to disable the device\n");
>> +		return ret;
>> +	}
>> +
>> +	if (led->enable_gpio)
>> +		gpiod_direction_output(led->enable_gpio, 0);
>> +
>> +	if (led->regulator) {
>> +		ret = regulator_disable(led->regulator);
>> +		if (ret)
>> +			dev_err(&led->client->dev,
>> +				"Failed to disable regulator\n");
>> +	}
>> +
>> +	mutex_destroy(&led->lock);
>> +
>> +	return 0;
>> +}
> 
> Duplicate.
> 
> Can we get some more sharing? One way would be to have struct with
> all the constants (instead of #defines) use that...?

I will look at the adding common constants to but they should be common across
more then just 2 devices.  As you can see the LM3632 code is quite different
when you add in the flash/torch support.

I am trying to keep the common as light weight as possible and not add code that
cannot be used across multiple devices.

Dan

> 
> Thanks,
> 
> 									Pavel
> 


-- 
------------------
Dan Murphy

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

* Re: [RFC PATCH 7/9] leds: lm3633: Introduce the lm3633 driver
  2018-09-27 12:04       ` Dan Murphy
  (?)
@ 2018-09-27 21:47       ` Pavel Machek
  2018-09-28 16:48           ` Dan Murphy
  -1 siblings, 1 reply; 32+ messages in thread
From: Pavel Machek @ 2018-09-27 21:47 UTC (permalink / raw)
  To: Dan Murphy
  Cc: robh+dt, jacek.anaszewski, devicetree, linux-kernel, lee.jones,
	linux-omap, linux-leds

[-- Attachment #1: Type: text/plain, Size: 757 bytes --]

Hi!

> > Duplicate.
> > 
> > Can we get some more sharing? One way would be to have struct with
> > all the constants (instead of #defines) use that...?
> 
> I will look at the adding common constants to but they should be common across
> more then just 2 devices.  As you can see the LM3632 code is quite different
> when you add in the flash/torch support.

LM3632 is indeed different, I see.

On the other hand, I'd really like to see the code shared, even if it
is just for 2 devices. (I believe that family is big enough that we'll
likely see more sharing in future).

Thanks,

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

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

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

* Re: [RFC PATCH 7/9] leds: lm3633: Introduce the lm3633 driver
  2018-09-27 21:47       ` Pavel Machek
@ 2018-09-28 16:48           ` Dan Murphy
  0 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-28 16:48 UTC (permalink / raw)
  To: Pavel Machek
  Cc: robh+dt, jacek.anaszewski, devicetree, linux-kernel, lee.jones,
	linux-omap, linux-leds

Pavel

On 09/27/2018 04:47 PM, Pavel Machek wrote:
> Hi!
> 
>>> Duplicate.
>>>
>>> Can we get some more sharing? One way would be to have struct with
>>> all the constants (instead of #defines) use that...?
>>
>> I will look at the adding common constants to but they should be common across
>> more then just 2 devices.  As you can see the LM3632 code is quite different
>> when you add in the flash/torch support.
> 
> LM3632 is indeed different, I see.
> 
> On the other hand, I'd really like to see the code shared, even if it
> is just for 2 devices. (I believe that family is big enough that we'll
> likely see more sharing in future).
> 

Yes I have been looking at the other drivers I created that support 11 bit and
I plan to move them over to call the common code if the code is accepted.

I just finished debugging and getting the 3632 and 3633 drivers working with
flash/torch and configurable indicator support and there is a lot of code
diff especially around the node parsing and configuration.

Dan

> Thanks,
> 
> 									Pavel
> 


-- 
------------------
Dan Murphy

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

* Re: [RFC PATCH 7/9] leds: lm3633: Introduce the lm3633 driver
@ 2018-09-28 16:48           ` Dan Murphy
  0 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-09-28 16:48 UTC (permalink / raw)
  To: Pavel Machek
  Cc: robh+dt, jacek.anaszewski, devicetree, linux-kernel, lee.jones,
	linux-omap, linux-leds

Pavel

On 09/27/2018 04:47 PM, Pavel Machek wrote:
> Hi!
> 
>>> Duplicate.
>>>
>>> Can we get some more sharing? One way would be to have struct with
>>> all the constants (instead of #defines) use that...?
>>
>> I will look at the adding common constants to but they should be common across
>> more then just 2 devices.  As you can see the LM3632 code is quite different
>> when you add in the flash/torch support.
> 
> LM3632 is indeed different, I see.
> 
> On the other hand, I'd really like to see the code shared, even if it
> is just for 2 devices. (I believe that family is big enough that we'll
> likely see more sharing in future).
> 

Yes I have been looking at the other drivers I created that support 11 bit and
I plan to move them over to call the common code if the code is accepted.

I just finished debugging and getting the 3632 and 3633 drivers working with
flash/torch and configurable indicator support and there is a lot of code
diff especially around the node parsing and configuration.

Dan

> Thanks,
> 
> 									Pavel
> 


-- 
------------------
Dan Murphy

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

* Re: [RFC PATCH 9/9] leds: lm3632: Introduce the TI LM3632 driver
  2018-09-26 13:09   ` Dan Murphy
  (?)
@ 2018-10-07 13:46   ` Pavel Machek
  2018-10-08 12:23       ` Dan Murphy
  -1 siblings, 1 reply; 32+ messages in thread
From: Pavel Machek @ 2018-10-07 13:46 UTC (permalink / raw)
  To: Dan Murphy
  Cc: robh+dt, jacek.anaszewski, devicetree, linux-kernel, lee.jones,
	linux-omap, linux-leds

[-- Attachment #1: Type: text/plain, Size: 523 bytes --]

On Wed 2018-09-26 08:09:21, Dan Murphy wrote:
> Add the dedicated TI LM3632 LED driver.  This
> LED device is capable of driving a backlight display.
> 
> In addition to the backlight the device has control
> of a strobe and torch output.

Is this in some way similar to 3639? That also seems to include flash
and torch.

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

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

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

* Re: [RFC PATCH 9/9] leds: lm3632: Introduce the TI LM3632 driver
  2018-10-07 13:46   ` Pavel Machek
@ 2018-10-08 12:23       ` Dan Murphy
  0 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-10-08 12:23 UTC (permalink / raw)
  To: Pavel Machek
  Cc: robh+dt, jacek.anaszewski, devicetree, linux-kernel, lee.jones,
	linux-omap, linux-leds

Pavel

On 10/07/2018 08:46 AM, Pavel Machek wrote:
> On Wed 2018-09-26 08:09:21, Dan Murphy wrote:
>> Add the dedicated TI LM3632 LED driver.  This
>> LED device is capable of driving a backlight display.
>>
>> In addition to the backlight the device has control
>> of a strobe and torch output.
> 
> Is this in some way similar to 3639? That also seems to include flash
> and torch.
> 
> drivers/video/backlight/lm3639_bl.c
> 	

NICE!  

Yes this driver looks to be good enough to use for the 3632 as well.

We would have to extend that driver to support the 3632, add firmware support,
convert the led class registration to the flash LED registration and add the regulator support.

I can abandon the dedicated LED driver and update this one for the 3632.
No reason to have duplicate code.

Dan

							Pavel
> 


-- 
------------------
Dan Murphy

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

* Re: [RFC PATCH 9/9] leds: lm3632: Introduce the TI LM3632 driver
@ 2018-10-08 12:23       ` Dan Murphy
  0 siblings, 0 replies; 32+ messages in thread
From: Dan Murphy @ 2018-10-08 12:23 UTC (permalink / raw)
  To: Pavel Machek
  Cc: robh+dt, jacek.anaszewski, devicetree, linux-kernel, lee.jones,
	linux-omap, linux-leds

Pavel

On 10/07/2018 08:46 AM, Pavel Machek wrote:
> On Wed 2018-09-26 08:09:21, Dan Murphy wrote:
>> Add the dedicated TI LM3632 LED driver.  This
>> LED device is capable of driving a backlight display.
>>
>> In addition to the backlight the device has control
>> of a strobe and torch output.
> 
> Is this in some way similar to 3639? That also seems to include flash
> and torch.
> 
> drivers/video/backlight/lm3639_bl.c
> 	

NICE!  

Yes this driver looks to be good enough to use for the 3632 as well.

We would have to extend that driver to support the 3632, add firmware support,
convert the led class registration to the flash LED registration and add the regulator support.

I can abandon the dedicated LED driver and update this one for the 3632.
No reason to have duplicate code.

Dan

							Pavel
> 


-- 
------------------
Dan Murphy

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

end of thread, other threads:[~2018-10-08 12:24 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-09-26 13:09 [RFC PATCH 0/9] TI LMU and Dedicated LED drivers Dan Murphy
2018-09-26 13:09 ` Dan Murphy
2018-09-26 13:09 ` [RFC PATCH 1/9] leds: add TI LMU backlight driver Dan Murphy
2018-09-26 13:09   ` Dan Murphy
2018-09-26 15:01   ` Tony Lindgren
2018-09-26 15:30     ` Dan Murphy
2018-09-26 15:30       ` Dan Murphy
2018-09-26 13:09 ` [RFC PATCH 2/9] dt-bindings: ti-lmu: Remove LM3697 Dan Murphy
2018-09-26 13:09   ` Dan Murphy
2018-09-26 13:09 ` [RFC PATCH 3/9] mfd: ti-lmu: Remove support for LM3697 Dan Murphy
2018-09-26 13:09   ` Dan Murphy
2018-09-26 13:09 ` [RFC PATCH 4/9] dt-bindings: leds: Add bindings for lm3697 driver Dan Murphy
2018-09-26 13:09   ` Dan Murphy
2018-09-26 13:09 ` [RFC PATCH 5/9] leds: lm3697: Introduce the " Dan Murphy
2018-09-26 13:09   ` Dan Murphy
2018-09-26 13:09 ` [RFC PATCH 6/9] dt-bindings: leds: Add support for the LM3633 Dan Murphy
2018-09-26 13:09   ` Dan Murphy
2018-09-26 13:09 ` [RFC PATCH 7/9] leds: lm3633: Introduce the lm3633 driver Dan Murphy
2018-09-26 13:09   ` Dan Murphy
2018-09-26 22:36   ` Pavel Machek
2018-09-27 12:04     ` Dan Murphy
2018-09-27 12:04       ` Dan Murphy
2018-09-27 21:47       ` Pavel Machek
2018-09-28 16:48         ` Dan Murphy
2018-09-28 16:48           ` Dan Murphy
2018-09-26 13:09 ` [RFC PATCH 8/9] dt-bindings: leds: Add the LM3632 LED dt binding Dan Murphy
2018-09-26 13:09   ` Dan Murphy
2018-09-26 13:09 ` [RFC PATCH 9/9] leds: lm3632: Introduce the TI LM3632 driver Dan Murphy
2018-09-26 13:09   ` Dan Murphy
2018-10-07 13:46   ` Pavel Machek
2018-10-08 12:23     ` Dan Murphy
2018-10-08 12:23       ` Dan Murphy

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.