Skip to contentSkip to conversation

Samsung Expressatt: Camera Flash

PATCH Superseded Rudraksha Gupta 3 participants 6 messages Mar 26

Subsystems (4)

v3 of 7 Superseded Latest is v7 →

lore
Share & tools
lore
#0 lore raw

v3 cover Samsung Expressatt: Camera Flash

This small series adds camera flash to an existing similar mainline driver and adds it to the Samsung Expressatt's DTS

// Tests

// # Navigate to LED
sudo su
cd /sys/class/leds/white:flash

// # Should stay at dim brightness
echo 1 > brightness
echo 1 > brightness
echo 1 > brightness
echo 1 > brightness
echo 0 > brightness # LED_OFF

// # Max Brightness
echo 50 > brightness
echo 0 > brightness # LED_OFF
echo 99 > brightness
echo 0 > brightness # LED_OFF
echo 1000 > brightness
echo 0 > brightness # LED_OFF
echo 100 > brightness
echo 0 > brightness # LED_OFF

// # Should increase in brightness
for i in $(seq 1 16); do echo $i > brightness; sleep 1; done
echo 0 > brightness # LED_OFF

// # Test flash strobe (rt8515_led_flash_strobe_set)

cat max_flash_timeout          # check max
echo 200000 > flash_timeout    # 200ms
echo 1 > flash_strobe          # strobe ON → brightness_commit + timer
cat flash_strobe               # should read 1, then 0 after timeout

sleep 1
cat flash_strobe # should be 0 (timer fired)

// # Test manual strobe cancel
echo 1 > flash_strobe ; echo 0 > flash_strobe # immediate off

// # Check regulator error handling
dmesg | tail -20 # look for any "failed to turn off LED" msgs

Downstream reference:

---
Changes in v3:
- DTS:
- Renamed and reordered nodes
- Driver:
- Use regulator_is_enabled() instead of reg_enabled
- remove ent xor vin check
- remove rt->reg == -ENODEV check
- rename functions to reflect what they do and added ret's
- Fixed: LED was increasing in brightness when setting the same
brightness multiple times
- Link to v2: https://lore.kernel.org/r/[email protected]

Changes in v2:
- dt-bindings: Explain the hardware and not the driver
- **/*: Use vin-supply instead of unlock-gpio
- expressatt DTS: Reorder pinctrl-*
- expressatt DTS: Define rfs-ohms to a default (couldn't find
information about this)
- Link to v1: https://lore.kernel.org/r/[email protected]

---
Rudraksha Gupta (3):
dt-bindings: leds: rt8515: Support single-GPIO flash ICs with vin supply
leds: flash: rt8515: Support single-GPIO flash ICs with vin supply
ARM: dts: qcom: msm8960: expressatt: Add camera flash

 .../devicetree/bindings/leds/richtek,rt8515.yaml   | 34 +++++++-
 .../dts/qcom/qcom-msm8960-samsung-expressatt.dts   | 43 +++++++++++
 drivers/leds/flash/leds-rt8515.c                   | 90 +++++++++++++++++-----
 3 files changed, 148 insertions(+), 19 deletions(-)

---
base-commit: e9ec05addd1a067fc7cb218f20ecdc1b1b0898c0
change-id: 20260306-expressatt_camera_flash-13c15a7427aa
prerequisite-message-id: <[email protected]>
prerequisite-patch-id: 8de4de7909722ccaf385c4224f25a623eaa72c28

Best regards,

Signature · 1 line
Rudraksha Gupta <[email protected]>
#1 lore raw

PATCH v3 1/3 dt-bindings: leds: rt8515: Support single-GPIO flash ICs with vin supply

Some flash ICs use the same one-wire pulse-count protocol as the RT8515 but have only a single enable line for both flash and torch modes, plus an optional input voltage supply (e.g. a GPIO-controlled fixed regulator) that gates power to the chip.

Make ent-gpios optional and add a vin-supply property to support these variants. Add a oneOf constraint requiring exactly one of ent-gpios or vin-supply. Add a binding example showing the single-GPIO configuration with an input supply.

Assisted-by Claude:claude-opus-4.6
---
 .../devicetree/bindings/leds/richtek,rt8515.yaml   | 34 +++++++++++++++++++++-
 1 file changed, 33 insertions(+), 1 deletion(-)
Documentation/devicetree/bindings/leds/richtek,rt8515.yaml +33 -1
index 0356371a6b01..ab3c5139132c 100644
@@ -15,6 +15,10 @@ description: |
   current for each mode is defined in hardware using two resistors
   RFS and RTS.
 
+  Some flash ICs use the same one-wire pulse-count protocol but have
+  only a single enable line for both flash and torch modes. For these
+  single-channel variants, only enf-gpios is needed.
+
 properties:
   compatible:
     const: richtek,rt8515
@@ -26,6 +30,11 @@ properties:
   ent-gpios:
     maxItems: 1
     description: A connection to the 'ENT' (enable torch) pin.
+      Not present on single-channel flash ICs that use only one enable
+      line for both flash and torch modes.
+
+  vin-supply:
+    description: Optional input supply for the flash IC.
 
   richtek,rfs-ohms:
     minimum: 7680
@@ -81,10 +90,15 @@ properties:
 
 required:
   - compatible
-  - ent-gpios
   - enf-gpios
   - led
 
+oneOf:
+  - required:
+      - ent-gpios
+  - required:
+      - vin-supply
+
 additionalProperties: false
 
 examples:
@@ -108,4 +122,22 @@ examples:
         };
     };
 
+  - |
+    /* Single-channel flash IC with input supply */
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/leds/common.h>
+
+    led-controller {
+        compatible = "richtek,rt8515";
+        enf-gpios = <&tlmm 3 GPIO_ACTIVE_HIGH>;
+        vin-supply = <&flash_reg>;
+        richtek,rfs-ohms = <16000>;
+
+        led {
+            function = LED_FUNCTION_FLASH;
+            color = <LED_COLOR_ID_WHITE>;
+            flash-max-timeout-us = <250000>;
+        };
+    };
+
 ...
Signature · 1 line
2.53.0
#2 lore raw

PATCH v3 2/3 leds: flash: rt8515: Support single-GPIO flash ICs with vin supply

In reply to Rudraksha Gupta

Extend the RT8515 driver to support flash ICs that use only a single GPIO for both flash and torch modes (no separate ENT pin), with an optional vin regulator that gates power to the flash IC.

When vin-supply is provided, the driver enables the regulator before activating the LED and disables it when turning off.

Make ent-gpios optional and validate at probe time that exactly one of ent-gpios or vin-supply is provided. When ent-gpios is absent, the driver uses enf-gpios for both flash and torch brightness control.

Assisted-by Claude:claude-opus-4.6
---
 drivers/leds/flash/leds-rt8515.c | 90 ++++++++++++++++++++++++++++++++--------
 1 file changed, 72 insertions(+), 18 deletions(-)
drivers/leds/flash/leds-rt8515.c +72 -18
index f6b439674c03..739cac8789c8 100644
@@ -63,16 +63,44 @@ static struct rt8515 *to_rt8515(struct led_classdev_flash *fled)
 	return container_of(fled, struct rt8515, fled);
 }
 
-static void rt8515_gpio_led_off(struct rt8515 *rt)
+static int rt8515_led_off(struct rt8515 *rt)
 {
+	int ret;
+
 	gpiod_set_value(rt->enable_flash, 0);
-	gpiod_set_value(rt->enable_torch, 0);
+	if (rt->enable_torch)
+		gpiod_set_value(rt->enable_torch, 0);
+
+	/* Disable regulator */
+	ret = regulator_is_enabled(rt->reg);
+	if (ret < 0)
+		return ret;
+	if (ret > 0)
+		return regulator_disable(rt->reg);
+
+	return 0;
 }
 
-static void rt8515_gpio_brightness_commit(struct gpio_desc *gpiod,
-					  int brightness)
+static int rt8515_set_flash_brightness(struct rt8515 *rt,
+					 struct gpio_desc *gpiod,
+					 int brightness)
 {
 	int i;
+	int ret;
+
+	/*
+	 * Reset the IC to start brightness from zero,
+	 * then re-enable and pulse to the desired level.
+	 */
+	ret = rt8515_led_off(rt);
+	if (ret)
+		return ret;
+	/* IC needs time to reset its brightness counter */
+	usleep_range(100, 200);
+	/* Enable regulator */
+	ret = regulator_enable(rt->reg);
+	if (ret)
+		return ret;
 
 	/*
 	 * Toggling a GPIO line with a small delay increases the
@@ -84,31 +112,41 @@ static void rt8515_gpio_brightness_commit(struct gpio_desc *gpiod,
 		gpiod_set_value(gpiod, 1);
 		udelay(1);
 	}
+
+	return 0;
 }
 
 /* This is setting the torch light level */
-static int rt8515_led_brightness_set(struct led_classdev *led,
+static int rt8515_set_brightness(struct led_classdev *led,
 				     enum led_brightness brightness)
 {
 	struct led_classdev_flash *fled = lcdev_to_flcdev(led);
 	struct rt8515 *rt = to_rt8515(fled);
+	int ret = 0;
 
 	mutex_lock(&rt->lock);
 
 	if (brightness == LED_OFF) {
 		/* Off */
-		rt8515_gpio_led_off(rt);
+		ret = rt8515_led_off(rt);
+		if (ret)
+			goto out;
 	} else if (brightness < RT8515_TORCH_MAX) {
-		/* Step it up to movie mode brightness using the flash pin */
-		rt8515_gpio_brightness_commit(rt->enable_torch, brightness);
+		/* Step it up to movie mode brightness */
+		ret = rt8515_set_flash_brightness(rt, rt->enable_torch ?
+				rt->enable_torch : rt->enable_flash, brightness);
+		if (ret)
+			goto out;
 	} else {
 		/* Max torch brightness requested */
-		gpiod_set_value(rt->enable_torch, 1);
+		gpiod_set_value(rt->enable_torch ? rt->enable_torch :
+				rt->enable_flash, 1);
 	}
 
+out:
 	mutex_unlock(&rt->lock);
 
-	return 0;
+	return ret;
 }
 
 static int rt8515_led_flash_strobe_set(struct led_classdev_flash *fled,
@@ -117,27 +155,33 @@ static int rt8515_led_flash_strobe_set(struct led_classdev_flash *fled,
 	struct rt8515 *rt = to_rt8515(fled);
 	struct led_flash_setting *timeout = &fled->timeout;
 	int brightness = rt->flash_max_intensity;
+	int ret = 0;
 
 	mutex_lock(&rt->lock);
 
 	if (state) {
 		/* Enable LED flash mode and set brightness */
-		rt8515_gpio_brightness_commit(rt->enable_flash, brightness);
+		ret = rt8515_set_flash_brightness(rt, rt->enable_flash, brightness);
+		if (ret)
+			goto out;
 		/* Set timeout */
 		mod_timer(&rt->powerdown_timer,
 			  jiffies + usecs_to_jiffies(timeout->val));
 	} else {
 		timer_delete_sync(&rt->powerdown_timer);
 		/* Turn the LED off */
-		rt8515_gpio_led_off(rt);
+		ret = rt8515_led_off(rt);
+		if (ret)
+			goto out;
 	}
 
 	fled->led_cdev.brightness = LED_OFF;
 	/* After this the torch LED will be disabled */
 
+out:
 	mutex_unlock(&rt->lock);
 
-	return 0;
+	return ret;
 }
 
 static int rt8515_led_flash_strobe_get(struct led_classdev_flash *fled,
@@ -166,9 +210,12 @@ static const struct led_flash_ops rt8515_flash_ops = {
 static void rt8515_powerdown_timer(struct timer_list *t)
 {
 	struct rt8515 *rt = timer_container_of(rt, t, powerdown_timer);
+	int ret;
 
 	/* Turn the LED off */
-	rt8515_gpio_led_off(rt);
+	ret = rt8515_led_off(rt);
+	if (ret)
+		dev_err(rt->dev, "failed to turn off LED (%d)\n", ret);
 }
 
 static void rt8515_init_flash_timeout(struct rt8515 *rt)
@@ -298,12 +345,18 @@ static int rt8515_probe(struct platform_device *pdev)
 		return dev_err_probe(dev, PTR_ERR(rt->enable_flash),
 				     "cannot get ENF (enable flash) GPIO\n");
 
-	/* ENT - Enable Torch line */
-	rt->enable_torch = devm_gpiod_get(dev, "ent", GPIOD_OUT_LOW);
+	/* ENT - Enable Torch line (optional for single-GPIO flash ICs) */
+	rt->enable_torch = devm_gpiod_get_optional(dev, "ent", GPIOD_OUT_LOW);
 	if (IS_ERR(rt->enable_torch))
 		return dev_err_probe(dev, PTR_ERR(rt->enable_torch),
 				     "cannot get ENT (enable torch) GPIO\n");
 
+	/* Optional VIN supply */
+	rt->reg = devm_regulator_get(dev, "vin");
+	if (IS_ERR(rt->reg))
+		return dev_err_probe(dev, PTR_ERR(rt->reg),
+				     "failed to get vin supply\n");
+
 	child = device_get_next_child_node(dev, NULL);
 	if (!child) {
 		dev_err(dev,
@@ -333,8 +386,9 @@ static int rt8515_probe(struct platform_device *pdev)
 
 	fled->ops = &rt8515_flash_ops;
 
-	led->max_brightness = rt->torch_max_intensity;
-	led->brightness_set_blocking = rt8515_led_brightness_set;
+	led->max_brightness = rt->enable_torch ?
+		rt->torch_max_intensity : rt->flash_max_intensity;
+	led->brightness_set_blocking = rt8515_set_brightness;
 	led->flags |= LED_CORE_SUSPENDRESUME | LED_DEV_CAP_FLASH;
 
 	mutex_init(&rt->lock);
Signature · 1 line
2.53.0
#3 lore raw

Re: [PATCH v3 2/3] leds: flash: rt8515: Support single-GPIO flash ICs with vin supply

Hi Rudraksha,

this is starting to look good, some remaining snags below:

drivers/leds/flash/leds-rt8515.c static struct rt8515 *to_rt8515(struct led_classdev_flash *fled) · line 86

Quoted 5 lines · On Thu, Mar 26, 2026 at 8:57 AM Rudraksha Gupta via B4 Relay…
drivers/leds/flash/leds-rt8515.c +3 -2
-static void rt8515_gpio_brightness_commit(struct gpio_desc *gpiod,
-                                         int brightness)
+static int rt8515_set_flash_brightness(struct rt8515 *rt,
+                                        struct gpio_desc *gpiod,
+                                        int brightness)

Do not change the name of the function, it's confusing. This is also used for setting the brightness of the torch, and after this change it looks like the function is just for the flash.

drivers/leds/flash/leds-rt8515.c static void rt8515_gpio_brightness_commit(struct gpio_desc *gpiod, · line 139

Quoted 8 lines
        } else if (brightness < RT8515_TORCH_MAX) {
drivers/leds/flash/leds-rt8515.c +5 -2
-               /* Step it up to movie mode brightness using the flash pin */
-               rt8515_gpio_brightness_commit(rt->enable_torch, brightness);
+               /* Step it up to movie mode brightness */
+               ret = rt8515_set_flash_brightness(rt, rt->enable_torch ?
+                               rt->enable_torch : rt->enable_flash, brightness);
+               if (ret)
+                       goto out;

Add a comment befor this that if we don't have a separate torch enable pin connected, we use the flash pin also for the torch.

Yours,
Linus Walleij

#4 lore raw

PATCH v3 3/3 ARM: dts: qcom: msm8960: expressatt: Add camera flash

In reply to Rudraksha Gupta

Add camera flash support for the Samsung Galaxy Express (expressatt).

The flash IC uses a one-wire pulse-count protocol on GPIO 3, powered by a GPIO-controlled fixed regulator on PMIC MPP 4. The regulator is modeled as a regulator-fixed node and supplied to the flash IC via vin-supply.

Downstream references:

Assisted-by Claude:claude-opus-4.6
---
 .../dts/qcom/qcom-msm8960-samsung-expressatt.dts   | 43 ++++++++++++++++++++++
 1 file changed, 43 insertions(+)
arch/arm/boot/dts/qcom/qcom-msm8960-samsung-expressatt.dts +43 -0
index c4b98af6955d..35514fd53e3d 100644
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <dt-bindings/input/input.h>
+#include <dt-bindings/leds/common.h>
 #include <dt-bindings/reset/qcom,gcc-msm8960.h>
 
 #include "qcom-msm8960.dtsi"
@@ -61,6 +62,32 @@ touchkey_enable: touchkey-enable {
 		regulator-boot-on;
 	};
 
+	vreg_flash: regulator-flash {
+		compatible = "regulator-fixed";
+		regulator-name = "VREG_FLASH_3P3";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		gpio = <&pm8921_mpps 4 GPIO_ACTIVE_HIGH>;
+		enable-active-high;
+		pinctrl-0 = <&flash_led_unlock>;
+		pinctrl-names = "default";
+	};
+
+	led-controller {
+		compatible = "richtek,rt8515";
+		enf-gpios = <&tlmm 3 GPIO_ACTIVE_HIGH>;
+		vin-supply = <&vreg_flash>;
+		richtek,rfs-ohms = <16000>;
+		pinctrl-0 = <&cam_flash_en>;
+		pinctrl-names = "default";
+
+		led {
+			function = LED_FUNCTION_FLASH;
+			color = <LED_COLOR_ID_WHITE>;
+			flash-max-timeout-us = <250000>;
+		};
+	};
+
 	i2c-gpio-touchkey {
 		compatible = "i2c-gpio";
 		#address-cells = <1>;
@@ -172,6 +199,13 @@ touchscreen@4a {
 };
 
 &tlmm {
+	cam_flash_en: cam-flash-en-state {
+		pins = "gpio3";
+		function = "gpio";
+		drive-strength = <16>;
+		bias-pull-down;
+	};
+
 	spi1_default: spi1-default-state {
 		mosi-pins {
 			pins = "gpio6";
@@ -572,3 +606,12 @@ magnetometer@2e {
 		/* TODO: Figure out Mount Matrix */
 	};
 };
+
+&pm8921_mpps {
+	flash_led_unlock: flash-led-unlock-state {
+		pins = "mpp4";
+		function = "digital";
+		output-low;
+		power-source = <PM8921_GPIO_S4>;
+	};
+};
Signature · 1 line
2.53.0
Reviewed-by on v3 3/3
#5 lore raw

Re: [PATCH v3 3/3] ARM: dts: qcom: msm8960: expressatt: Add camera flash

Quoted 9 lines · On 3/26/26 8:57 AM, Rudraksha Gupta via B4 Relay wrote:

From: Rudraksha Gupta <[email protected]>

Add camera flash support for the Samsung Galaxy Express (expressatt).

The flash IC uses a one-wire pulse-count protocol on GPIO 3, powered by a GPIO-controlled fixed regulator on PMIC MPP 4. The regulator is modeled as a regulator-fixed node and supplied to the flash IC via vin-supply.

Quoted 8 lines

Downstream references:

Assisted-by Claude:claude-opus-4.6

---

IDK what to do with the AI thing, but for the change itself:

Konrad