All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] Add sy7802 flash led driver
@ 2024-03-27 22:51 ` André Apitzsch
  0 siblings, 0 replies; 10+ messages in thread
From: André Apitzsch via B4 Relay @ 2024-03-27 22:51 UTC (permalink / raw)
  To: Pavel Machek, Lee Jones, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Kees Cook, Gustavo A. R. Silva, Bjorn Andersson,
	Konrad Dybcio
  Cc: linux-leds, devicetree, linux-kernel, linux-hardening,
	linux-arm-msm, ~postmarketos/upstreaming, André Apitzsch

This series introduces a driver for the Silergy SY7802 charge pump used
in the BQ Aquaris M5 and X5 smartphones.

The implementation is based on information extracted from downstream as
the datasheet provided by a distributor of the hardware didn't include
any information about the i2c register description.

Signed-off-by: André Apitzsch <git@apitzsch.eu>
---
André Apitzsch (3):
      dt-bindings: leds: Add Silergy SY7802 flash LED
      leds: sy7802: Add support for Silergy SY7802 flash LED controller
      arm64: dts: qcom: msm8939-longcheer-l9100: Add rear flash

 .../devicetree/bindings/leds/silergy,sy7802.yaml   |  96 ++++
 .../boot/dts/qcom/msm8939-longcheer-l9100.dts      |  26 +
 drivers/leds/flash/Kconfig                         |  11 +
 drivers/leds/flash/Makefile                        |   1 +
 drivers/leds/flash/leds-sy7802.c                   | 532 +++++++++++++++++++++
 5 files changed, 666 insertions(+)
---
base-commit: 084c8e315db34b59d38d06e684b1a0dd07d30287
change-id: 20240325-sy7802-f40fc6f56525

Best regards,
-- 
André Apitzsch <git@apitzsch.eu>



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

* [PATCH 0/3] Add sy7802 flash led driver
@ 2024-03-27 22:51 ` André Apitzsch
  0 siblings, 0 replies; 10+ messages in thread
From: André Apitzsch @ 2024-03-27 22:51 UTC (permalink / raw)
  To: Pavel Machek, Lee Jones, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Kees Cook, Gustavo A. R. Silva, Bjorn Andersson,
	Konrad Dybcio
  Cc: linux-leds, devicetree, linux-kernel, linux-hardening,
	linux-arm-msm, ~postmarketos/upstreaming, André Apitzsch

This series introduces a driver for the Silergy SY7802 charge pump used
in the BQ Aquaris M5 and X5 smartphones.

The implementation is based on information extracted from downstream as
the datasheet provided by a distributor of the hardware didn't include
any information about the i2c register description.

Signed-off-by: André Apitzsch <git@apitzsch.eu>
---
André Apitzsch (3):
      dt-bindings: leds: Add Silergy SY7802 flash LED
      leds: sy7802: Add support for Silergy SY7802 flash LED controller
      arm64: dts: qcom: msm8939-longcheer-l9100: Add rear flash

 .../devicetree/bindings/leds/silergy,sy7802.yaml   |  96 ++++
 .../boot/dts/qcom/msm8939-longcheer-l9100.dts      |  26 +
 drivers/leds/flash/Kconfig                         |  11 +
 drivers/leds/flash/Makefile                        |   1 +
 drivers/leds/flash/leds-sy7802.c                   | 532 +++++++++++++++++++++
 5 files changed, 666 insertions(+)
---
base-commit: 084c8e315db34b59d38d06e684b1a0dd07d30287
change-id: 20240325-sy7802-f40fc6f56525

Best regards,
-- 
André Apitzsch <git@apitzsch.eu>


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

* [PATCH 1/3] dt-bindings: leds: Add Silergy SY7802 flash LED
  2024-03-27 22:51 ` André Apitzsch
@ 2024-03-27 22:51   ` André Apitzsch
  -1 siblings, 0 replies; 10+ messages in thread
From: André Apitzsch via B4 Relay @ 2024-03-27 22:51 UTC (permalink / raw)
  To: Pavel Machek, Lee Jones, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Kees Cook, Gustavo A. R. Silva, Bjorn Andersson,
	Konrad Dybcio
  Cc: linux-leds, devicetree, linux-kernel, linux-hardening,
	linux-arm-msm, ~postmarketos/upstreaming, André Apitzsch

From: André Apitzsch <git@apitzsch.eu>

Document Silergy SY7802 flash LED driver devicetree bindings.

Signed-off-by: André Apitzsch <git@apitzsch.eu>
---
 .../devicetree/bindings/leds/silergy,sy7802.yaml   | 96 ++++++++++++++++++++++
 1 file changed, 96 insertions(+)

diff --git a/Documentation/devicetree/bindings/leds/silergy,sy7802.yaml b/Documentation/devicetree/bindings/leds/silergy,sy7802.yaml
new file mode 100644
index 000000000000..d32efac8baa6
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/silergy,sy7802.yaml
@@ -0,0 +1,96 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/leds/silergy,sy7802.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Silergy SY7802 1800mA Boost Charge Pump LED Driver
+
+maintainers:
+  - André Apitzsch <git@apitzsch.eu>
+
+description: |
+  The SY7802 is a current-regulated charge pump which can regulate two current
+  levels for Flash and Torch modes.
+
+  The SY7802 is a high-current synchronous boost converter with 2-channel
+  high side current sources. Each channel is able to deliver 900mA current.
+
+properties:
+  compatible:
+    enum:
+      - silergy,sy7802
+
+  reg:
+    maxItems: 1
+
+  enable-gpios:
+    maxItems: 1
+    description: A connection to the 'EN' pin.
+
+  flash-gpios:
+    maxItems: 1
+    description: A connection to the 'FLEN' pin.
+
+  vin-supply:
+    description: Regulator providing power to the 'VIN' pin.
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+patternProperties:
+  "^led@[0-1]$":
+    type: object
+    $ref: common.yaml#
+    unevaluatedProperties: false
+
+    properties:
+      reg:
+        description: Index of the LED.
+        minimum: 0
+        maximum: 1
+
+      led-sources:
+        allOf:
+          - minItems: 1
+            maxItems: 2
+            items:
+              minimum: 0
+              maximum: 1
+
+    required:
+      - reg
+      - led-sources
+
+required:
+  - compatible
+  - reg
+  - "#address-cells"
+  - "#size-cells"
+  - enable-gpios
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/leds/common.h>
+
+    flash-led-controller@53 {
+        compatible = "silergy,sy7802";
+        reg = <0x53>;
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        enable-gpios = <&tlmm 16 GPIO_ACTIVE_HIGH>;
+
+        led@0 {
+            reg = <0>;
+            function = LED_FUNCTION_FLASH;
+            color = <LED_COLOR_ID_WHITE>;
+            led-sources = <0>, <1>;
+        };
+    };

-- 
2.44.0



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

* [PATCH 1/3] dt-bindings: leds: Add Silergy SY7802 flash LED
@ 2024-03-27 22:51   ` André Apitzsch
  0 siblings, 0 replies; 10+ messages in thread
From: André Apitzsch @ 2024-03-27 22:51 UTC (permalink / raw)
  To: Pavel Machek, Lee Jones, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Kees Cook, Gustavo A. R. Silva, Bjorn Andersson,
	Konrad Dybcio
  Cc: linux-leds, devicetree, linux-kernel, linux-hardening,
	linux-arm-msm, ~postmarketos/upstreaming, André Apitzsch

Document Silergy SY7802 flash LED driver devicetree bindings.

Signed-off-by: André Apitzsch <git@apitzsch.eu>
---
 .../devicetree/bindings/leds/silergy,sy7802.yaml   | 96 ++++++++++++++++++++++
 1 file changed, 96 insertions(+)

diff --git a/Documentation/devicetree/bindings/leds/silergy,sy7802.yaml b/Documentation/devicetree/bindings/leds/silergy,sy7802.yaml
new file mode 100644
index 000000000000..d32efac8baa6
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/silergy,sy7802.yaml
@@ -0,0 +1,96 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/leds/silergy,sy7802.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Silergy SY7802 1800mA Boost Charge Pump LED Driver
+
+maintainers:
+  - André Apitzsch <git@apitzsch.eu>
+
+description: |
+  The SY7802 is a current-regulated charge pump which can regulate two current
+  levels for Flash and Torch modes.
+
+  The SY7802 is a high-current synchronous boost converter with 2-channel
+  high side current sources. Each channel is able to deliver 900mA current.
+
+properties:
+  compatible:
+    enum:
+      - silergy,sy7802
+
+  reg:
+    maxItems: 1
+
+  enable-gpios:
+    maxItems: 1
+    description: A connection to the 'EN' pin.
+
+  flash-gpios:
+    maxItems: 1
+    description: A connection to the 'FLEN' pin.
+
+  vin-supply:
+    description: Regulator providing power to the 'VIN' pin.
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+patternProperties:
+  "^led@[0-1]$":
+    type: object
+    $ref: common.yaml#
+    unevaluatedProperties: false
+
+    properties:
+      reg:
+        description: Index of the LED.
+        minimum: 0
+        maximum: 1
+
+      led-sources:
+        allOf:
+          - minItems: 1
+            maxItems: 2
+            items:
+              minimum: 0
+              maximum: 1
+
+    required:
+      - reg
+      - led-sources
+
+required:
+  - compatible
+  - reg
+  - "#address-cells"
+  - "#size-cells"
+  - enable-gpios
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/leds/common.h>
+
+    flash-led-controller@53 {
+        compatible = "silergy,sy7802";
+        reg = <0x53>;
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        enable-gpios = <&tlmm 16 GPIO_ACTIVE_HIGH>;
+
+        led@0 {
+            reg = <0>;
+            function = LED_FUNCTION_FLASH;
+            color = <LED_COLOR_ID_WHITE>;
+            led-sources = <0>, <1>;
+        };
+    };

-- 
2.44.0


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

* [PATCH 2/3] leds: sy7802: Add support for Silergy SY7802 flash LED controller
  2024-03-27 22:51 ` André Apitzsch
@ 2024-03-27 22:51   ` André Apitzsch
  -1 siblings, 0 replies; 10+ messages in thread
From: André Apitzsch via B4 Relay @ 2024-03-27 22:51 UTC (permalink / raw)
  To: Pavel Machek, Lee Jones, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Kees Cook, Gustavo A. R. Silva, Bjorn Andersson,
	Konrad Dybcio
  Cc: linux-leds, devicetree, linux-kernel, linux-hardening,
	linux-arm-msm, ~postmarketos/upstreaming, André Apitzsch

From: André Apitzsch <git@apitzsch.eu>

Add support for SY7802 flash LED controller. It can support up to 1.8A
flash current.

Signed-off-by: André Apitzsch <git@apitzsch.eu>
---
 drivers/leds/flash/Kconfig       |  11 +
 drivers/leds/flash/Makefile      |   1 +
 drivers/leds/flash/leds-sy7802.c | 532 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 544 insertions(+)

diff --git a/drivers/leds/flash/Kconfig b/drivers/leds/flash/Kconfig
index 809b6d98bb3e..f39f0bfe6eef 100644
--- a/drivers/leds/flash/Kconfig
+++ b/drivers/leds/flash/Kconfig
@@ -121,4 +121,15 @@ config LEDS_SGM3140
 	  This option enables support for the SGM3140 500mA Buck/Boost Charge
 	  Pump LED Driver.
 
+config LEDS_SY7802
+	tristate "LED support for the Silergy SY7802"
+	depends on I2C && OF
+	depends on GPIOLIB
+	select REGMAP_I2C
+	help
+	  This option enables support for the SY7802 flash LED controller.
+	  SY7802 includes torch and flash functions with programmable current.
+
+	  This driver can be built as a module, it will be called "leds-sy7802".
+
 endif # LEDS_CLASS_FLASH
diff --git a/drivers/leds/flash/Makefile b/drivers/leds/flash/Makefile
index 91d60a4b7952..48860eeced79 100644
--- a/drivers/leds/flash/Makefile
+++ b/drivers/leds/flash/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_LEDS_QCOM_FLASH)	+= leds-qcom-flash.o
 obj-$(CONFIG_LEDS_RT4505)	+= leds-rt4505.o
 obj-$(CONFIG_LEDS_RT8515)	+= leds-rt8515.o
 obj-$(CONFIG_LEDS_SGM3140)	+= leds-sgm3140.o
+obj-$(CONFIG_LEDS_SY7802)	+= leds-sy7802.o
diff --git a/drivers/leds/flash/leds-sy7802.c b/drivers/leds/flash/leds-sy7802.c
new file mode 100644
index 000000000000..c03a571b0e08
--- /dev/null
+++ b/drivers/leds/flash/leds-sy7802.c
@@ -0,0 +1,532 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Silergy SY7802 flash LED driver with I2C interface
+ *
+ * Copyright 2024 André Apitzsch <git@apitzsch.eu>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/led-class-flash.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#define SY7802_MAX_LEDS 2
+#define SY7802_LED_JOINT 2
+
+#define SY7802_REG_ENABLE		0x10
+#define SY7802_REG_TORCH_BRIGHTNESS	0xa0
+#define SY7802_REG_FLASH_BRIGHTNESS	0xb0
+#define SY7802_REG_FLASH_DURATION	0xc0
+#define SY7802_REG_FLAGS		0xd0
+#define SY7802_REG_CONFIG_1		0xe0
+#define SY7802_REG_CONFIG_2		0xf0
+#define SY7802_REG_VIN_MONITOR		0x80
+#define SY7802_REG_LAST_FLASH		0x81
+#define SY7802_REG_VLED_MONITOR		0x30
+#define SY7802_REG_ADC_DELAY		0x31
+#define SY7802_REG_DEV_ID		0xff
+
+#define SY7802_MODE_OFF		0
+#define SY7802_MODE_TORCH	2
+#define SY7802_MODE_FLASH	3
+#define SY7802_MODE_MASK	GENMASK(1, 0)
+
+#define SY7802_LEDS_SHIFT	3
+#define SY7802_LEDS_MASK(_id)	(BIT(_id) << SY7802_LEDS_SHIFT)
+#define SY7802_LEDS_MASK_ALL	(SY7802_LEDS_MASK(0) | SY7802_LEDS_MASK(1))
+
+#define SY7802_TORCH_CURRENT_SHIFT	3
+#define SY7802_TORCH_CURRENT_MASK(_id) \
+	(GENMASK(2, 0) << (SY7802_TORCH_CURRENT_SHIFT * (_id)))
+#define SY7802_TORCH_CURRENT_MASK_ALL \
+	(SY7802_TORCH_CURRENT_MASK(0) | SY7802_TORCH_CURRENT_MASK(1))
+
+#define SY7802_FLASH_CURRENT_SHIFT	4
+#define SY7802_FLASH_CURRENT_MASK(_id) \
+	(GENMASK(3, 0) << (SY7802_FLASH_CURRENT_SHIFT * (_id)))
+#define SY7802_FLASH_CURRENT_MASK_ALL \
+	(SY7802_FLASH_CURRENT_MASK(0) | SY7802_FLASH_CURRENT_MASK(1))
+
+#define SY7802_TIMEOUT_DEFAULT_US	512000U
+#define SY7802_TIMEOUT_MIN_US		32000U
+#define SY7802_TIMEOUT_MAX_US		1024000U
+#define SY7802_TIMEOUT_STEPSIZE_US	32000U
+
+#define SY7802_TORCH_BRIGHTNESS_MAX 8
+
+#define SY7802_FLASH_BRIGHTNESS_DEFAULT	14
+#define SY7802_FLASH_BRIGHTNESS_MIN	0
+#define SY7802_FLASH_BRIGHTNESS_MAX	15
+#define SY7802_FLASH_BRIGHTNESS_STEP	1
+
+#define SY7802_FLAG_TIMEOUT			(1 << 0)
+#define SY7802_FLAG_THERMAL_SHUTDOWN		(1 << 1)
+#define SY7802_FLAG_LED_FAULT			(1 << 2)
+#define SY7802_FLAG_TX1_INTERRUPT		(1 << 3)
+#define SY7802_FLAG_TX2_INTERRUPT		(1 << 4)
+#define SY7802_FLAG_LED_THERMAL_FAULT		(1 << 5)
+#define SY7802_FLAG_FLASH_INPUT_VOLTAGE_LOW	(1 << 6)
+#define SY7802_FLAG_INPUT_VOLTAGE_LOW		(1 << 7)
+
+#define SY7802_CHIP_ID	0x51
+
+static const struct reg_default sy7802_regmap_defs[] = {
+	{ SY7802_REG_ENABLE, SY7802_LEDS_MASK_ALL },
+	{ SY7802_REG_TORCH_BRIGHTNESS, 0x92 },
+	{ SY7802_REG_FLASH_BRIGHTNESS, SY7802_FLASH_BRIGHTNESS_DEFAULT |
+		SY7802_FLASH_BRIGHTNESS_DEFAULT << SY7802_FLASH_CURRENT_SHIFT },
+	{ SY7802_REG_FLASH_DURATION, 0x6f },
+	{ SY7802_REG_FLAGS, 0x0 },
+	{ SY7802_REG_CONFIG_1, 0x68 },
+	{ SY7802_REG_CONFIG_2, 0xf0 },
+};
+
+struct sy7802_led {
+	struct led_classdev_flash flash;
+	struct sy7802 *chip;
+	u8 led_no;
+};
+
+struct sy7802 {
+	struct mutex mutex;
+	struct device *dev;
+	struct regmap *regmap;
+
+	struct gpio_desc *enable_gpio;
+	struct regulator *vin_regulator;
+
+	unsigned int fled_strobe_used;
+	unsigned int fled_torch_used;
+	unsigned int leds_active;
+	int num_leds;
+	struct sy7802_led leds[] __counted_by(num_leds);
+};
+
+static int sy7802_torch_brightness_set(struct led_classdev *lcdev, enum led_brightness level)
+{
+	struct sy7802_led *led = container_of(lcdev, struct sy7802_led, flash.led_cdev);
+	u32 led_enable_mask = led->led_no == SY7802_LED_JOINT ? SY7802_LEDS_MASK_ALL :
+			      SY7802_LEDS_MASK(led->led_no);
+	u32 enable_mask = SY7802_MODE_MASK | led_enable_mask;
+	u32 val = level ? led_enable_mask : SY7802_MODE_OFF;
+	struct sy7802 *chip = led->chip;
+	u32 curr;
+	u32 mask;
+	int ret;
+
+	mutex_lock(&chip->mutex);
+
+	/*
+	 * There is only one set of flash control logic, and this flag is used to check if 'strobe'
+	 * is currently being used.
+	 */
+	if (chip->fled_strobe_used) {
+		dev_warn(chip->dev, "Please disable strobe first [%d]\n", chip->fled_strobe_used);
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	if (level)
+		curr = chip->fled_torch_used | BIT(led->led_no);
+	else
+		curr = chip->fled_torch_used & ~BIT(led->led_no);
+
+	if (curr)
+		val |= SY7802_MODE_TORCH;
+
+	/* Torch needs to be disabled first to apply new brightness */
+	ret = regmap_update_bits(chip->regmap, SY7802_REG_ENABLE, SY7802_MODE_MASK,
+				 SY7802_MODE_OFF);
+	if (ret)
+		goto unlock;
+
+	mask = led->led_no == SY7802_LED_JOINT ? SY7802_TORCH_CURRENT_MASK_ALL :
+	       SY7802_TORCH_CURRENT_MASK(led->led_no);
+
+	/* Register expects brightness between 0 and MAX_BRIGHTNESS - 1 */
+	if (level)
+		level -= 1;
+
+	level |= (level << SY7802_TORCH_CURRENT_SHIFT);
+
+	ret = regmap_update_bits(chip->regmap, SY7802_REG_TORCH_BRIGHTNESS, mask, level);
+	if (ret)
+		goto unlock;
+
+	ret = regmap_update_bits(chip->regmap, SY7802_REG_ENABLE, enable_mask, val);
+	if (ret)
+		goto unlock;
+
+	chip->fled_torch_used = curr;
+
+unlock:
+	mutex_unlock(&chip->mutex);
+	return ret;
+}
+
+static int sy7802_flash_brightness_set(struct led_classdev_flash *fl_cdev, u32 brightness)
+{
+	struct sy7802_led *led = container_of(fl_cdev, struct sy7802_led, flash);
+	struct led_flash_setting *s = &fl_cdev->brightness;
+	u32 val = (brightness - s->min) / s->step;
+	struct sy7802 *chip = led->chip;
+	u32 mask;
+	int ret;
+
+	val |= (val << SY7802_FLASH_CURRENT_SHIFT);
+	mask = led->led_no == SY7802_LED_JOINT ? SY7802_FLASH_CURRENT_MASK_ALL :
+	       SY7802_FLASH_CURRENT_MASK(led->led_no);
+
+	mutex_lock(&chip->mutex);
+	ret = regmap_update_bits(chip->regmap, SY7802_REG_FLASH_BRIGHTNESS, mask, val);
+	mutex_unlock(&chip->mutex);
+
+	return ret;
+}
+
+static int sy7802_strobe_set(struct led_classdev_flash *fl_cdev, bool state)
+{
+	struct sy7802_led *led = container_of(fl_cdev, struct sy7802_led, flash);
+	u32 led_enable_mask = led->led_no == SY7802_LED_JOINT ? SY7802_LEDS_MASK_ALL :
+			      SY7802_LEDS_MASK(led->led_no);
+	u32 enable_mask = SY7802_MODE_MASK | led_enable_mask;
+	u32 val = state ? led_enable_mask : SY7802_MODE_OFF;
+	struct sy7802 *chip = led->chip;
+	u32 curr;
+	int ret;
+
+	mutex_lock(&chip->mutex);
+
+	/*
+	 * There is only one set of flash control logic, and this flag is used to check if 'torch'
+	 * is currently being used.
+	 */
+	if (chip->fled_torch_used) {
+		dev_warn(chip->dev, "Please disable torch first [0x%x]\n", chip->fled_torch_used);
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	if (state)
+		curr = chip->fled_strobe_used | BIT(led->led_no);
+	else
+		curr = chip->fled_strobe_used & ~BIT(led->led_no);
+
+	if (curr)
+		val |= SY7802_MODE_FLASH;
+
+	ret = regmap_update_bits(chip->regmap, SY7802_REG_ENABLE, enable_mask, val);
+
+	if (ret)
+		goto unlock;
+
+	chip->fled_strobe_used = curr;
+
+unlock:
+	mutex_unlock(&chip->mutex);
+	return ret;
+}
+
+static int sy7802_strobe_get(struct led_classdev_flash *fl_cdev, bool *state)
+{
+	struct sy7802_led *led = container_of(fl_cdev, struct sy7802_led, flash);
+	struct sy7802 *chip = led->chip;
+
+	mutex_lock(&chip->mutex);
+	*state = !!(chip->fled_strobe_used & BIT(led->led_no));
+	mutex_unlock(&chip->mutex);
+
+	return 0;
+}
+
+static int sy7802_timeout_set(struct led_classdev_flash *fl_cdev,
+			      u32 timeout)
+{
+	struct sy7802_led *led = container_of(fl_cdev, struct sy7802_led, flash);
+	struct led_flash_setting *s = &fl_cdev->timeout;
+	u32 val = (timeout - s->min) / s->step;
+	struct sy7802 *chip = led->chip;
+
+	return regmap_write(chip->regmap, SY7802_REG_FLASH_DURATION, val);
+}
+
+static int sy7802_fault_get(struct led_classdev_flash *fl_cdev, u32 *fault)
+{
+	struct sy7802_led *led = container_of(fl_cdev, struct sy7802_led, flash);
+	struct sy7802 *chip = led->chip;
+	u32 val, led_faults = 0;
+	int ret;
+
+	/* NOTE: reading register clears fault status */
+	ret = regmap_read(chip->regmap, SY7802_REG_FLAGS, &val);
+	if (ret)
+		return ret;
+
+	if (val & (SY7802_FLAG_FLASH_INPUT_VOLTAGE_LOW | SY7802_FLAG_INPUT_VOLTAGE_LOW))
+		led_faults |= LED_FAULT_INPUT_VOLTAGE;
+
+	if (val & SY7802_FLAG_THERMAL_SHUTDOWN)
+		led_faults |= LED_FAULT_OVER_TEMPERATURE;
+
+	if (val & SY7802_FLAG_TIMEOUT)
+		led_faults |= LED_FAULT_TIMEOUT;
+
+	*fault = led_faults;
+	return 0;
+}
+
+static const struct led_flash_ops sy7802_flash_ops = {
+	.flash_brightness_set = sy7802_flash_brightness_set,
+	.strobe_set = sy7802_strobe_set,
+	.strobe_get = sy7802_strobe_get,
+	.timeout_set = sy7802_timeout_set,
+	.fault_get = sy7802_fault_get,
+};
+
+static void sy7802_init_flash_brightness(struct led_classdev_flash *fl_cdev)
+{
+	struct led_flash_setting *s;
+
+	/* Init flash brightness setting */
+	s = &fl_cdev->brightness;
+	s->min = SY7802_FLASH_BRIGHTNESS_MIN;
+	s->max = SY7802_FLASH_BRIGHTNESS_MAX;
+	s->step = SY7802_FLASH_BRIGHTNESS_STEP;
+	s->val = SY7802_FLASH_BRIGHTNESS_DEFAULT;
+}
+
+static void sy7802_init_flash_timeout(struct led_classdev_flash *fl_cdev)
+{
+	struct led_flash_setting *s;
+
+	/* Init flash timeout setting */
+	s = &fl_cdev->timeout;
+	s->min = SY7802_TIMEOUT_MIN_US;
+	s->max = SY7802_TIMEOUT_MAX_US;
+	s->step = SY7802_TIMEOUT_STEPSIZE_US;
+	s->val = SY7802_TIMEOUT_DEFAULT_US;
+}
+
+static int sy7802_led_register(struct device *dev, struct sy7802_led *led,
+			       struct device_node *np)
+{
+	struct led_init_data init_data = {};
+	int ret;
+
+	init_data.fwnode = of_fwnode_handle(np);
+
+	ret = devm_led_classdev_flash_register_ext(dev, &led->flash, &init_data);
+	if (ret) {
+		dev_err(dev, "Couldn't register flash %d\n", led->led_no);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int sy7802_init_flash_properties(struct device *dev, struct sy7802_led *led,
+					struct device_node *np)
+{
+	struct led_classdev_flash *flash = &led->flash;
+	struct led_classdev *lcdev = &flash->led_cdev;
+	u32 sources[SY7802_MAX_LEDS];
+	int i, num, ret;
+
+	num = of_property_count_u32_elems(np, "led-sources");
+	if (num < 1) {
+		dev_err(dev, "Not specified or wrong number of led-sources\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32_array(np, "led-sources", sources, num);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < num; i++) {
+		if (sources[i] >= SY7802_MAX_LEDS)
+			return -EINVAL;
+		if (led->chip->leds_active & BIT(sources[i]))
+			return -EINVAL;
+		led->chip->leds_active |= BIT(sources[i]);
+	}
+
+	/* If both channels are specified in 'led-sources', joint flash output mode is used */
+	led->led_no = num == 2 ? SY7802_LED_JOINT : sources[0];
+
+	lcdev->max_brightness = SY7802_TORCH_BRIGHTNESS_MAX;
+	lcdev->brightness_set_blocking = sy7802_torch_brightness_set;
+	lcdev->flags |= LED_DEV_CAP_FLASH;
+
+	flash->ops = &sy7802_flash_ops;
+
+	sy7802_init_flash_brightness(flash);
+	sy7802_init_flash_timeout(flash);
+
+	return 0;
+}
+
+static int sy7802_chip_check(struct sy7802 *chip)
+{
+	struct device *dev = chip->dev;
+	u32 chipid;
+	int ret;
+
+	ret = regmap_read(chip->regmap, SY7802_REG_DEV_ID, &chipid);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to read chip ID\n");
+
+	if (chipid != SY7802_CHIP_ID)
+		return dev_err_probe(dev, -ENODEV, "Chip reported wrong ID: %x\n", chipid);
+
+	return 0;
+}
+
+static void sy7802_enable(struct sy7802 *chip)
+{
+	gpiod_set_value_cansleep(chip->enable_gpio, 1);
+	usleep_range(200, 300);
+}
+
+static void sy7802_disable(struct sy7802 *chip)
+{
+	gpiod_set_value_cansleep(chip->enable_gpio, 0);
+}
+
+static int sy7802_probe_dt(struct sy7802 *chip)
+{
+	struct device_node *np = dev_of_node(chip->dev), *child;
+	int i = 0;
+	int ret;
+
+	regmap_write(chip->regmap, SY7802_REG_ENABLE, SY7802_MODE_OFF);
+	regmap_write(chip->regmap, SY7802_REG_TORCH_BRIGHTNESS, LED_OFF);
+
+	for_each_available_child_of_node(np, child) {
+		struct sy7802_led *led = chip->leds + i;
+
+		led->chip = chip;
+		led->led_no = i;
+
+		ret = sy7802_init_flash_properties(chip->dev, led, child);
+		if (ret) {
+			of_node_put(child);
+			return ret;
+		}
+
+		ret = sy7802_led_register(chip->dev, led, child);
+		if (ret) {
+			of_node_put(child);
+			return ret;
+		}
+
+		i++;
+	}
+	return 0;
+}
+
+static const struct regmap_config sy7802_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0xff,
+	.cache_type = REGCACHE_MAPLE,
+	.reg_defaults = sy7802_regmap_defs,
+	.num_reg_defaults = ARRAY_SIZE(sy7802_regmap_defs),
+};
+
+static int sy7802_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct sy7802 *chip;
+	size_t count;
+	int ret;
+
+	count = device_get_child_node_count(dev);
+	if (!count || count > SY7802_MAX_LEDS)
+		return dev_err_probe(dev, -EINVAL,
+		       "No child node or node count over max led number %zu\n", count);
+
+	chip = devm_kzalloc(dev, struct_size(chip, leds, count), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->num_leds = count;
+
+	chip->dev = dev;
+	i2c_set_clientdata(client, chip);
+
+	chip->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+	ret = PTR_ERR_OR_ZERO(chip->enable_gpio);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to request enable gpio\n");
+
+	chip->vin_regulator = devm_regulator_get(dev, "vin");
+	ret = PTR_ERR_OR_ZERO(chip->vin_regulator);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to request regulator\n");
+
+	ret = regulator_enable(chip->vin_regulator);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to enable regulator\n");
+
+	chip->regmap = devm_regmap_init_i2c(client, &sy7802_regmap_config);
+	if (IS_ERR(chip->regmap)) {
+		regulator_disable(chip->vin_regulator);
+		return dev_err_probe(dev, PTR_ERR(chip->regmap),
+				    "Failed to allocate register map.\n");
+	}
+
+	ret = sy7802_probe_dt(chip);
+	if (ret < 0) {
+		regulator_disable(chip->vin_regulator);
+		return ret;
+	}
+
+	sy7802_enable(chip);
+
+	ret = sy7802_chip_check(chip);
+	if (ret) {
+		sy7802_disable(chip);
+		regulator_disable(chip->vin_regulator);
+		return ret;
+	}
+
+	mutex_init(&chip->mutex);
+
+	return ret;
+}
+
+static void sy7802_remove(struct i2c_client *client)
+{
+	struct sy7802 *chip = i2c_get_clientdata(client);
+
+	sy7802_disable(chip);
+
+	mutex_destroy(&chip->mutex);
+	regulator_disable(chip->vin_regulator);
+}
+
+static const struct of_device_id __maybe_unused sy7802_leds_match[] = {
+	{ .compatible = "silergy,sy7802", },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, sy7802_leds_match);
+
+static struct i2c_driver sy7802_driver = {
+	.driver = {
+		.name = "sy7802",
+		.of_match_table = of_match_ptr(sy7802_leds_match),
+	},
+	.probe = sy7802_probe,
+	.remove = sy7802_remove,
+};
+
+module_i2c_driver(sy7802_driver);
+
+MODULE_AUTHOR("André Apitzsch <git@apitzsch.eu>");
+MODULE_DESCRIPTION("Silergy SY7802 flash LED driver");
+MODULE_LICENSE("GPL");

-- 
2.44.0



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

* [PATCH 2/3] leds: sy7802: Add support for Silergy SY7802 flash LED controller
@ 2024-03-27 22:51   ` André Apitzsch
  0 siblings, 0 replies; 10+ messages in thread
From: André Apitzsch @ 2024-03-27 22:51 UTC (permalink / raw)
  To: Pavel Machek, Lee Jones, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Kees Cook, Gustavo A. R. Silva, Bjorn Andersson,
	Konrad Dybcio
  Cc: linux-leds, devicetree, linux-kernel, linux-hardening,
	linux-arm-msm, ~postmarketos/upstreaming, André Apitzsch

Add support for SY7802 flash LED controller. It can support up to 1.8A
flash current.

Signed-off-by: André Apitzsch <git@apitzsch.eu>
---
 drivers/leds/flash/Kconfig       |  11 +
 drivers/leds/flash/Makefile      |   1 +
 drivers/leds/flash/leds-sy7802.c | 532 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 544 insertions(+)

diff --git a/drivers/leds/flash/Kconfig b/drivers/leds/flash/Kconfig
index 809b6d98bb3e..f39f0bfe6eef 100644
--- a/drivers/leds/flash/Kconfig
+++ b/drivers/leds/flash/Kconfig
@@ -121,4 +121,15 @@ config LEDS_SGM3140
 	  This option enables support for the SGM3140 500mA Buck/Boost Charge
 	  Pump LED Driver.
 
+config LEDS_SY7802
+	tristate "LED support for the Silergy SY7802"
+	depends on I2C && OF
+	depends on GPIOLIB
+	select REGMAP_I2C
+	help
+	  This option enables support for the SY7802 flash LED controller.
+	  SY7802 includes torch and flash functions with programmable current.
+
+	  This driver can be built as a module, it will be called "leds-sy7802".
+
 endif # LEDS_CLASS_FLASH
diff --git a/drivers/leds/flash/Makefile b/drivers/leds/flash/Makefile
index 91d60a4b7952..48860eeced79 100644
--- a/drivers/leds/flash/Makefile
+++ b/drivers/leds/flash/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_LEDS_QCOM_FLASH)	+= leds-qcom-flash.o
 obj-$(CONFIG_LEDS_RT4505)	+= leds-rt4505.o
 obj-$(CONFIG_LEDS_RT8515)	+= leds-rt8515.o
 obj-$(CONFIG_LEDS_SGM3140)	+= leds-sgm3140.o
+obj-$(CONFIG_LEDS_SY7802)	+= leds-sy7802.o
diff --git a/drivers/leds/flash/leds-sy7802.c b/drivers/leds/flash/leds-sy7802.c
new file mode 100644
index 000000000000..c03a571b0e08
--- /dev/null
+++ b/drivers/leds/flash/leds-sy7802.c
@@ -0,0 +1,532 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Silergy SY7802 flash LED driver with I2C interface
+ *
+ * Copyright 2024 André Apitzsch <git@apitzsch.eu>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/led-class-flash.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#define SY7802_MAX_LEDS 2
+#define SY7802_LED_JOINT 2
+
+#define SY7802_REG_ENABLE		0x10
+#define SY7802_REG_TORCH_BRIGHTNESS	0xa0
+#define SY7802_REG_FLASH_BRIGHTNESS	0xb0
+#define SY7802_REG_FLASH_DURATION	0xc0
+#define SY7802_REG_FLAGS		0xd0
+#define SY7802_REG_CONFIG_1		0xe0
+#define SY7802_REG_CONFIG_2		0xf0
+#define SY7802_REG_VIN_MONITOR		0x80
+#define SY7802_REG_LAST_FLASH		0x81
+#define SY7802_REG_VLED_MONITOR		0x30
+#define SY7802_REG_ADC_DELAY		0x31
+#define SY7802_REG_DEV_ID		0xff
+
+#define SY7802_MODE_OFF		0
+#define SY7802_MODE_TORCH	2
+#define SY7802_MODE_FLASH	3
+#define SY7802_MODE_MASK	GENMASK(1, 0)
+
+#define SY7802_LEDS_SHIFT	3
+#define SY7802_LEDS_MASK(_id)	(BIT(_id) << SY7802_LEDS_SHIFT)
+#define SY7802_LEDS_MASK_ALL	(SY7802_LEDS_MASK(0) | SY7802_LEDS_MASK(1))
+
+#define SY7802_TORCH_CURRENT_SHIFT	3
+#define SY7802_TORCH_CURRENT_MASK(_id) \
+	(GENMASK(2, 0) << (SY7802_TORCH_CURRENT_SHIFT * (_id)))
+#define SY7802_TORCH_CURRENT_MASK_ALL \
+	(SY7802_TORCH_CURRENT_MASK(0) | SY7802_TORCH_CURRENT_MASK(1))
+
+#define SY7802_FLASH_CURRENT_SHIFT	4
+#define SY7802_FLASH_CURRENT_MASK(_id) \
+	(GENMASK(3, 0) << (SY7802_FLASH_CURRENT_SHIFT * (_id)))
+#define SY7802_FLASH_CURRENT_MASK_ALL \
+	(SY7802_FLASH_CURRENT_MASK(0) | SY7802_FLASH_CURRENT_MASK(1))
+
+#define SY7802_TIMEOUT_DEFAULT_US	512000U
+#define SY7802_TIMEOUT_MIN_US		32000U
+#define SY7802_TIMEOUT_MAX_US		1024000U
+#define SY7802_TIMEOUT_STEPSIZE_US	32000U
+
+#define SY7802_TORCH_BRIGHTNESS_MAX 8
+
+#define SY7802_FLASH_BRIGHTNESS_DEFAULT	14
+#define SY7802_FLASH_BRIGHTNESS_MIN	0
+#define SY7802_FLASH_BRIGHTNESS_MAX	15
+#define SY7802_FLASH_BRIGHTNESS_STEP	1
+
+#define SY7802_FLAG_TIMEOUT			(1 << 0)
+#define SY7802_FLAG_THERMAL_SHUTDOWN		(1 << 1)
+#define SY7802_FLAG_LED_FAULT			(1 << 2)
+#define SY7802_FLAG_TX1_INTERRUPT		(1 << 3)
+#define SY7802_FLAG_TX2_INTERRUPT		(1 << 4)
+#define SY7802_FLAG_LED_THERMAL_FAULT		(1 << 5)
+#define SY7802_FLAG_FLASH_INPUT_VOLTAGE_LOW	(1 << 6)
+#define SY7802_FLAG_INPUT_VOLTAGE_LOW		(1 << 7)
+
+#define SY7802_CHIP_ID	0x51
+
+static const struct reg_default sy7802_regmap_defs[] = {
+	{ SY7802_REG_ENABLE, SY7802_LEDS_MASK_ALL },
+	{ SY7802_REG_TORCH_BRIGHTNESS, 0x92 },
+	{ SY7802_REG_FLASH_BRIGHTNESS, SY7802_FLASH_BRIGHTNESS_DEFAULT |
+		SY7802_FLASH_BRIGHTNESS_DEFAULT << SY7802_FLASH_CURRENT_SHIFT },
+	{ SY7802_REG_FLASH_DURATION, 0x6f },
+	{ SY7802_REG_FLAGS, 0x0 },
+	{ SY7802_REG_CONFIG_1, 0x68 },
+	{ SY7802_REG_CONFIG_2, 0xf0 },
+};
+
+struct sy7802_led {
+	struct led_classdev_flash flash;
+	struct sy7802 *chip;
+	u8 led_no;
+};
+
+struct sy7802 {
+	struct mutex mutex;
+	struct device *dev;
+	struct regmap *regmap;
+
+	struct gpio_desc *enable_gpio;
+	struct regulator *vin_regulator;
+
+	unsigned int fled_strobe_used;
+	unsigned int fled_torch_used;
+	unsigned int leds_active;
+	int num_leds;
+	struct sy7802_led leds[] __counted_by(num_leds);
+};
+
+static int sy7802_torch_brightness_set(struct led_classdev *lcdev, enum led_brightness level)
+{
+	struct sy7802_led *led = container_of(lcdev, struct sy7802_led, flash.led_cdev);
+	u32 led_enable_mask = led->led_no == SY7802_LED_JOINT ? SY7802_LEDS_MASK_ALL :
+			      SY7802_LEDS_MASK(led->led_no);
+	u32 enable_mask = SY7802_MODE_MASK | led_enable_mask;
+	u32 val = level ? led_enable_mask : SY7802_MODE_OFF;
+	struct sy7802 *chip = led->chip;
+	u32 curr;
+	u32 mask;
+	int ret;
+
+	mutex_lock(&chip->mutex);
+
+	/*
+	 * There is only one set of flash control logic, and this flag is used to check if 'strobe'
+	 * is currently being used.
+	 */
+	if (chip->fled_strobe_used) {
+		dev_warn(chip->dev, "Please disable strobe first [%d]\n", chip->fled_strobe_used);
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	if (level)
+		curr = chip->fled_torch_used | BIT(led->led_no);
+	else
+		curr = chip->fled_torch_used & ~BIT(led->led_no);
+
+	if (curr)
+		val |= SY7802_MODE_TORCH;
+
+	/* Torch needs to be disabled first to apply new brightness */
+	ret = regmap_update_bits(chip->regmap, SY7802_REG_ENABLE, SY7802_MODE_MASK,
+				 SY7802_MODE_OFF);
+	if (ret)
+		goto unlock;
+
+	mask = led->led_no == SY7802_LED_JOINT ? SY7802_TORCH_CURRENT_MASK_ALL :
+	       SY7802_TORCH_CURRENT_MASK(led->led_no);
+
+	/* Register expects brightness between 0 and MAX_BRIGHTNESS - 1 */
+	if (level)
+		level -= 1;
+
+	level |= (level << SY7802_TORCH_CURRENT_SHIFT);
+
+	ret = regmap_update_bits(chip->regmap, SY7802_REG_TORCH_BRIGHTNESS, mask, level);
+	if (ret)
+		goto unlock;
+
+	ret = regmap_update_bits(chip->regmap, SY7802_REG_ENABLE, enable_mask, val);
+	if (ret)
+		goto unlock;
+
+	chip->fled_torch_used = curr;
+
+unlock:
+	mutex_unlock(&chip->mutex);
+	return ret;
+}
+
+static int sy7802_flash_brightness_set(struct led_classdev_flash *fl_cdev, u32 brightness)
+{
+	struct sy7802_led *led = container_of(fl_cdev, struct sy7802_led, flash);
+	struct led_flash_setting *s = &fl_cdev->brightness;
+	u32 val = (brightness - s->min) / s->step;
+	struct sy7802 *chip = led->chip;
+	u32 mask;
+	int ret;
+
+	val |= (val << SY7802_FLASH_CURRENT_SHIFT);
+	mask = led->led_no == SY7802_LED_JOINT ? SY7802_FLASH_CURRENT_MASK_ALL :
+	       SY7802_FLASH_CURRENT_MASK(led->led_no);
+
+	mutex_lock(&chip->mutex);
+	ret = regmap_update_bits(chip->regmap, SY7802_REG_FLASH_BRIGHTNESS, mask, val);
+	mutex_unlock(&chip->mutex);
+
+	return ret;
+}
+
+static int sy7802_strobe_set(struct led_classdev_flash *fl_cdev, bool state)
+{
+	struct sy7802_led *led = container_of(fl_cdev, struct sy7802_led, flash);
+	u32 led_enable_mask = led->led_no == SY7802_LED_JOINT ? SY7802_LEDS_MASK_ALL :
+			      SY7802_LEDS_MASK(led->led_no);
+	u32 enable_mask = SY7802_MODE_MASK | led_enable_mask;
+	u32 val = state ? led_enable_mask : SY7802_MODE_OFF;
+	struct sy7802 *chip = led->chip;
+	u32 curr;
+	int ret;
+
+	mutex_lock(&chip->mutex);
+
+	/*
+	 * There is only one set of flash control logic, and this flag is used to check if 'torch'
+	 * is currently being used.
+	 */
+	if (chip->fled_torch_used) {
+		dev_warn(chip->dev, "Please disable torch first [0x%x]\n", chip->fled_torch_used);
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	if (state)
+		curr = chip->fled_strobe_used | BIT(led->led_no);
+	else
+		curr = chip->fled_strobe_used & ~BIT(led->led_no);
+
+	if (curr)
+		val |= SY7802_MODE_FLASH;
+
+	ret = regmap_update_bits(chip->regmap, SY7802_REG_ENABLE, enable_mask, val);
+
+	if (ret)
+		goto unlock;
+
+	chip->fled_strobe_used = curr;
+
+unlock:
+	mutex_unlock(&chip->mutex);
+	return ret;
+}
+
+static int sy7802_strobe_get(struct led_classdev_flash *fl_cdev, bool *state)
+{
+	struct sy7802_led *led = container_of(fl_cdev, struct sy7802_led, flash);
+	struct sy7802 *chip = led->chip;
+
+	mutex_lock(&chip->mutex);
+	*state = !!(chip->fled_strobe_used & BIT(led->led_no));
+	mutex_unlock(&chip->mutex);
+
+	return 0;
+}
+
+static int sy7802_timeout_set(struct led_classdev_flash *fl_cdev,
+			      u32 timeout)
+{
+	struct sy7802_led *led = container_of(fl_cdev, struct sy7802_led, flash);
+	struct led_flash_setting *s = &fl_cdev->timeout;
+	u32 val = (timeout - s->min) / s->step;
+	struct sy7802 *chip = led->chip;
+
+	return regmap_write(chip->regmap, SY7802_REG_FLASH_DURATION, val);
+}
+
+static int sy7802_fault_get(struct led_classdev_flash *fl_cdev, u32 *fault)
+{
+	struct sy7802_led *led = container_of(fl_cdev, struct sy7802_led, flash);
+	struct sy7802 *chip = led->chip;
+	u32 val, led_faults = 0;
+	int ret;
+
+	/* NOTE: reading register clears fault status */
+	ret = regmap_read(chip->regmap, SY7802_REG_FLAGS, &val);
+	if (ret)
+		return ret;
+
+	if (val & (SY7802_FLAG_FLASH_INPUT_VOLTAGE_LOW | SY7802_FLAG_INPUT_VOLTAGE_LOW))
+		led_faults |= LED_FAULT_INPUT_VOLTAGE;
+
+	if (val & SY7802_FLAG_THERMAL_SHUTDOWN)
+		led_faults |= LED_FAULT_OVER_TEMPERATURE;
+
+	if (val & SY7802_FLAG_TIMEOUT)
+		led_faults |= LED_FAULT_TIMEOUT;
+
+	*fault = led_faults;
+	return 0;
+}
+
+static const struct led_flash_ops sy7802_flash_ops = {
+	.flash_brightness_set = sy7802_flash_brightness_set,
+	.strobe_set = sy7802_strobe_set,
+	.strobe_get = sy7802_strobe_get,
+	.timeout_set = sy7802_timeout_set,
+	.fault_get = sy7802_fault_get,
+};
+
+static void sy7802_init_flash_brightness(struct led_classdev_flash *fl_cdev)
+{
+	struct led_flash_setting *s;
+
+	/* Init flash brightness setting */
+	s = &fl_cdev->brightness;
+	s->min = SY7802_FLASH_BRIGHTNESS_MIN;
+	s->max = SY7802_FLASH_BRIGHTNESS_MAX;
+	s->step = SY7802_FLASH_BRIGHTNESS_STEP;
+	s->val = SY7802_FLASH_BRIGHTNESS_DEFAULT;
+}
+
+static void sy7802_init_flash_timeout(struct led_classdev_flash *fl_cdev)
+{
+	struct led_flash_setting *s;
+
+	/* Init flash timeout setting */
+	s = &fl_cdev->timeout;
+	s->min = SY7802_TIMEOUT_MIN_US;
+	s->max = SY7802_TIMEOUT_MAX_US;
+	s->step = SY7802_TIMEOUT_STEPSIZE_US;
+	s->val = SY7802_TIMEOUT_DEFAULT_US;
+}
+
+static int sy7802_led_register(struct device *dev, struct sy7802_led *led,
+			       struct device_node *np)
+{
+	struct led_init_data init_data = {};
+	int ret;
+
+	init_data.fwnode = of_fwnode_handle(np);
+
+	ret = devm_led_classdev_flash_register_ext(dev, &led->flash, &init_data);
+	if (ret) {
+		dev_err(dev, "Couldn't register flash %d\n", led->led_no);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int sy7802_init_flash_properties(struct device *dev, struct sy7802_led *led,
+					struct device_node *np)
+{
+	struct led_classdev_flash *flash = &led->flash;
+	struct led_classdev *lcdev = &flash->led_cdev;
+	u32 sources[SY7802_MAX_LEDS];
+	int i, num, ret;
+
+	num = of_property_count_u32_elems(np, "led-sources");
+	if (num < 1) {
+		dev_err(dev, "Not specified or wrong number of led-sources\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32_array(np, "led-sources", sources, num);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < num; i++) {
+		if (sources[i] >= SY7802_MAX_LEDS)
+			return -EINVAL;
+		if (led->chip->leds_active & BIT(sources[i]))
+			return -EINVAL;
+		led->chip->leds_active |= BIT(sources[i]);
+	}
+
+	/* If both channels are specified in 'led-sources', joint flash output mode is used */
+	led->led_no = num == 2 ? SY7802_LED_JOINT : sources[0];
+
+	lcdev->max_brightness = SY7802_TORCH_BRIGHTNESS_MAX;
+	lcdev->brightness_set_blocking = sy7802_torch_brightness_set;
+	lcdev->flags |= LED_DEV_CAP_FLASH;
+
+	flash->ops = &sy7802_flash_ops;
+
+	sy7802_init_flash_brightness(flash);
+	sy7802_init_flash_timeout(flash);
+
+	return 0;
+}
+
+static int sy7802_chip_check(struct sy7802 *chip)
+{
+	struct device *dev = chip->dev;
+	u32 chipid;
+	int ret;
+
+	ret = regmap_read(chip->regmap, SY7802_REG_DEV_ID, &chipid);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to read chip ID\n");
+
+	if (chipid != SY7802_CHIP_ID)
+		return dev_err_probe(dev, -ENODEV, "Chip reported wrong ID: %x\n", chipid);
+
+	return 0;
+}
+
+static void sy7802_enable(struct sy7802 *chip)
+{
+	gpiod_set_value_cansleep(chip->enable_gpio, 1);
+	usleep_range(200, 300);
+}
+
+static void sy7802_disable(struct sy7802 *chip)
+{
+	gpiod_set_value_cansleep(chip->enable_gpio, 0);
+}
+
+static int sy7802_probe_dt(struct sy7802 *chip)
+{
+	struct device_node *np = dev_of_node(chip->dev), *child;
+	int i = 0;
+	int ret;
+
+	regmap_write(chip->regmap, SY7802_REG_ENABLE, SY7802_MODE_OFF);
+	regmap_write(chip->regmap, SY7802_REG_TORCH_BRIGHTNESS, LED_OFF);
+
+	for_each_available_child_of_node(np, child) {
+		struct sy7802_led *led = chip->leds + i;
+
+		led->chip = chip;
+		led->led_no = i;
+
+		ret = sy7802_init_flash_properties(chip->dev, led, child);
+		if (ret) {
+			of_node_put(child);
+			return ret;
+		}
+
+		ret = sy7802_led_register(chip->dev, led, child);
+		if (ret) {
+			of_node_put(child);
+			return ret;
+		}
+
+		i++;
+	}
+	return 0;
+}
+
+static const struct regmap_config sy7802_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0xff,
+	.cache_type = REGCACHE_MAPLE,
+	.reg_defaults = sy7802_regmap_defs,
+	.num_reg_defaults = ARRAY_SIZE(sy7802_regmap_defs),
+};
+
+static int sy7802_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct sy7802 *chip;
+	size_t count;
+	int ret;
+
+	count = device_get_child_node_count(dev);
+	if (!count || count > SY7802_MAX_LEDS)
+		return dev_err_probe(dev, -EINVAL,
+		       "No child node or node count over max led number %zu\n", count);
+
+	chip = devm_kzalloc(dev, struct_size(chip, leds, count), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->num_leds = count;
+
+	chip->dev = dev;
+	i2c_set_clientdata(client, chip);
+
+	chip->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+	ret = PTR_ERR_OR_ZERO(chip->enable_gpio);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to request enable gpio\n");
+
+	chip->vin_regulator = devm_regulator_get(dev, "vin");
+	ret = PTR_ERR_OR_ZERO(chip->vin_regulator);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to request regulator\n");
+
+	ret = regulator_enable(chip->vin_regulator);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to enable regulator\n");
+
+	chip->regmap = devm_regmap_init_i2c(client, &sy7802_regmap_config);
+	if (IS_ERR(chip->regmap)) {
+		regulator_disable(chip->vin_regulator);
+		return dev_err_probe(dev, PTR_ERR(chip->regmap),
+				    "Failed to allocate register map.\n");
+	}
+
+	ret = sy7802_probe_dt(chip);
+	if (ret < 0) {
+		regulator_disable(chip->vin_regulator);
+		return ret;
+	}
+
+	sy7802_enable(chip);
+
+	ret = sy7802_chip_check(chip);
+	if (ret) {
+		sy7802_disable(chip);
+		regulator_disable(chip->vin_regulator);
+		return ret;
+	}
+
+	mutex_init(&chip->mutex);
+
+	return ret;
+}
+
+static void sy7802_remove(struct i2c_client *client)
+{
+	struct sy7802 *chip = i2c_get_clientdata(client);
+
+	sy7802_disable(chip);
+
+	mutex_destroy(&chip->mutex);
+	regulator_disable(chip->vin_regulator);
+}
+
+static const struct of_device_id __maybe_unused sy7802_leds_match[] = {
+	{ .compatible = "silergy,sy7802", },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, sy7802_leds_match);
+
+static struct i2c_driver sy7802_driver = {
+	.driver = {
+		.name = "sy7802",
+		.of_match_table = of_match_ptr(sy7802_leds_match),
+	},
+	.probe = sy7802_probe,
+	.remove = sy7802_remove,
+};
+
+module_i2c_driver(sy7802_driver);
+
+MODULE_AUTHOR("André Apitzsch <git@apitzsch.eu>");
+MODULE_DESCRIPTION("Silergy SY7802 flash LED driver");
+MODULE_LICENSE("GPL");

-- 
2.44.0


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

* [PATCH 3/3] arm64: dts: qcom: msm8939-longcheer-l9100: Add rear flash
  2024-03-27 22:51 ` André Apitzsch
@ 2024-03-27 22:51   ` André Apitzsch
  -1 siblings, 0 replies; 10+ messages in thread
From: André Apitzsch via B4 Relay @ 2024-03-27 22:51 UTC (permalink / raw)
  To: Pavel Machek, Lee Jones, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Kees Cook, Gustavo A. R. Silva, Bjorn Andersson,
	Konrad Dybcio
  Cc: linux-leds, devicetree, linux-kernel, linux-hardening,
	linux-arm-msm, ~postmarketos/upstreaming, André Apitzsch

From: André Apitzsch <git@apitzsch.eu>

The phone has a Silergy SY7802 flash LED controller.

Signed-off-by: André Apitzsch <git@apitzsch.eu>
---
 .../boot/dts/qcom/msm8939-longcheer-l9100.dts      | 26 ++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/msm8939-longcheer-l9100.dts b/arch/arm64/boot/dts/qcom/msm8939-longcheer-l9100.dts
index e3404c4455cf..528737929274 100644
--- a/arch/arm64/boot/dts/qcom/msm8939-longcheer-l9100.dts
+++ b/arch/arm64/boot/dts/qcom/msm8939-longcheer-l9100.dts
@@ -159,6 +159,25 @@ led@2 {
 			};
 		};
 	};
+
+	flash-led-controller@53 {
+		compatible = "silergy,sy7802";
+		reg = <0x53>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		enable-gpios = <&tlmm 16 GPIO_ACTIVE_HIGH>;
+
+		pinctrl-0 = <&camera_rear_flash_default>;
+		pinctrl-names = "default";
+
+		led@0 {
+			reg = <0>;
+			function = LED_FUNCTION_FLASH;
+			color = <LED_COLOR_ID_WHITE>;
+			led-sources = <0>, <1>;
+		};
+	};
 };
 
 &blsp_i2c3 {
@@ -318,6 +337,13 @@ camera_front_flash_default: camera-front-flash-default-state {
 		bias-disable;
 	};
 
+	camera_rear_flash_default: camera-rear-flash-default-state {
+		pins = "gpio9", "gpio16", "gpio51";
+		function = "gpio";
+		drive-strength = <2>;
+		bias-disable;
+	};
+
 	gpio_hall_sensor_default: gpio-hall-sensor-default-state {
 		pins = "gpio20";
 		function = "gpio";

-- 
2.44.0



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

* [PATCH 3/3] arm64: dts: qcom: msm8939-longcheer-l9100: Add rear flash
@ 2024-03-27 22:51   ` André Apitzsch
  0 siblings, 0 replies; 10+ messages in thread
From: André Apitzsch @ 2024-03-27 22:51 UTC (permalink / raw)
  To: Pavel Machek, Lee Jones, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Kees Cook, Gustavo A. R. Silva, Bjorn Andersson,
	Konrad Dybcio
  Cc: linux-leds, devicetree, linux-kernel, linux-hardening,
	linux-arm-msm, ~postmarketos/upstreaming, André Apitzsch

The phone has a Silergy SY7802 flash LED controller.

Signed-off-by: André Apitzsch <git@apitzsch.eu>
---
 .../boot/dts/qcom/msm8939-longcheer-l9100.dts      | 26 ++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/msm8939-longcheer-l9100.dts b/arch/arm64/boot/dts/qcom/msm8939-longcheer-l9100.dts
index e3404c4455cf..528737929274 100644
--- a/arch/arm64/boot/dts/qcom/msm8939-longcheer-l9100.dts
+++ b/arch/arm64/boot/dts/qcom/msm8939-longcheer-l9100.dts
@@ -159,6 +159,25 @@ led@2 {
 			};
 		};
 	};
+
+	flash-led-controller@53 {
+		compatible = "silergy,sy7802";
+		reg = <0x53>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		enable-gpios = <&tlmm 16 GPIO_ACTIVE_HIGH>;
+
+		pinctrl-0 = <&camera_rear_flash_default>;
+		pinctrl-names = "default";
+
+		led@0 {
+			reg = <0>;
+			function = LED_FUNCTION_FLASH;
+			color = <LED_COLOR_ID_WHITE>;
+			led-sources = <0>, <1>;
+		};
+	};
 };
 
 &blsp_i2c3 {
@@ -318,6 +337,13 @@ camera_front_flash_default: camera-front-flash-default-state {
 		bias-disable;
 	};
 
+	camera_rear_flash_default: camera-rear-flash-default-state {
+		pins = "gpio9", "gpio16", "gpio51";
+		function = "gpio";
+		drive-strength = <2>;
+		bias-disable;
+	};
+
 	gpio_hall_sensor_default: gpio-hall-sensor-default-state {
 		pins = "gpio20";
 		function = "gpio";

-- 
2.44.0


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

* Re: [PATCH 1/3] dt-bindings: leds: Add Silergy SY7802 flash LED
  2024-03-27 22:51   ` André Apitzsch
  (?)
@ 2024-03-28  0:44   ` Rob Herring
  -1 siblings, 0 replies; 10+ messages in thread
From: Rob Herring @ 2024-03-28  0:44 UTC (permalink / raw)
  To: André Apitzsch
  Cc: ~postmarketos/upstreaming, linux-kernel, Kees Cook,
	linux-arm-msm, Lee Jones, Konrad Dybcio, Pavel Machek,
	linux-hardening, Conor Dooley, Krzysztof Kozlowski,
	Bjorn Andersson, linux-leds, devicetree, Gustavo A. R. Silva


On Wed, 27 Mar 2024 23:51:33 +0100, André Apitzsch wrote:
> Document Silergy SY7802 flash LED driver devicetree bindings.
> 
> Signed-off-by: André Apitzsch <git@apitzsch.eu>
> ---
>  .../devicetree/bindings/leds/silergy,sy7802.yaml   | 96 ++++++++++++++++++++++
>  1 file changed, 96 insertions(+)
> 

My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):

yamllint warnings/errors:

dtschema/dtc warnings/errors:
Documentation/devicetree/bindings/leds/silergy,sy7802.example.dts:23.13-26: Warning (reg_format): /example-0/flash-led-controller@53:reg: property has invalid length (4 bytes) (#address-cells == 1, #size-cells == 1)
Documentation/devicetree/bindings/leds/silergy,sy7802.example.dtb: Warning (pci_device_reg): Failed prerequisite 'reg_format'
Documentation/devicetree/bindings/leds/silergy,sy7802.example.dtb: Warning (pci_device_bus_num): Failed prerequisite 'reg_format'
Documentation/devicetree/bindings/leds/silergy,sy7802.example.dtb: Warning (simple_bus_reg): Failed prerequisite 'reg_format'
Documentation/devicetree/bindings/leds/silergy,sy7802.example.dtb: Warning (i2c_bus_reg): Failed prerequisite 'reg_format'
Documentation/devicetree/bindings/leds/silergy,sy7802.example.dtb: Warning (spi_bus_reg): Failed prerequisite 'reg_format'

doc reference errors (make refcheckdocs):

See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20240327-sy7802-v1-1-db74ab32faaf@apitzsch.eu

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.


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

* Re: [PATCH 1/3] dt-bindings: leds: Add Silergy SY7802 flash LED
  2024-03-27 22:51   ` André Apitzsch
  (?)
  (?)
@ 2024-03-28 18:50   ` Rob Herring
  -1 siblings, 0 replies; 10+ messages in thread
From: Rob Herring @ 2024-03-28 18:50 UTC (permalink / raw)
  To: André Apitzsch
  Cc: Pavel Machek, Lee Jones, Krzysztof Kozlowski, Conor Dooley,
	Kees Cook, Gustavo A. R. Silva, Bjorn Andersson, Konrad Dybcio,
	linux-leds, devicetree, linux-kernel, linux-hardening,
	linux-arm-msm, ~postmarketos/upstreaming

On Wed, Mar 27, 2024 at 11:51:33PM +0100, André Apitzsch wrote:
> Document Silergy SY7802 flash LED driver devicetree bindings.
> 
> Signed-off-by: André Apitzsch <git@apitzsch.eu>
> ---
>  .../devicetree/bindings/leds/silergy,sy7802.yaml   | 96 ++++++++++++++++++++++
>  1 file changed, 96 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/leds/silergy,sy7802.yaml b/Documentation/devicetree/bindings/leds/silergy,sy7802.yaml
> new file mode 100644
> index 000000000000..d32efac8baa6
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/leds/silergy,sy7802.yaml
> @@ -0,0 +1,96 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/leds/silergy,sy7802.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Silergy SY7802 1800mA Boost Charge Pump LED Driver
> +
> +maintainers:
> +  - André Apitzsch <git@apitzsch.eu>
> +
> +description: |
> +  The SY7802 is a current-regulated charge pump which can regulate two current
> +  levels for Flash and Torch modes.
> +
> +  The SY7802 is a high-current synchronous boost converter with 2-channel
> +  high side current sources. Each channel is able to deliver 900mA current.
> +
> +properties:
> +  compatible:
> +    enum:
> +      - silergy,sy7802
> +
> +  reg:
> +    maxItems: 1
> +
> +  enable-gpios:
> +    maxItems: 1
> +    description: A connection to the 'EN' pin.
> +
> +  flash-gpios:
> +    maxItems: 1
> +    description: A connection to the 'FLEN' pin.
> +
> +  vin-supply:
> +    description: Regulator providing power to the 'VIN' pin.
> +
> +  "#address-cells":
> +    const: 1
> +
> +  "#size-cells":
> +    const: 0
> +
> +patternProperties:
> +  "^led@[0-1]$":
> +    type: object
> +    $ref: common.yaml#
> +    unevaluatedProperties: false
> +
> +    properties:
> +      reg:
> +        description: Index of the LED.
> +        minimum: 0
> +        maximum: 1
> +
> +      led-sources:
> +        allOf:

Don't need allOf here.

> +          - minItems: 1
> +            maxItems: 2
> +            items:
> +              minimum: 0
> +              maximum: 1
> +
> +    required:
> +      - reg
> +      - led-sources
> +
> +required:
> +  - compatible
> +  - reg
> +  - "#address-cells"
> +  - "#size-cells"
> +  - enable-gpios
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/gpio/gpio.h>
> +    #include <dt-bindings/leds/common.h>
> +
> +    flash-led-controller@53 {

This needs to go under an appropriate bus node to fix the errors. i2c 
presumably.

> +        compatible = "silergy,sy7802";
> +        reg = <0x53>;
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        enable-gpios = <&tlmm 16 GPIO_ACTIVE_HIGH>;
> +
> +        led@0 {
> +            reg = <0>;
> +            function = LED_FUNCTION_FLASH;
> +            color = <LED_COLOR_ID_WHITE>;
> +            led-sources = <0>, <1>;
> +        };
> +    };
> 
> -- 
> 2.44.0
> 

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

end of thread, other threads:[~2024-03-28 18:50 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-03-27 22:51 [PATCH 0/3] Add sy7802 flash led driver André Apitzsch via B4 Relay
2024-03-27 22:51 ` André Apitzsch
2024-03-27 22:51 ` [PATCH 1/3] dt-bindings: leds: Add Silergy SY7802 flash LED André Apitzsch via B4 Relay
2024-03-27 22:51   ` André Apitzsch
2024-03-28  0:44   ` Rob Herring
2024-03-28 18:50   ` Rob Herring
2024-03-27 22:51 ` [PATCH 2/3] leds: sy7802: Add support for Silergy SY7802 flash LED controller André Apitzsch via B4 Relay
2024-03-27 22:51   ` André Apitzsch
2024-03-27 22:51 ` [PATCH 3/3] arm64: dts: qcom: msm8939-longcheer-l9100: Add rear flash André Apitzsch via B4 Relay
2024-03-27 22:51   ` André Apitzsch

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.