* [PATCH v6 0/6] media: lm3560: convert to use OF bindings
@ 2026-05-06 6:48 Svyatoslav Ryhel
2026-05-06 6:48 ` [PATCH v6 1/6] dt-bindings: leds: Document TI LM3560 Synchronous Boost Flash Driver Svyatoslav Ryhel
` (5 more replies)
0 siblings, 6 replies; 8+ messages in thread
From: Svyatoslav Ryhel @ 2026-05-06 6:48 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sakari Ailus, Mauro Carvalho Chehab,
Svyatoslav Ryhel
Cc: linux-leds, devicetree, linux-kernel, linux-media
Add missing HWEN input pin and IN supply. Fix v4l2 subdev registration.
Remove platform data and switch to OF device tree bindings.
---
Changes in v6:
- fixed swapped current ranges for flash-max-microamp and led-max-microamp
- for_each_available_child_of_node > device_for_each_child_node_scoped
- pm_runtime_get_if_in_use > pm_runtime_get_if_active
Changes in v5:
- schema adjusted to take into account lm3559
- device_for_each_child_node > for_each_available_child_of_node
- lm3559 and lm3560 configuration was diverged with data match
- removed redundant header
Changes in v4:
- fixed current being off by 10 in schema
- label property from schema replaced with modern equivalents
- lm3560_init_device moved before subdev registration
- v4l2_device_unregister_subdev > v4l2_async_unregister_subdev
- added subdevice cleanup if second led registration fails
- added check if "reg" property exists for LED nodes
- added missing fwnode_handle_put if device loop fails
- added bitmap to monitor configured LED id
- added pm_ptr() macro for PM operations pointer
Changes in v3:
- added note regarding lm3559 in the schema commit
- lm3560 power on/off functions converted to be part of PM,
dropped redundant wrappers
Changes in v2:
- vendor properties swapped with generic LED properties
- added mutex lock usage optimization
- power supply and enable gpio commits squashed into PM
configuration since they are both required in making
proper on/off sequence.
---
Svyatoslav Ryhel (6):
dt-bindings: leds: Document TI LM3560 Synchronous Boost Flash Driver
media: i2c: lm3560: Fix v4l2 subdev registration
media: i2c: lm3560: Optimize mutex lock usage
media: i2c: lm3560: Convert to use OF bindings
media: i2c: lm3560: Add support for PM features
media: i2c: lm3560: Add proper support for LM3559
.../devicetree/bindings/leds/ti,lm3560.yaml | 163 ++++++++
drivers/media/i2c/lm3560.c | 383 +++++++++++++++---
include/media/i2c/lm3560.h | 84 ----
3 files changed, 479 insertions(+), 151 deletions(-)
create mode 100644 Documentation/devicetree/bindings/leds/ti,lm3560.yaml
delete mode 100644 include/media/i2c/lm3560.h
--
2.51.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v6 1/6] dt-bindings: leds: Document TI LM3560 Synchronous Boost Flash Driver
2026-05-06 6:48 [PATCH v6 0/6] media: lm3560: convert to use OF bindings Svyatoslav Ryhel
@ 2026-05-06 6:48 ` Svyatoslav Ryhel
2026-05-06 17:03 ` Conor Dooley
2026-05-06 6:48 ` [PATCH v6 2/6] media: i2c: lm3560: Fix v4l2 subdev registration Svyatoslav Ryhel
` (4 subsequent siblings)
5 siblings, 1 reply; 8+ messages in thread
From: Svyatoslav Ryhel @ 2026-05-06 6:48 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sakari Ailus, Mauro Carvalho Chehab,
Svyatoslav Ryhel
Cc: linux-leds, devicetree, linux-kernel, linux-media
Document TI LM3559 and LM3560 Synchronous Boost Flash Driver used for
camera flash LEDs.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
.../devicetree/bindings/leds/ti,lm3560.yaml | 163 ++++++++++++++++++
1 file changed, 163 insertions(+)
create mode 100644 Documentation/devicetree/bindings/leds/ti,lm3560.yaml
diff --git a/Documentation/devicetree/bindings/leds/ti,lm3560.yaml b/Documentation/devicetree/bindings/leds/ti,lm3560.yaml
new file mode 100644
index 000000000000..6cf8cf91ab2e
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/ti,lm3560.yaml
@@ -0,0 +1,163 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/leds/ti,lm3560.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: TI LM3560 Synchronous Boost Flash Driver
+
+maintainers:
+ - Svyatoslav Ryhel <clamor95@gmail.com>
+
+description:
+ The LM3560 is a 2-MHz fixed frequency synchronous boost converter with two
+ 1000-mA constant current drivers for high-current white LEDs. The dual high-
+ side current sources allow for grounded cathode LED operation and can be
+ tied together for providing flash currents at up to 2 A through a single LED.
+ An adaptive regulation method ensures the current for each LED remains in
+ regulation and maximizes efficiency.
+
+properties:
+ compatible:
+ enum:
+ - ti,lm3559
+ - ti,lm3560
+
+ reg:
+ maxItems: 1
+
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+ enable-gpios:
+ description: GPIO connected to the HWEN pin.
+ maxItems: 1
+
+ vin-supply:
+ description: Supply connected to the IN line.
+
+ flash-max-timeout-us:
+ minimum: 32000
+ maximum: 1024000
+ default: 32000
+
+ ti,peak-current-microamp:
+ description:
+ The LM3560 features 4 selectable current limits 1.6A, 2.3A, 3A, and 3.6A
+ (in case of LM3559 - 1.4A, 2.1A, 2.7A, and 3.2A). When the current limit
+ is reached, the LM3559/LM3560 stops switching for the remainder of the
+ switching cycle.
+
+patternProperties:
+ '^led@[01]$':
+ type: object
+ $ref: /schemas/leds/common.yaml#
+ description: LED control bank nodes.
+ unevaluatedProperties: false
+
+ properties:
+ reg:
+ description: Control bank selection (0 = bank A, 1 = bank B).
+ maximum: 1
+
+ required:
+ - reg
+ - flash-max-microamp
+ - led-max-microamp
+
+allOf:
+ - $ref: /schemas/leds/common.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: ti,lm3559
+ then:
+ properties:
+ ti,peak-current-microamp:
+ enum: [1400000, 2100000, 2700000, 3200000]
+ default: 1400000
+ patternProperties:
+ '^led@[01]$':
+ properties:
+ flash-max-microamp:
+ minimum: 56250
+ maximum: 900000
+ led-max-microamp:
+ minimum: 28125
+ maximum: 225000
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: ti,lm3560
+ then:
+ properties:
+ ti,peak-current-microamp:
+ enum: [1600000, 2300000, 3000000, 3600000]
+ default: 1600000
+ patternProperties:
+ '^led@[01]$':
+ properties:
+ flash-max-microamp:
+ minimum: 62500
+ maximum: 1000000
+ led-max-microamp:
+ minimum: 31250
+ maximum: 250000
+
+required:
+ - compatible
+ - reg
+ - '#address-cells'
+ - '#size-cells'
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/leds/common.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ led-controller@53 {
+ compatible = "ti,lm3560";
+ reg = <0x53>;
+
+ enable-gpios = <&gpio 28 GPIO_ACTIVE_HIGH>;
+ vin-supply = <&vdd_3v3_sys>;
+
+ flash-max-timeout-us = <1024000>;
+ ti,peak-current-microamp = <1600000>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ led@0 {
+ reg = <0>;
+
+ function = LED_FUNCTION_FLASH;
+ color = <LED_COLOR_ID_WHITE>;
+
+ flash-max-microamp = <562500>;
+ led-max-microamp = <156250>;
+ };
+
+ led@1 {
+ reg = <1>;
+
+ function = LED_FUNCTION_FLASH;
+ color = <LED_COLOR_ID_YELLOW>;
+
+ flash-max-microamp = <562500>;
+ led-max-microamp = <156250>;
+ };
+ };
+ };
--
2.51.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v6 2/6] media: i2c: lm3560: Fix v4l2 subdev registration
2026-05-06 6:48 [PATCH v6 0/6] media: lm3560: convert to use OF bindings Svyatoslav Ryhel
2026-05-06 6:48 ` [PATCH v6 1/6] dt-bindings: leds: Document TI LM3560 Synchronous Boost Flash Driver Svyatoslav Ryhel
@ 2026-05-06 6:48 ` Svyatoslav Ryhel
2026-05-06 6:48 ` [PATCH v6 3/6] media: i2c: lm3560: Optimize mutex lock usage Svyatoslav Ryhel
` (3 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Svyatoslav Ryhel @ 2026-05-06 6:48 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sakari Ailus, Mauro Carvalho Chehab,
Svyatoslav Ryhel
Cc: linux-leds, devicetree, linux-kernel, linux-media
The existing driver does not call media subdev registration, making it
invisible to the media framework. Since the LM3560 supports two
independent LEDs, register each LED as a separate media entity.
Because registering LEDs before device initialization may cause access
attempts before the hardware is ready, lm3560_init_device has been moved
before the subdevice initializations.
An additional helper, lm3560_subdev_cleanup, was added to release LED0 if
the initialization of LED1 fails, and to deregister both LEDs in the
remove function.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/media/i2c/lm3560.c | 34 ++++++++++++++++++++++++----------
1 file changed, 24 insertions(+), 10 deletions(-)
diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c
index f4cc844f4e3c..edfb07587cab 100644
--- a/drivers/media/i2c/lm3560.c
+++ b/drivers/media/i2c/lm3560.c
@@ -364,8 +364,15 @@ static int lm3560_subdev_init(struct lm3560_flash *flash,
goto err_out;
flash->subdev_led[led_no].entity.function = MEDIA_ENT_F_FLASH;
- return rval;
+ rval = v4l2_async_register_subdev(&flash->subdev_led[led_no]);
+ if (rval < 0) {
+ dev_err(flash->dev, "failed to register V4L2 subdev");
+ goto error_out_media;
+ }
+ return rval;
+error_out_media:
+ media_entity_cleanup(&flash->subdev_led[led_no].entity);
err_out:
v4l2_ctrl_handler_free(&flash->ctrls_led[led_no]);
return rval;
@@ -391,6 +398,14 @@ static int lm3560_init_device(struct lm3560_flash *flash)
return rval;
}
+static void lm3560_subdev_cleanup(struct lm3560_flash *flash,
+ enum lm3560_led_id led_no)
+{
+ v4l2_async_unregister_subdev(&flash->subdev_led[led_no]);
+ v4l2_ctrl_handler_free(&flash->ctrls_led[led_no]);
+ media_entity_cleanup(&flash->subdev_led[led_no].entity);
+}
+
static int lm3560_probe(struct i2c_client *client)
{
struct lm3560_flash *flash;
@@ -425,17 +440,19 @@ static int lm3560_probe(struct i2c_client *client)
flash->dev = &client->dev;
mutex_init(&flash->lock);
- rval = lm3560_subdev_init(flash, LM3560_LED0, "lm3560-led0");
+ rval = lm3560_init_device(flash);
if (rval < 0)
return rval;
- rval = lm3560_subdev_init(flash, LM3560_LED1, "lm3560-led1");
+ rval = lm3560_subdev_init(flash, LM3560_LED0, "lm3560-led0");
if (rval < 0)
return rval;
- rval = lm3560_init_device(flash);
- if (rval < 0)
+ rval = lm3560_subdev_init(flash, LM3560_LED1, "lm3560-led1");
+ if (rval < 0) {
+ lm3560_subdev_cleanup(flash, LM3560_LED0);
return rval;
+ }
i2c_set_clientdata(client, flash);
@@ -447,11 +464,8 @@ static void lm3560_remove(struct i2c_client *client)
struct lm3560_flash *flash = i2c_get_clientdata(client);
unsigned int i;
- for (i = LM3560_LED0; i < LM3560_LED_MAX; i++) {
- v4l2_device_unregister_subdev(&flash->subdev_led[i]);
- v4l2_ctrl_handler_free(&flash->ctrls_led[i]);
- media_entity_cleanup(&flash->subdev_led[i].entity);
- }
+ for (i = LM3560_LED0; i < LM3560_LED_MAX; i++)
+ lm3560_subdev_cleanup(flash, i);
}
static const struct i2c_device_id lm3560_id_table[] = {
--
2.51.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v6 3/6] media: i2c: lm3560: Optimize mutex lock usage
2026-05-06 6:48 [PATCH v6 0/6] media: lm3560: convert to use OF bindings Svyatoslav Ryhel
2026-05-06 6:48 ` [PATCH v6 1/6] dt-bindings: leds: Document TI LM3560 Synchronous Boost Flash Driver Svyatoslav Ryhel
2026-05-06 6:48 ` [PATCH v6 2/6] media: i2c: lm3560: Fix v4l2 subdev registration Svyatoslav Ryhel
@ 2026-05-06 6:48 ` Svyatoslav Ryhel
2026-05-06 6:48 ` [PATCH v6 4/6] media: i2c: lm3560: Convert to use OF bindings Svyatoslav Ryhel
` (2 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Svyatoslav Ryhel @ 2026-05-06 6:48 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sakari Ailus, Mauro Carvalho Chehab,
Svyatoslav Ryhel
Cc: linux-leds, devicetree, linux-kernel, linux-media
Pass the device's own mutex lock to the control handler so that the media
framework can handle control access instead of managing it manually. The
lock must be common to both sub-devices since they share same hardware,
so the individual sub-device locks will not work here.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/media/i2c/lm3560.c | 19 ++++++-------------
1 file changed, 6 insertions(+), 13 deletions(-)
diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c
index edfb07587cab..5b568ed9536b 100644
--- a/drivers/media/i2c/lm3560.c
+++ b/drivers/media/i2c/lm3560.c
@@ -162,14 +162,12 @@ static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
struct lm3560_flash *flash = to_lm3560_flash(ctrl, led_no);
int rval = -EINVAL;
- mutex_lock(&flash->lock);
-
if (ctrl->id == V4L2_CID_FLASH_FAULT) {
s32 fault = 0;
unsigned int reg_val;
rval = regmap_read(flash->regmap, REG_FLAG, ®_val);
if (rval < 0)
- goto out;
+ return rval;
if (reg_val & FAULT_SHORT_CIRCUIT)
fault |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
if (reg_val & FAULT_OVERTEMP)
@@ -179,8 +177,6 @@ static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
ctrl->cur.val = fault;
}
-out:
- mutex_unlock(&flash->lock);
return rval;
}
@@ -190,8 +186,6 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
u8 tout_bits;
int rval = -EINVAL;
- mutex_lock(&flash->lock);
-
switch (ctrl->id) {
case V4L2_CID_FLASH_LED_MODE:
flash->led_mode = ctrl->val;
@@ -202,14 +196,12 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
case V4L2_CID_FLASH_STROBE_SOURCE:
rval = regmap_update_bits(flash->regmap,
REG_CONFIG1, 0x04, (ctrl->val) << 2);
- if (rval < 0)
- goto err_out;
break;
case V4L2_CID_FLASH_STROBE:
if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) {
rval = -EBUSY;
- goto err_out;
+ break;
}
flash->led_mode = V4L2_FLASH_LED_MODE_FLASH;
rval = lm3560_mode_ctrl(flash);
@@ -218,7 +210,7 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
case V4L2_CID_FLASH_STROBE_STOP:
if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) {
rval = -EBUSY;
- goto err_out;
+ break;
}
flash->led_mode = V4L2_FLASH_LED_MODE_NONE;
rval = lm3560_mode_ctrl(flash);
@@ -239,8 +231,6 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
break;
}
-err_out:
- mutex_unlock(&flash->lock);
return rval;
}
@@ -328,6 +318,8 @@ static int lm3560_init_controls(struct lm3560_flash *flash,
if (fault != NULL)
fault->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ hdl->lock = &flash->lock;
+
if (hdl->error)
return hdl->error;
@@ -363,6 +355,7 @@ static int lm3560_subdev_init(struct lm3560_flash *flash,
if (rval < 0)
goto err_out;
flash->subdev_led[led_no].entity.function = MEDIA_ENT_F_FLASH;
+ flash->subdev_led[led_no].state_lock = &flash->lock;
rval = v4l2_async_register_subdev(&flash->subdev_led[led_no]);
if (rval < 0) {
--
2.51.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v6 4/6] media: i2c: lm3560: Convert to use OF bindings
2026-05-06 6:48 [PATCH v6 0/6] media: lm3560: convert to use OF bindings Svyatoslav Ryhel
` (2 preceding siblings ...)
2026-05-06 6:48 ` [PATCH v6 3/6] media: i2c: lm3560: Optimize mutex lock usage Svyatoslav Ryhel
@ 2026-05-06 6:48 ` Svyatoslav Ryhel
2026-05-06 6:48 ` [PATCH v6 5/6] media: i2c: lm3560: Add support for PM features Svyatoslav Ryhel
2026-05-06 6:48 ` [PATCH v6 6/6] media: i2c: lm3560: Add proper support for LM3559 Svyatoslav Ryhel
5 siblings, 0 replies; 8+ messages in thread
From: Svyatoslav Ryhel @ 2026-05-06 6:48 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sakari Ailus, Mauro Carvalho Chehab,
Svyatoslav Ryhel
Cc: linux-leds, devicetree, linux-kernel, linux-media
Since there are no users of this driver via platform data, remove the
platform data support and switch to using Device Tree bindings.
Converting to Device Tree assumes dynamic and independent registration of
LEDs. To monitor the configured LEDs, a bitmap has been added. This makes
LED cleanup more robust and less context dependent.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/media/i2c/lm3560.c | 141 ++++++++++++++++++++++++++-----------
include/media/i2c/lm3560.h | 15 ----
2 files changed, 100 insertions(+), 56 deletions(-)
diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c
index 5b568ed9536b..609fd0339345 100644
--- a/drivers/media/i2c/lm3560.c
+++ b/drivers/media/i2c/lm3560.c
@@ -9,11 +9,14 @@
* Ldd-Mlp <ldd-mlp@list.ti.com>
*/
+#include <linux/bitmap.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
+#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/videodev2.h>
#include <media/i2c/lm3560.h>
@@ -43,22 +46,33 @@ enum led_enable {
* struct lm3560_flash
*
* @dev: pointer to &struct device
- * @pdata: platform data
* @regmap: reg. map for i2c
* @lock: muxtex for serial access.
* @led_mode: V4L2 LED mode
* @ctrls_led: V4L2 controls
* @subdev_led: V4L2 subdev
+ * @led_id: LED status holder
+ * @peak: peak current
+ * @max_flash_timeout: flash timeout
+ * @max_flash_brt: flash mode led brightness
+ * @max_torch_brt: torch mode led brightness
*/
struct lm3560_flash {
struct device *dev;
- struct lm3560_platform_data *pdata;
struct regmap *regmap;
struct mutex lock;
enum v4l2_flash_led_mode led_mode;
struct v4l2_ctrl_handler ctrls_led[LM3560_LED_MAX];
struct v4l2_subdev subdev_led[LM3560_LED_MAX];
+
+ DECLARE_BITMAP(led_id, LM3560_LED_MAX);
+
+ enum lm3560_peak_current peak;
+ u32 max_flash_timeout;
+
+ u32 max_flash_brt[LM3560_LED_MAX];
+ u32 max_torch_brt[LM3560_LED_MAX];
};
#define to_lm3560_flash(_ctrl, _no) \
@@ -269,8 +283,8 @@ static int lm3560_init_controls(struct lm3560_flash *flash,
enum lm3560_led_id led_no)
{
struct v4l2_ctrl *fault;
- u32 max_flash_brt = flash->pdata->max_flash_brt[led_no];
- u32 max_torch_brt = flash->pdata->max_torch_brt[led_no];
+ u32 max_flash_brt = flash->max_flash_brt[led_no];
+ u32 max_torch_brt = flash->max_torch_brt[led_no];
struct v4l2_ctrl_handler *hdl = &flash->ctrls_led[led_no];
const struct v4l2_ctrl_ops *ops = &lm3560_led_ctrl_ops[led_no];
@@ -295,9 +309,9 @@ static int lm3560_init_controls(struct lm3560_flash *flash,
/* flash strobe timeout */
v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TIMEOUT,
LM3560_FLASH_TOUT_MIN,
- flash->pdata->max_flash_timeout,
+ flash->max_flash_timeout,
LM3560_FLASH_TOUT_STEP,
- flash->pdata->max_flash_timeout);
+ flash->max_flash_timeout);
/* flash brt */
v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_INTENSITY,
@@ -339,15 +353,18 @@ static const struct regmap_config lm3560_regmap = {
};
static int lm3560_subdev_init(struct lm3560_flash *flash,
- enum lm3560_led_id led_no, char *led_name)
+ enum lm3560_led_id led_no,
+ struct fwnode_handle *fwnode)
{
struct i2c_client *client = to_i2c_client(flash->dev);
int rval;
v4l2_i2c_subdev_init(&flash->subdev_led[led_no], client, &lm3560_ops);
flash->subdev_led[led_no].flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- strscpy(flash->subdev_led[led_no].name, led_name,
- sizeof(flash->subdev_led[led_no].name));
+ snprintf(flash->subdev_led[led_no].name,
+ sizeof(flash->subdev_led[led_no].name),
+ "lm3560-led%d", led_no);
+ flash->subdev_led[led_no].fwnode = fwnode_handle_get(fwnode);
rval = lm3560_init_controls(flash, led_no);
if (rval)
goto err_out;
@@ -378,7 +395,7 @@ static int lm3560_init_device(struct lm3560_flash *flash)
/* set peak current */
rval = regmap_update_bits(flash->regmap,
- REG_FLASH_TOUT, 0x60, flash->pdata->peak);
+ REG_FLASH_TOUT, 0x60, flash->peak);
if (rval < 0)
return rval;
/* output disable */
@@ -391,18 +408,21 @@ static int lm3560_init_device(struct lm3560_flash *flash)
return rval;
}
-static void lm3560_subdev_cleanup(struct lm3560_flash *flash,
- enum lm3560_led_id led_no)
+static void lm3560_subdev_cleanup(struct lm3560_flash *flash)
{
- v4l2_async_unregister_subdev(&flash->subdev_led[led_no]);
- v4l2_ctrl_handler_free(&flash->ctrls_led[led_no]);
- media_entity_cleanup(&flash->subdev_led[led_no].entity);
+ int led_no;
+
+ for_each_set_bit(led_no, flash->led_id, LM3560_LED_MAX) {
+ v4l2_async_unregister_subdev(&flash->subdev_led[led_no]);
+ v4l2_ctrl_handler_free(&flash->ctrls_led[led_no]);
+ media_entity_cleanup(&flash->subdev_led[led_no].entity);
+ }
}
static int lm3560_probe(struct i2c_client *client)
{
struct lm3560_flash *flash;
- struct lm3560_platform_data *pdata = dev_get_platdata(&client->dev);
+ u32 peak_ua;
int rval;
flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL);
@@ -415,36 +435,69 @@ static int lm3560_probe(struct i2c_client *client)
return rval;
}
- /* if there is no platform data, use chip default value */
- if (pdata == NULL) {
- pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
- if (pdata == NULL)
- return -ENODEV;
- pdata->peak = LM3560_PEAK_3600mA;
- pdata->max_flash_timeout = LM3560_FLASH_TOUT_MAX;
- /* led 1 */
- pdata->max_flash_brt[LM3560_LED0] = LM3560_FLASH_BRT_MAX;
- pdata->max_torch_brt[LM3560_LED0] = LM3560_TORCH_BRT_MAX;
- /* led 2 */
- pdata->max_flash_brt[LM3560_LED1] = LM3560_FLASH_BRT_MAX;
- pdata->max_torch_brt[LM3560_LED1] = LM3560_TORCH_BRT_MAX;
- }
- flash->pdata = pdata;
flash->dev = &client->dev;
mutex_init(&flash->lock);
+ bitmap_zero(flash->led_id, LM3560_LED_MAX);
+
+ flash->peak = LM3560_PEAK_1600mA;
+ rval = device_property_read_u32(flash->dev,
+ "ti,peak-current-microamp", &peak_ua);
+ if (!rval) {
+ switch (peak_ua) {
+ case 1600000:
+ flash->peak = LM3560_PEAK_1600mA;
+ break;
+ case 2300000:
+ flash->peak = LM3560_PEAK_2300mA;
+ break;
+ case 3000000:
+ flash->peak = LM3560_PEAK_3000mA;
+ break;
+ case 3600000:
+ flash->peak = LM3560_PEAK_3600mA;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ flash->max_flash_timeout = LM3560_FLASH_TOUT_MIN * 1000;
+ device_property_read_u32(flash->dev, "flash-max-timeout-us",
+ &flash->max_flash_timeout);
+ flash->max_flash_timeout /= 1000;
+
rval = lm3560_init_device(flash);
if (rval < 0)
return rval;
- rval = lm3560_subdev_init(flash, LM3560_LED0, "lm3560-led0");
- if (rval < 0)
- return rval;
+ device_for_each_child_node_scoped(flash->dev, node) {
+ u32 reg;
- rval = lm3560_subdev_init(flash, LM3560_LED1, "lm3560-led1");
- if (rval < 0) {
- lm3560_subdev_cleanup(flash, LM3560_LED0);
- return rval;
+ rval = fwnode_property_read_u32(node, "reg", ®);
+ if (rval < 0)
+ /* We care only about nodes with reg property */
+ continue;
+
+ if (reg == LM3560_LED0 || reg == LM3560_LED1) {
+ flash->max_flash_brt[reg] = LM3560_FLASH_BRT_MIN;
+ fwnode_property_read_u32(node, "flash-max-microamp",
+ &flash->max_flash_brt[reg]);
+
+ flash->max_torch_brt[reg] = LM3560_TORCH_BRT_MIN;
+ fwnode_property_read_u32(node, "led-max-microamp",
+ &flash->max_torch_brt[reg]);
+
+ rval = lm3560_subdev_init(flash, reg, node);
+ if (rval < 0) {
+ lm3560_subdev_cleanup(flash);
+ return dev_err_probe(flash->dev, rval,
+ "failed to register led%d\n",
+ reg);
+ }
+
+ set_bit(reg, flash->led_id);
+ }
}
i2c_set_clientdata(client, flash);
@@ -455,12 +508,17 @@ static int lm3560_probe(struct i2c_client *client)
static void lm3560_remove(struct i2c_client *client)
{
struct lm3560_flash *flash = i2c_get_clientdata(client);
- unsigned int i;
- for (i = LM3560_LED0; i < LM3560_LED_MAX; i++)
- lm3560_subdev_cleanup(flash, i);
+ lm3560_subdev_cleanup(flash);
}
+static const struct of_device_id lm3560_of_match[] = {
+ { .compatible = "ti,lm3559" },
+ { .compatible = "ti,lm3560" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lm3560_of_match);
+
static const struct i2c_device_id lm3560_id_table[] = {
{ LM3559_NAME },
{ LM3560_NAME },
@@ -473,6 +531,7 @@ static struct i2c_driver lm3560_i2c_driver = {
.driver = {
.name = LM3560_NAME,
.pm = NULL,
+ .of_match_table = lm3560_of_match,
},
.probe = lm3560_probe,
.remove = lm3560_remove,
diff --git a/include/media/i2c/lm3560.h b/include/media/i2c/lm3560.h
index 770d8c72c94a..b56c1ff8fd49 100644
--- a/include/media/i2c/lm3560.h
+++ b/include/media/i2c/lm3560.h
@@ -66,19 +66,4 @@ enum lm3560_peak_current {
LM3560_PEAK_3600mA = 0x60
};
-/* struct lm3560_platform_data
- *
- * @peak : peak current
- * @max_flash_timeout: flash timeout
- * @max_flash_brt: flash mode led brightness
- * @max_torch_brt: torch mode led brightness
- */
-struct lm3560_platform_data {
- enum lm3560_peak_current peak;
-
- u32 max_flash_timeout;
- u32 max_flash_brt[LM3560_LED_MAX];
- u32 max_torch_brt[LM3560_LED_MAX];
-};
-
#endif /* __LM3560_H__ */
--
2.51.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v6 5/6] media: i2c: lm3560: Add support for PM features
2026-05-06 6:48 [PATCH v6 0/6] media: lm3560: convert to use OF bindings Svyatoslav Ryhel
` (3 preceding siblings ...)
2026-05-06 6:48 ` [PATCH v6 4/6] media: i2c: lm3560: Convert to use OF bindings Svyatoslav Ryhel
@ 2026-05-06 6:48 ` Svyatoslav Ryhel
2026-05-06 6:48 ` [PATCH v6 6/6] media: i2c: lm3560: Add proper support for LM3559 Svyatoslav Ryhel
5 siblings, 0 replies; 8+ messages in thread
From: Svyatoslav Ryhel @ 2026-05-06 6:48 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sakari Ailus, Mauro Carvalho Chehab,
Svyatoslav Ryhel
Cc: linux-leds, devicetree, linux-kernel, linux-media
Add support for power management features to better control the LM3560
within the media framework. To achieve the desired PM support, the HWEN
GPIO and VIN power supply were added and configured into power on/off
sequences. Added PM operations along with the PM configuration setup.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/media/i2c/lm3560.c | 117 ++++++++++++++++++++++++++++++++++---
1 file changed, 110 insertions(+), 7 deletions(-)
diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c
index 609fd0339345..49030bc0f441 100644
--- a/drivers/media/i2c/lm3560.c
+++ b/drivers/media/i2c/lm3560.c
@@ -12,12 +12,15 @@
#include <linux/bitmap.h>
#include <linux/delay.h>
#include <linux/module.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
#include <linux/property.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/videodev2.h>
#include <media/i2c/lm3560.h>
#include <media/v4l2-ctrls.h>
@@ -48,6 +51,8 @@ enum led_enable {
* @dev: pointer to &struct device
* @regmap: reg. map for i2c
* @lock: muxtex for serial access.
+ * @hwen_gpio: line connected to HWEN pin
+ * @vin_supply: line connected to IN supply (2.5V - 5.5V)
* @led_mode: V4L2 LED mode
* @ctrls_led: V4L2 controls
* @subdev_led: V4L2 subdev
@@ -62,6 +67,9 @@ struct lm3560_flash {
struct regmap *regmap;
struct mutex lock;
+ struct gpio_desc *hwen_gpio;
+ struct regulator *vin_supply;
+
enum v4l2_flash_led_mode led_mode;
struct v4l2_ctrl_handler ctrls_led[LM3560_LED_MAX];
struct v4l2_subdev subdev_led[LM3560_LED_MAX];
@@ -176,12 +184,17 @@ static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
struct lm3560_flash *flash = to_lm3560_flash(ctrl, led_no);
int rval = -EINVAL;
+ if (!pm_runtime_get_if_active(flash->dev))
+ return 0;
+
if (ctrl->id == V4L2_CID_FLASH_FAULT) {
s32 fault = 0;
unsigned int reg_val;
rval = regmap_read(flash->regmap, REG_FLAG, ®_val);
- if (rval < 0)
+ if (rval < 0) {
+ pm_runtime_put(flash->dev);
return rval;
+ }
if (reg_val & FAULT_SHORT_CIRCUIT)
fault |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
if (reg_val & FAULT_OVERTEMP)
@@ -191,6 +204,8 @@ static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
ctrl->cur.val = fault;
}
+ pm_runtime_put(flash->dev);
+
return rval;
}
@@ -200,6 +215,9 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
u8 tout_bits;
int rval = -EINVAL;
+ if (!pm_runtime_get_if_active(flash->dev))
+ return 0;
+
switch (ctrl->id) {
case V4L2_CID_FLASH_LED_MODE:
flash->led_mode = ctrl->val;
@@ -245,6 +263,8 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
break;
}
+ pm_runtime_put(flash->dev);
+
return rval;
}
@@ -408,6 +428,38 @@ static int lm3560_init_device(struct lm3560_flash *flash)
return rval;
}
+static int __maybe_unused lm3560_power_off(struct device *dev)
+{
+ struct lm3560_flash *flash = dev_get_drvdata(dev);
+
+ gpiod_set_value_cansleep(flash->hwen_gpio, 0);
+ regulator_disable(flash->vin_supply);
+
+ return 0;
+}
+
+static int __maybe_unused lm3560_power_on(struct device *dev)
+{
+ struct lm3560_flash *flash = dev_get_drvdata(dev);
+ int rval;
+
+ rval = regulator_enable(flash->vin_supply);
+ if (rval < 0) {
+ dev_err(flash->dev, "failed to enable vin power supply\n");
+ return rval;
+ }
+
+ gpiod_set_value_cansleep(flash->hwen_gpio, 1);
+
+ rval = lm3560_init_device(flash);
+ if (rval < 0) {
+ lm3560_power_off(dev);
+ return rval;
+ }
+
+ return 0;
+}
+
static void lm3560_subdev_cleanup(struct lm3560_flash *flash)
{
int led_no;
@@ -440,6 +492,17 @@ static int lm3560_probe(struct i2c_client *client)
bitmap_zero(flash->led_id, LM3560_LED_MAX);
+ flash->hwen_gpio = devm_gpiod_get_optional(flash->dev, "enable",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(flash->hwen_gpio))
+ return dev_err_probe(flash->dev, PTR_ERR(flash->hwen_gpio),
+ "failed to get hwen gpio\n");
+
+ flash->vin_supply = devm_regulator_get(flash->dev, "vin");
+ if (IS_ERR(flash->vin_supply))
+ return dev_err_probe(flash->dev, PTR_ERR(flash->vin_supply),
+ "failed to get vin-supply\n");
+
flash->peak = LM3560_PEAK_1600mA;
rval = device_property_read_u32(flash->dev,
"ti,peak-current-microamp", &peak_ua);
@@ -467,9 +530,19 @@ static int lm3560_probe(struct i2c_client *client)
&flash->max_flash_timeout);
flash->max_flash_timeout /= 1000;
+ rval = regulator_enable(flash->vin_supply);
+ if (rval < 0)
+ return dev_err_probe(flash->dev, rval,
+ "failed to enable vin power supply\n");
+
+ gpiod_set_value_cansleep(flash->hwen_gpio, 1);
+
rval = lm3560_init_device(flash);
if (rval < 0)
- return rval;
+ goto error_disable;
+
+ pm_runtime_set_active(flash->dev);
+ pm_runtime_enable(flash->dev);
device_for_each_child_node_scoped(flash->dev, node) {
u32 reg;
@@ -490,10 +563,10 @@ static int lm3560_probe(struct i2c_client *client)
rval = lm3560_subdev_init(flash, reg, node);
if (rval < 0) {
- lm3560_subdev_cleanup(flash);
- return dev_err_probe(flash->dev, rval,
- "failed to register led%d\n",
- reg);
+ dev_err(flash->dev,
+ "failed to register led%d: %d\n",
+ reg, rval);
+ goto error_clean;
}
set_bit(reg, flash->led_id);
@@ -502,7 +575,23 @@ static int lm3560_probe(struct i2c_client *client)
i2c_set_clientdata(client, flash);
+ pm_runtime_set_autosuspend_delay(flash->dev, 1000);
+ pm_runtime_use_autosuspend(flash->dev);
+ pm_runtime_idle(flash->dev);
+
return 0;
+
+error_clean:
+ pm_runtime_disable(flash->dev);
+ pm_runtime_set_suspended(flash->dev);
+
+ lm3560_subdev_cleanup(flash);
+
+error_disable:
+ gpiod_set_value_cansleep(flash->hwen_gpio, 0);
+ regulator_disable(flash->vin_supply);
+
+ return rval;
}
static void lm3560_remove(struct i2c_client *client)
@@ -510,8 +599,22 @@ static void lm3560_remove(struct i2c_client *client)
struct lm3560_flash *flash = i2c_get_clientdata(client);
lm3560_subdev_cleanup(flash);
+
+ /*
+ * Disable runtime PM. In case runtime PM is disabled in the kernel,
+ * make sure to turn power off manually.
+ */
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev)) {
+ lm3560_power_off(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ }
}
+static const struct dev_pm_ops lm3560_pm_ops = {
+ SET_RUNTIME_PM_OPS(lm3560_power_off, lm3560_power_on, NULL)
+};
+
static const struct of_device_id lm3560_of_match[] = {
{ .compatible = "ti,lm3559" },
{ .compatible = "ti,lm3560" },
@@ -530,7 +633,7 @@ MODULE_DEVICE_TABLE(i2c, lm3560_id_table);
static struct i2c_driver lm3560_i2c_driver = {
.driver = {
.name = LM3560_NAME,
- .pm = NULL,
+ .pm = pm_ptr(&lm3560_pm_ops),
.of_match_table = lm3560_of_match,
},
.probe = lm3560_probe,
--
2.51.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v6 6/6] media: i2c: lm3560: Add proper support for LM3559
2026-05-06 6:48 [PATCH v6 0/6] media: lm3560: convert to use OF bindings Svyatoslav Ryhel
` (4 preceding siblings ...)
2026-05-06 6:48 ` [PATCH v6 5/6] media: i2c: lm3560: Add support for PM features Svyatoslav Ryhel
@ 2026-05-06 6:48 ` Svyatoslav Ryhel
5 siblings, 0 replies; 8+ messages in thread
From: Svyatoslav Ryhel @ 2026-05-06 6:48 UTC (permalink / raw)
To: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sakari Ailus, Mauro Carvalho Chehab,
Svyatoslav Ryhel
Cc: linux-leds, devicetree, linux-kernel, linux-media
The LM3559 is very similar to the LM3560, but it operates at much lower
currents. This may result in incorrect current selection if LM3560 ranges
are applied to the LM3559. Implement driver data matching to use
device-specific current configurations.
Since the driver no longer supports platform data and device configuration
is performed more granularly, move the remaining enums from the header
into the driver file and remove the header.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
drivers/media/i2c/lm3560.c | 120 ++++++++++++++++++++++++++++++-------
include/media/i2c/lm3560.h | 69 ---------------------
2 files changed, 100 insertions(+), 89 deletions(-)
delete mode 100644 include/media/i2c/lm3560.h
diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c
index 49030bc0f441..46d316a26e50 100644
--- a/drivers/media/i2c/lm3560.c
+++ b/drivers/media/i2c/lm3560.c
@@ -22,9 +22,9 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/videodev2.h>
-#include <media/i2c/lm3560.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
/* registers definitions */
#define REG_ENABLE 0x10
@@ -39,12 +39,44 @@
#define FAULT_OVERTEMP (1<<1)
#define FAULT_SHORT_CIRCUIT (1<<2)
+#define LM3560_FLASH_TOUT_MIN 32
+#define LM3560_FLASH_TOUT_STEP 32
+#define LM3560_FLASH_TOUT_MAX 1024
+#define LM3560_FLASH_TOUT_ms_TO_REG(a) \
+ ((a) < LM3560_FLASH_TOUT_MIN ? 0 : \
+ (((a) - LM3560_FLASH_TOUT_MIN) / LM3560_FLASH_TOUT_STEP))
+#define LM3560_FLASH_TOUT_REG_TO_ms(a) \
+ ((a) * LM3560_FLASH_TOUT_STEP + LM3560_FLASH_TOUT_MIN)
+
+enum lm3560_led_id {
+ LM3560_LED0 = 0,
+ LM3560_LED1,
+ LM3560_LED_MAX
+};
+
+enum lm3560_peak_current {
+ LM3560_PEAK_1600mA = 0x00,
+ LM3560_PEAK_2300mA = 0x20,
+ LM3560_PEAK_3000mA = 0x40,
+ LM3560_PEAK_3600mA = 0x60
+};
+
enum led_enable {
MODE_SHDN = 0x0,
MODE_TORCH = 0x2,
MODE_FLASH = 0x3,
};
+struct lm3560_flash_config {
+ u32 flash_brt_min_ua;
+ u32 flash_brt_max_ua;
+ u32 flash_brt_step_ua;
+
+ u32 torch_brt_min_ua;
+ u32 torch_brt_max_ua;
+ u32 torch_brt_step_ua;
+};
+
/**
* struct lm3560_flash
*
@@ -61,6 +93,7 @@ enum led_enable {
* @max_flash_timeout: flash timeout
* @max_flash_brt: flash mode led brightness
* @max_torch_brt: torch mode led brightness
+ * @config: device specific current configuration
*/
struct lm3560_flash {
struct device *dev;
@@ -81,6 +114,8 @@ struct lm3560_flash {
u32 max_flash_brt[LM3560_LED_MAX];
u32 max_torch_brt[LM3560_LED_MAX];
+
+ const struct lm3560_flash_config *config;
};
#define to_lm3560_flash(_ctrl, _no) \
@@ -136,15 +171,20 @@ static int lm3560_enable_ctrl(struct lm3560_flash *flash,
static int lm3560_torch_brt_ctrl(struct lm3560_flash *flash,
enum lm3560_led_id led_no, unsigned int brt)
{
+ const struct lm3560_flash_config *config = flash->config;
int rval;
- u8 br_bits;
+ u32 br_bits;
- if (brt < LM3560_TORCH_BRT_MIN)
+ if (brt < config->torch_brt_min_ua)
return lm3560_enable_ctrl(flash, led_no, false);
else
rval = lm3560_enable_ctrl(flash, led_no, true);
- br_bits = LM3560_TORCH_BRT_uA_TO_REG(brt);
+ br_bits = clamp(brt, config->torch_brt_min_ua,
+ config->torch_brt_max_ua);
+ br_bits = (br_bits - config->torch_brt_min_ua) /
+ config->torch_brt_step_ua;
+
if (led_no == LM3560_LED0)
rval = regmap_update_bits(flash->regmap,
REG_TORCH_BR, 0x07, br_bits);
@@ -159,15 +199,20 @@ static int lm3560_torch_brt_ctrl(struct lm3560_flash *flash,
static int lm3560_flash_brt_ctrl(struct lm3560_flash *flash,
enum lm3560_led_id led_no, unsigned int brt)
{
+ const struct lm3560_flash_config *config = flash->config;
int rval;
- u8 br_bits;
+ u32 br_bits;
- if (brt < LM3560_FLASH_BRT_MIN)
+ if (brt < config->flash_brt_min_ua)
return lm3560_enable_ctrl(flash, led_no, false);
else
rval = lm3560_enable_ctrl(flash, led_no, true);
- br_bits = LM3560_FLASH_BRT_uA_TO_REG(brt);
+ br_bits = clamp(brt, config->flash_brt_min_ua,
+ config->flash_brt_max_ua);
+ br_bits = (br_bits - config->flash_brt_min_ua) /
+ config->flash_brt_step_ua;
+
if (led_no == LM3560_LED0)
rval = regmap_update_bits(flash->regmap,
REG_FLASH_BR, 0x0f, br_bits);
@@ -307,6 +352,7 @@ static int lm3560_init_controls(struct lm3560_flash *flash,
u32 max_torch_brt = flash->max_torch_brt[led_no];
struct v4l2_ctrl_handler *hdl = &flash->ctrls_led[led_no];
const struct v4l2_ctrl_ops *ops = &lm3560_led_ctrl_ops[led_no];
+ const struct lm3560_flash_config *config = flash->config;
v4l2_ctrl_handler_init(hdl, 8);
@@ -335,13 +381,13 @@ static int lm3560_init_controls(struct lm3560_flash *flash,
/* flash brt */
v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_INTENSITY,
- LM3560_FLASH_BRT_MIN, max_flash_brt,
- LM3560_FLASH_BRT_STEP, max_flash_brt);
+ config->flash_brt_min_ua, max_flash_brt,
+ config->flash_brt_step_ua, max_flash_brt);
/* torch brt */
v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TORCH_INTENSITY,
- LM3560_TORCH_BRT_MIN, max_torch_brt,
- LM3560_TORCH_BRT_STEP, max_torch_brt);
+ config->torch_brt_min_ua, max_torch_brt,
+ config->torch_brt_step_ua, max_torch_brt);
/* fault */
fault = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_FAULT, 0,
@@ -481,6 +527,10 @@ static int lm3560_probe(struct i2c_client *client)
if (flash == NULL)
return -ENOMEM;
+ flash->config = device_get_match_data(&client->dev);
+ if (!flash->config)
+ return -ENODEV;
+
flash->regmap = devm_regmap_init_i2c(client, &lm3560_regmap);
if (IS_ERR(flash->regmap)) {
rval = PTR_ERR(flash->regmap);
@@ -507,16 +557,26 @@ static int lm3560_probe(struct i2c_client *client)
rval = device_property_read_u32(flash->dev,
"ti,peak-current-microamp", &peak_ua);
if (!rval) {
+ /*
+ * LM3559 has lower peak current limit, but
+ * bit configuration matches LM3560.
+ * Correct current restrictions are enforced
+ * by the LM3560 schema.
+ */
switch (peak_ua) {
+ case 1400000:
case 1600000:
flash->peak = LM3560_PEAK_1600mA;
break;
+ case 2100000:
case 2300000:
flash->peak = LM3560_PEAK_2300mA;
break;
+ case 2700000:
case 3000000:
flash->peak = LM3560_PEAK_3000mA;
break;
+ case 3200000:
case 3600000:
flash->peak = LM3560_PEAK_3600mA;
break;
@@ -545,6 +605,7 @@ static int lm3560_probe(struct i2c_client *client)
pm_runtime_enable(flash->dev);
device_for_each_child_node_scoped(flash->dev, node) {
+ const struct lm3560_flash_config *config = flash->config;
u32 reg;
rval = fwnode_property_read_u32(node, "reg", ®);
@@ -553,11 +614,11 @@ static int lm3560_probe(struct i2c_client *client)
continue;
if (reg == LM3560_LED0 || reg == LM3560_LED1) {
- flash->max_flash_brt[reg] = LM3560_FLASH_BRT_MIN;
+ flash->max_flash_brt[reg] = config->flash_brt_min_ua;
fwnode_property_read_u32(node, "flash-max-microamp",
&flash->max_flash_brt[reg]);
- flash->max_torch_brt[reg] = LM3560_TORCH_BRT_MIN;
+ flash->max_torch_brt[reg] = config->torch_brt_min_ua;
fwnode_property_read_u32(node, "led-max-microamp",
&flash->max_torch_brt[reg]);
@@ -615,24 +676,43 @@ static const struct dev_pm_ops lm3560_pm_ops = {
SET_RUNTIME_PM_OPS(lm3560_power_off, lm3560_power_on, NULL)
};
+static const struct lm3560_flash_config lm3559_config = {
+ .flash_brt_min_ua = 56250,
+ .flash_brt_max_ua = 900000,
+ .flash_brt_step_ua = 56250,
+
+ .torch_brt_min_ua = 28125,
+ .torch_brt_max_ua = 225000,
+ .torch_brt_step_ua = 28125,
+};
+
+static const struct lm3560_flash_config lm3560_config = {
+ .flash_brt_min_ua = 62500,
+ .flash_brt_max_ua = 1000000,
+ .flash_brt_step_ua = 62500,
+
+ .torch_brt_min_ua = 31250,
+ .torch_brt_max_ua = 250000,
+ .torch_brt_step_ua = 31250,
+};
+
static const struct of_device_id lm3560_of_match[] = {
- { .compatible = "ti,lm3559" },
- { .compatible = "ti,lm3560" },
+ { .compatible = "ti,lm3559", .data = &lm3559_config },
+ { .compatible = "ti,lm3560", .data = &lm3560_config },
{ }
};
MODULE_DEVICE_TABLE(of, lm3560_of_match);
static const struct i2c_device_id lm3560_id_table[] = {
- { LM3559_NAME },
- { LM3560_NAME },
- {}
+ { "lm3559", (kernel_ulong_t)&lm3559_config },
+ { "lm3560", (kernel_ulong_t)&lm3560_config },
+ { }
};
-
MODULE_DEVICE_TABLE(i2c, lm3560_id_table);
static struct i2c_driver lm3560_i2c_driver = {
.driver = {
- .name = LM3560_NAME,
+ .name = "lm3560",
.pm = pm_ptr(&lm3560_pm_ops),
.of_match_table = lm3560_of_match,
},
diff --git a/include/media/i2c/lm3560.h b/include/media/i2c/lm3560.h
deleted file mode 100644
index b56c1ff8fd49..000000000000
--- a/include/media/i2c/lm3560.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * include/media/i2c/lm3560.h
- *
- * Copyright (C) 2013 Texas Instruments
- *
- * Contact: Daniel Jeong <gshark.jeong@gmail.com>
- * Ldd-Mlp <ldd-mlp@list.ti.com>
- */
-
-#ifndef __LM3560_H__
-#define __LM3560_H__
-
-#include <media/v4l2-subdev.h>
-
-#define LM3559_NAME "lm3559"
-#define LM3560_NAME "lm3560"
-#define LM3560_I2C_ADDR (0x53)
-
-/* FLASH Brightness
- * min 62500uA, step 62500uA, max 1000000uA
- */
-#define LM3560_FLASH_BRT_MIN 62500
-#define LM3560_FLASH_BRT_STEP 62500
-#define LM3560_FLASH_BRT_MAX 1000000
-#define LM3560_FLASH_BRT_uA_TO_REG(a) \
- ((a) < LM3560_FLASH_BRT_MIN ? 0 : \
- (((a) - LM3560_FLASH_BRT_MIN) / LM3560_FLASH_BRT_STEP))
-#define LM3560_FLASH_BRT_REG_TO_uA(a) \
- ((a) * LM3560_FLASH_BRT_STEP + LM3560_FLASH_BRT_MIN)
-
-/* FLASH TIMEOUT DURATION
- * min 32ms, step 32ms, max 1024ms
- */
-#define LM3560_FLASH_TOUT_MIN 32
-#define LM3560_FLASH_TOUT_STEP 32
-#define LM3560_FLASH_TOUT_MAX 1024
-#define LM3560_FLASH_TOUT_ms_TO_REG(a) \
- ((a) < LM3560_FLASH_TOUT_MIN ? 0 : \
- (((a) - LM3560_FLASH_TOUT_MIN) / LM3560_FLASH_TOUT_STEP))
-#define LM3560_FLASH_TOUT_REG_TO_ms(a) \
- ((a) * LM3560_FLASH_TOUT_STEP + LM3560_FLASH_TOUT_MIN)
-
-/* TORCH BRT
- * min 31250uA, step 31250uA, max 250000uA
- */
-#define LM3560_TORCH_BRT_MIN 31250
-#define LM3560_TORCH_BRT_STEP 31250
-#define LM3560_TORCH_BRT_MAX 250000
-#define LM3560_TORCH_BRT_uA_TO_REG(a) \
- ((a) < LM3560_TORCH_BRT_MIN ? 0 : \
- (((a) - LM3560_TORCH_BRT_MIN) / LM3560_TORCH_BRT_STEP))
-#define LM3560_TORCH_BRT_REG_TO_uA(a) \
- ((a) * LM3560_TORCH_BRT_STEP + LM3560_TORCH_BRT_MIN)
-
-enum lm3560_led_id {
- LM3560_LED0 = 0,
- LM3560_LED1,
- LM3560_LED_MAX
-};
-
-enum lm3560_peak_current {
- LM3560_PEAK_1600mA = 0x00,
- LM3560_PEAK_2300mA = 0x20,
- LM3560_PEAK_3000mA = 0x40,
- LM3560_PEAK_3600mA = 0x60
-};
-
-#endif /* __LM3560_H__ */
--
2.51.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v6 1/6] dt-bindings: leds: Document TI LM3560 Synchronous Boost Flash Driver
2026-05-06 6:48 ` [PATCH v6 1/6] dt-bindings: leds: Document TI LM3560 Synchronous Boost Flash Driver Svyatoslav Ryhel
@ 2026-05-06 17:03 ` Conor Dooley
0 siblings, 0 replies; 8+ messages in thread
From: Conor Dooley @ 2026-05-06 17:03 UTC (permalink / raw)
To: Svyatoslav Ryhel
Cc: Lee Jones, Pavel Machek, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sakari Ailus, Mauro Carvalho Chehab, linux-leds,
devicetree, linux-kernel, linux-media
[-- Attachment #1: Type: text/plain, Size: 304 bytes --]
On Wed, May 06, 2026 at 09:48:42AM +0300, Svyatoslav Ryhel wrote:
> Document TI LM3559 and LM3560 Synchronous Boost Flash Driver used for
> camera flash LEDs.
>
> Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-05-06 17:03 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-06 6:48 [PATCH v6 0/6] media: lm3560: convert to use OF bindings Svyatoslav Ryhel
2026-05-06 6:48 ` [PATCH v6 1/6] dt-bindings: leds: Document TI LM3560 Synchronous Boost Flash Driver Svyatoslav Ryhel
2026-05-06 17:03 ` Conor Dooley
2026-05-06 6:48 ` [PATCH v6 2/6] media: i2c: lm3560: Fix v4l2 subdev registration Svyatoslav Ryhel
2026-05-06 6:48 ` [PATCH v6 3/6] media: i2c: lm3560: Optimize mutex lock usage Svyatoslav Ryhel
2026-05-06 6:48 ` [PATCH v6 4/6] media: i2c: lm3560: Convert to use OF bindings Svyatoslav Ryhel
2026-05-06 6:48 ` [PATCH v6 5/6] media: i2c: lm3560: Add support for PM features Svyatoslav Ryhel
2026-05-06 6:48 ` [PATCH v6 6/6] media: i2c: lm3560: Add proper support for LM3559 Svyatoslav Ryhel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox