* [PATCH v5 0/3] Samsung Expressatt: Camera Flash
@ 2026-05-03 21:43 Rudraksha Gupta via B4 Relay
2026-05-03 21:43 ` [PATCH v5 1/3] dt-bindings: leds: rt8515: Support single-GPIO flash ICs with vin supply Rudraksha Gupta via B4 Relay
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Rudraksha Gupta via B4 Relay @ 2026-05-03 21:43 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Linus Walleij, Bjorn Andersson, Konrad Dybcio,
Liam Girdwood, Mark Brown
Cc: linux-leds, devicetree, linux-kernel, linux-arm-msm, phone-devel,
Rudraksha Gupta, Conor Dooley, David Heidelberg, Konrad Dybcio
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
// # Multiple strobes shouldn't cause errors
echo 200000 > flash_timeout
echo 1 > flash_strobe
echo 1 > flash_strobe
sleep 1
cat flash_strobe
dmesg | tail -20
// # Multiple strobes shouldn't cause errors (extreme test)
for i in $(seq 1 500); do echo 1 > flash_strobe; echo 0 > flash_strobe; done
dmesg | tail -20
// # Test minimal timeout (turns on briefly)
echo 1 > flash_timeout
echo 1 > flash_strobe
sleep 0.1
cat flash_strobe
dmesg | tail -20
// # Flash strobe overrides brightness and flashes at full brightness
echo 200000 > flash_timeout
echo 1 > brightness
echo 1 > flash_strobe
sleep 1
cat flash_strobe
cat brightness
dmesg | tail -20
Downstream reference:
Link: https://github.com/LineageOS/android_kernel_samsung_d2/blob/stable/cm-12.0-YNG4N/drivers/leds/Makefile#L51
Link: https://github.com/LineageOS/android_kernel_samsung_d2/blob/stable/cm-12.0-YNG4N/arch/arm/mach-msm/board-apexq-camera.c#L591
Signed-off-by: Rudraksha Gupta <guptarud@gmail.com>
---
Changes in v5:
- Address Lee Jones's comments:
- formatting
- use devm_regulator_get_optional()
- use a workqueue instead of a timer
- previously we were validating ent-gpios xor vin-supply at probe,
but was removed. update the commit msg to reflect this
- Link to v4: https://lore.kernel.org/r/20260331-expressatt_camera_flash-v4-0-f1e99f474513@gmail.com
Changes in v4:
- Driver:
- revert function renames
- add comment to use flash instead if torch pin not available
- Link to v3: https://lore.kernel.org/r/20260326-expressatt_camera_flash-v3-0-e75e5d58990f@gmail.com
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/20260318-expressatt_camera_flash-v2-0-5c2b9a623dcb@gmail.com
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/20260306-expressatt_camera_flash-v1-0-b1996f7cdfdd@gmail.com
---
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 | 130 +++++++++++++++++----
3 files changed, 181 insertions(+), 26 deletions(-)
---
base-commit: 3131ff5a117498bb4b9db3a238bb311cbf8383ce
change-id: 20260306-expressatt_camera_flash-13c15a7427aa
prerequisite-message-id: <20260503-expressatt-touchkey-v1-1-f7dd5db64e0d@gmail.com>
prerequisite-patch-id: 8de4de7909722ccaf385c4224f25a623eaa72c28
Best regards,
--
Rudraksha Gupta <guptarud@gmail.com>
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH v5 1/3] dt-bindings: leds: rt8515: Support single-GPIO flash ICs with vin supply 2026-05-03 21:43 [PATCH v5 0/3] Samsung Expressatt: Camera Flash Rudraksha Gupta via B4 Relay @ 2026-05-03 21:43 ` Rudraksha Gupta via B4 Relay 2026-05-03 21:43 ` [PATCH v5 2/3] leds: flash: " Rudraksha Gupta via B4 Relay 2026-05-03 21:43 ` [PATCH v5 3/3] ARM: dts: qcom: msm8960: expressatt: Add camera flash Rudraksha Gupta via B4 Relay 2 siblings, 0 replies; 5+ messages in thread From: Rudraksha Gupta via B4 Relay @ 2026-05-03 21:43 UTC (permalink / raw) To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Linus Walleij, Bjorn Andersson, Konrad Dybcio, Liam Girdwood, Mark Brown Cc: linux-leds, devicetree, linux-kernel, linux-arm-msm, phone-devel, Rudraksha Gupta, Conor Dooley From: Rudraksha Gupta <guptarud@gmail.com> 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 Acked-by: Conor Dooley <conor.dooley@microchip.com> Reviewed-by: Linus Walleij <linusw@kernel.org> Signed-off-by: Rudraksha Gupta <guptarud@gmail.com> --- .../devicetree/bindings/leds/richtek,rt8515.yaml | 34 +++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/leds/richtek,rt8515.yaml b/Documentation/devicetree/bindings/leds/richtek,rt8515.yaml index 0356371a6b01..ab3c5139132c 100644 --- a/Documentation/devicetree/bindings/leds/richtek,rt8515.yaml +++ b/Documentation/devicetree/bindings/leds/richtek,rt8515.yaml @@ -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>; + }; + }; + ... -- 2.54.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v5 2/3] leds: flash: rt8515: Support single-GPIO flash ICs with vin supply 2026-05-03 21:43 [PATCH v5 0/3] Samsung Expressatt: Camera Flash Rudraksha Gupta via B4 Relay 2026-05-03 21:43 ` [PATCH v5 1/3] dt-bindings: leds: rt8515: Support single-GPIO flash ICs with vin supply Rudraksha Gupta via B4 Relay @ 2026-05-03 21:43 ` Rudraksha Gupta via B4 Relay 2026-05-14 10:31 ` Lee Jones 2026-05-03 21:43 ` [PATCH v5 3/3] ARM: dts: qcom: msm8960: expressatt: Add camera flash Rudraksha Gupta via B4 Relay 2 siblings, 1 reply; 5+ messages in thread From: Rudraksha Gupta via B4 Relay @ 2026-05-03 21:43 UTC (permalink / raw) To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Linus Walleij, Bjorn Andersson, Konrad Dybcio, Liam Girdwood, Mark Brown Cc: linux-leds, devicetree, linux-kernel, linux-arm-msm, phone-devel, Rudraksha Gupta From: Rudraksha Gupta <guptarud@gmail.com> 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. When ent-gpios is absent, the driver uses enf-gpios for both flash and torch brightness control. Assisted-by: Claude:claude-opus-4.6 Reviewed-by: Linus Walleij <linusw@kernel.org> Signed-off-by: Rudraksha Gupta <guptarud@gmail.com> --- drivers/leds/flash/leds-rt8515.c | 130 +++++++++++++++++++++++++++++++-------- 1 file changed, 105 insertions(+), 25 deletions(-) diff --git a/drivers/leds/flash/leds-rt8515.c b/drivers/leds/flash/leds-rt8515.c index f6b439674c03..4459874e6a6c 100644 --- a/drivers/leds/flash/leds-rt8515.c +++ b/drivers/leds/flash/leds-rt8515.c @@ -31,6 +31,7 @@ #include <linux/platform_device.h> #include <linux/property.h> #include <linux/regulator/consumer.h> +#include <linux/workqueue.h> #include <media/v4l2-flash-led-class.h> @@ -52,7 +53,7 @@ struct rt8515 { struct regulator *reg; struct gpio_desc *enable_torch; struct gpio_desc *enable_flash; - struct timer_list powerdown_timer; + struct delayed_work powerdown_work; u32 max_timeout; /* Flash max timeout */ int flash_max_intensity; int torch_max_intensity; @@ -63,16 +64,50 @@ 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_gpio_led_off(struct rt8515 *rt) { + int ret; + gpiod_set_value(rt->enable_flash, 0); gpiod_set_value(rt->enable_torch, 0); + + if (!rt->reg) + return 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_gpio_brightness_commit(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_gpio_led_off(rt); + if (ret) + return ret; + + /* IC needs time to reset its brightness counter */ + usleep_range(100, 200); + + /* Enable regulator */ + if (rt->reg) { + ret = regulator_enable(rt->reg); + if (ret) + return ret; + } /* * Toggling a GPIO line with a small delay increases the @@ -84,6 +119,8 @@ 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 */ @@ -92,23 +129,38 @@ static int rt8515_led_brightness_set(struct led_classdev *led, { 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_gpio_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. + * If there is no separate torch pin, use the flash pin + * for torch as well. + */ + ret = rt8515_gpio_brightness_commit(rt, + rt->enable_torch ?: rt->enable_flash, brightness); + if (ret) + goto out; } else { - /* Max torch brightness requested */ - gpiod_set_value(rt->enable_torch, 1); + /* + * Max torch brightness requested. + * If there is no separate torch pin, use the flash pin + * for torch as well. + */ + gpiod_set_value(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 +169,34 @@ 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_gpio_brightness_commit(rt, rt->enable_flash, brightness); + if (ret) + goto out; + /* Set timeout */ - mod_timer(&rt->powerdown_timer, - jiffies + usecs_to_jiffies(timeout->val)); + schedule_delayed_work(&rt->powerdown_work, + usecs_to_jiffies(timeout->val)); } else { - timer_delete_sync(&rt->powerdown_timer); + cancel_delayed_work(&rt->powerdown_work); /* Turn the LED off */ - rt8515_gpio_led_off(rt); + ret = rt8515_gpio_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, @@ -145,7 +204,7 @@ static int rt8515_led_flash_strobe_get(struct led_classdev_flash *fled, { struct rt8515 *rt = to_rt8515(fled); - *state = timer_pending(&rt->powerdown_timer); + *state = delayed_work_pending(&rt->powerdown_work); return 0; } @@ -163,12 +222,18 @@ static const struct led_flash_ops rt8515_flash_ops = { .timeout_set = rt8515_led_flash_timeout_set, }; -static void rt8515_powerdown_timer(struct timer_list *t) +static void rt8515_powerdown_work(struct work_struct *work) { - struct rt8515 *rt = timer_container_of(rt, t, powerdown_timer); + struct rt8515 *rt = container_of(work, struct rt8515, powerdown_work.work); + int ret; /* Turn the LED off */ - rt8515_gpio_led_off(rt); + mutex_lock(&rt->lock); + ret = rt8515_gpio_led_off(rt); + mutex_unlock(&rt->lock); + + 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 +363,22 @@ 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_optional(dev, "vin"); + if (IS_ERR(rt->reg)) { + ret = PTR_ERR(rt->reg); + if (ret != -ENODEV) + return dev_err_probe(dev, ret, + "failed to get vin supply\n"); + rt->reg = NULL; + } + child = device_get_next_child_node(dev, NULL); if (!child) { dev_err(dev, @@ -328,12 +403,17 @@ static int rt8515_probe(struct platform_device *pdev) dev_warn(dev, "flash-max-timeout-us property missing\n"); } - timer_setup(&rt->powerdown_timer, rt8515_powerdown_timer, 0); + INIT_DELAYED_WORK(&rt->powerdown_work, rt8515_powerdown_work); rt8515_init_flash_timeout(rt); fled->ops = &rt8515_flash_ops; - led->max_brightness = rt->torch_max_intensity; + /* + * If there is no separate torch pin, use the flash max intensity + * as the max brightness instead. + */ + led->max_brightness = rt->enable_torch ? + rt->torch_max_intensity : rt->flash_max_intensity; led->brightness_set_blocking = rt8515_led_brightness_set; led->flags |= LED_CORE_SUSPENDRESUME | LED_DEV_CAP_FLASH; @@ -372,7 +452,7 @@ static void rt8515_remove(struct platform_device *pdev) struct rt8515 *rt = platform_get_drvdata(pdev); rt8515_v4l2_flash_release(rt); - timer_delete_sync(&rt->powerdown_timer); + cancel_delayed_work_sync(&rt->powerdown_work); mutex_destroy(&rt->lock); } -- 2.54.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v5 2/3] leds: flash: rt8515: Support single-GPIO flash ICs with vin supply 2026-05-03 21:43 ` [PATCH v5 2/3] leds: flash: " Rudraksha Gupta via B4 Relay @ 2026-05-14 10:31 ` Lee Jones 0 siblings, 0 replies; 5+ messages in thread From: Lee Jones @ 2026-05-14 10:31 UTC (permalink / raw) To: guptarud Cc: Pavel Machek, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Linus Walleij, Bjorn Andersson, Konrad Dybcio, Liam Girdwood, Mark Brown, linux-leds, devicetree, linux-kernel, linux-arm-msm, phone-devel On Sun, 03 May 2026, Rudraksha Gupta via B4 Relay wrote: > From: Rudraksha Gupta <guptarud@gmail.com> > > 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. When ent-gpios is absent, the driver uses > enf-gpios for both flash and torch brightness control. > > Assisted-by: Claude:claude-opus-4.6 > Reviewed-by: Linus Walleij <linusw@kernel.org> > Signed-off-by: Rudraksha Gupta <guptarud@gmail.com> > --- > drivers/leds/flash/leds-rt8515.c | 130 +++++++++++++++++++++++++++++++-------- > 1 file changed, 105 insertions(+), 25 deletions(-) > > diff --git a/drivers/leds/flash/leds-rt8515.c b/drivers/leds/flash/leds-rt8515.c > index f6b439674c03..4459874e6a6c 100644 > --- a/drivers/leds/flash/leds-rt8515.c > +++ b/drivers/leds/flash/leds-rt8515.c > @@ -31,6 +31,7 @@ > #include <linux/platform_device.h> > #include <linux/property.h> > #include <linux/regulator/consumer.h> > +#include <linux/workqueue.h> > > #include <media/v4l2-flash-led-class.h> > > @@ -52,7 +53,7 @@ struct rt8515 { > struct regulator *reg; > struct gpio_desc *enable_torch; > struct gpio_desc *enable_flash; > - struct timer_list powerdown_timer; > + struct delayed_work powerdown_work; > u32 max_timeout; /* Flash max timeout */ > int flash_max_intensity; > int torch_max_intensity; > @@ -63,16 +64,50 @@ 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_gpio_led_off(struct rt8515 *rt) > { > + int ret; > + > gpiod_set_value(rt->enable_flash, 0); > gpiod_set_value(rt->enable_torch, 0); > + > + if (!rt->reg) > + return 0; > + > + /* Disable regulator */ This comment is superfluous. > + ret = regulator_is_enabled(rt->reg); > + if (ret < 0) > + return ret; Initialise ret to 0 and return ret then you can remove this branch. > + 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_gpio_brightness_commit(struct rt8515 *rt, > + struct gpio_desc *gpiod, > + int brightness) Use 100-chars to avoid this cramping. > { > int i; > + int ret; > + > + /* > + * Reset the IC to start brightness from zero, > + * then re-enable and pulse to the desired level. > + */ > + ret = rt8515_gpio_led_off(rt); > + if (ret) > + return ret; > + > + /* IC needs time to reset its brightness counter */ > + usleep_range(100, 200); > + > + /* Enable regulator */ As above. > + if (rt->reg) { > + ret = regulator_enable(rt->reg); > + if (ret) > + return ret; > + } > > /* > * Toggling a GPIO line with a small delay increases the > @@ -84,6 +119,8 @@ 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 */ > @@ -92,23 +129,38 @@ static int rt8515_led_brightness_set(struct led_classdev *led, > { > 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_gpio_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. > + * If there is no separate torch pin, use the flash pin > + * for torch as well. > + */ > + ret = rt8515_gpio_brightness_commit(rt, > + rt->enable_torch ?: rt->enable_flash, brightness); > + if (ret) > + goto out; > } else { > - /* Max torch brightness requested */ > - gpiod_set_value(rt->enable_torch, 1); > + /* > + * Max torch brightness requested. > + * If there is no separate torch pin, use the flash pin > + * for torch as well. > + */ > + gpiod_set_value(rt->enable_torch ?: rt->enable_flash, 1); What does 1 mean in this context? Maybe define it so it's clear. > } > > +out: > mutex_unlock(&rt->lock); > > - return 0; > + return ret; > } > > static int rt8515_led_flash_strobe_set(struct led_classdev_flash *fled, > @@ -117,27 +169,34 @@ 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_gpio_brightness_commit(rt, rt->enable_flash, brightness); > + if (ret) > + goto out; > + > /* Set timeout */ > - mod_timer(&rt->powerdown_timer, > - jiffies + usecs_to_jiffies(timeout->val)); > + schedule_delayed_work(&rt->powerdown_work, > + usecs_to_jiffies(timeout->val)); > } else { > - timer_delete_sync(&rt->powerdown_timer); > + cancel_delayed_work(&rt->powerdown_work); Would it be safer to use 'cancel_delayed_work_sync()' here? The previous implementation used the synchronous 'timer_delete_sync()'. Swapping to an asynchronous cancellation might introduce a race condition if the work is already executing. > /* Turn the LED off */ > - rt8515_gpio_led_off(rt); > + ret = rt8515_gpio_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, > @@ -145,7 +204,7 @@ static int rt8515_led_flash_strobe_get(struct led_classdev_flash *fled, > { > struct rt8515 *rt = to_rt8515(fled); > > - *state = timer_pending(&rt->powerdown_timer); > + *state = delayed_work_pending(&rt->powerdown_work); > > return 0; > } > @@ -163,12 +222,18 @@ static const struct led_flash_ops rt8515_flash_ops = { > .timeout_set = rt8515_led_flash_timeout_set, > }; > > -static void rt8515_powerdown_timer(struct timer_list *t) > +static void rt8515_powerdown_work(struct work_struct *work) > { > - struct rt8515 *rt = timer_container_of(rt, t, powerdown_timer); > + struct rt8515 *rt = container_of(work, struct rt8515, powerdown_work.work); > + int ret; > > /* Turn the LED off */ > - rt8515_gpio_led_off(rt); > + mutex_lock(&rt->lock); > + ret = rt8515_gpio_led_off(rt); > + mutex_unlock(&rt->lock); > + > + 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 +363,22 @@ 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"); The user doesn't care about 'ENT'. "Failed to obtain the Enable Torch GPIO" > + > + /* Optional VIN supply */ Doesn't the function call already say this? > + rt->reg = devm_regulator_get_optional(dev, "vin"); > + if (IS_ERR(rt->reg)) { > + ret = PTR_ERR(rt->reg); > + if (ret != -ENODEV) > + return dev_err_probe(dev, ret, > + "failed to get vin supply\n"); Why are we erroring out on an optional regulator? > + rt->reg = NULL; > + } > + > child = device_get_next_child_node(dev, NULL); > if (!child) { > dev_err(dev, > @@ -328,12 +403,17 @@ static int rt8515_probe(struct platform_device *pdev) > dev_warn(dev, > "flash-max-timeout-us property missing\n"); > } > - timer_setup(&rt->powerdown_timer, rt8515_powerdown_timer, 0); > + INIT_DELAYED_WORK(&rt->powerdown_work, rt8515_powerdown_work); > rt8515_init_flash_timeout(rt); > > fled->ops = &rt8515_flash_ops; > > - led->max_brightness = rt->torch_max_intensity; > + /* > + * If there is no separate torch pin, use the flash max intensity Drop the "the". > + * as the max brightness instead. > + */ > + led->max_brightness = rt->enable_torch ? > + rt->torch_max_intensity : rt->flash_max_intensity; > led->brightness_set_blocking = rt8515_led_brightness_set; > led->flags |= LED_CORE_SUSPENDRESUME | LED_DEV_CAP_FLASH; > > @@ -372,7 +452,7 @@ static void rt8515_remove(struct platform_device *pdev) > struct rt8515 *rt = platform_get_drvdata(pdev); > > rt8515_v4l2_flash_release(rt); > - timer_delete_sync(&rt->powerdown_timer); > + cancel_delayed_work_sync(&rt->powerdown_work); > mutex_destroy(&rt->lock); > } > > > -- > 2.54.0 > > -- Lee Jones ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v5 3/3] ARM: dts: qcom: msm8960: expressatt: Add camera flash 2026-05-03 21:43 [PATCH v5 0/3] Samsung Expressatt: Camera Flash Rudraksha Gupta via B4 Relay 2026-05-03 21:43 ` [PATCH v5 1/3] dt-bindings: leds: rt8515: Support single-GPIO flash ICs with vin supply Rudraksha Gupta via B4 Relay 2026-05-03 21:43 ` [PATCH v5 2/3] leds: flash: " Rudraksha Gupta via B4 Relay @ 2026-05-03 21:43 ` Rudraksha Gupta via B4 Relay 2 siblings, 0 replies; 5+ messages in thread From: Rudraksha Gupta via B4 Relay @ 2026-05-03 21:43 UTC (permalink / raw) To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Linus Walleij, Bjorn Andersson, Konrad Dybcio, Liam Girdwood, Mark Brown Cc: linux-leds, devicetree, linux-kernel, linux-arm-msm, phone-devel, Rudraksha Gupta, David Heidelberg, Konrad Dybcio From: Rudraksha Gupta <guptarud@gmail.com> 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: Link: https://github.com/LineageOS/android_kernel_samsung_d2/blob/stable/cm-12.0-YNG4N/drivers/leds/Makefile#L51 Link: https://github.com/LineageOS/android_kernel_samsung_d2/blob/stable/cm-12.0-YNG4N/arch/arm/mach-msm/board-apexq-camera.c#L591 Assisted-by: Claude:claude-opus-4.6 Reviewed-by: David Heidelberg <david@ixit.cz> Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com> Signed-off-by: Rudraksha Gupta <guptarud@gmail.com> --- .../dts/qcom/qcom-msm8960-samsung-expressatt.dts | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/arch/arm/boot/dts/qcom/qcom-msm8960-samsung-expressatt.dts b/arch/arm/boot/dts/qcom/qcom-msm8960-samsung-expressatt.dts index c4b98af6955d..35514fd53e3d 100644 --- a/arch/arm/boot/dts/qcom/qcom-msm8960-samsung-expressatt.dts +++ b/arch/arm/boot/dts/qcom/qcom-msm8960-samsung-expressatt.dts @@ -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>; + }; +}; -- 2.54.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-05-14 10:31 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-03 21:43 [PATCH v5 0/3] Samsung Expressatt: Camera Flash Rudraksha Gupta via B4 Relay 2026-05-03 21:43 ` [PATCH v5 1/3] dt-bindings: leds: rt8515: Support single-GPIO flash ICs with vin supply Rudraksha Gupta via B4 Relay 2026-05-03 21:43 ` [PATCH v5 2/3] leds: flash: " Rudraksha Gupta via B4 Relay 2026-05-14 10:31 ` Lee Jones 2026-05-03 21:43 ` [PATCH v5 3/3] ARM: dts: qcom: msm8960: expressatt: Add camera flash Rudraksha Gupta via B4 Relay
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox