[PATCH v6 6/6] media: i2c: lm3560: Add proper support for LM3559

From: Svyatoslav Ryhel

Date: Wed May 06 2026 - 02:53:49 EST


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@xxxxxxxxx>
---
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", &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@xxxxxxxxx>
- * Ldd-Mlp <ldd-mlp@xxxxxxxxxxx>
- */
-
-#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