public inbox for devicetree@vger.kernel.org
 help / color / mirror / Atom feed
From: Svyatoslav Ryhel <clamor95@gmail.com>
To: Lee Jones <lee@kernel.org>, Pavel Machek <pavel@kernel.org>,
	Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	Sakari Ailus <sakari.ailus@linux.intel.com>,
	Mauro Carvalho Chehab <mchehab@kernel.org>,
	Svyatoslav Ryhel <clamor95@gmail.com>
Cc: linux-leds@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-media@vger.kernel.org
Subject: [PATCH v5 6/6] media: i2c: lm3560: Add proper support for LM3559
Date: Sun,  3 May 2026 19:44:45 +0300	[thread overview]
Message-ID: <20260503164445.215540-7-clamor95@gmail.com> (raw)
In-Reply-To: <20260503164445.215540-1-clamor95@gmail.com>

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 15741ea5684f..da12fbd7dc59 100644
--- a/drivers/media/i2c/lm3560.c
+++ b/drivers/media/i2c/lm3560.c
@@ -23,9 +23,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
@@ -40,12 +40,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
  *
@@ -62,6 +94,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;
@@ -82,6 +115,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)	\
@@ -137,15 +172,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);
@@ -160,15 +200,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);
@@ -308,6 +353,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);
 
@@ -336,13 +382,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,
@@ -483,6 +529,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);
@@ -509,16 +559,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;
@@ -547,6 +607,7 @@ static int lm3560_probe(struct i2c_client *client)
 	pm_runtime_enable(flash->dev);
 
 	for_each_available_child_of_node(dev_of_node(flash->dev), node) {
+		const struct lm3560_flash_config *config = flash->config;
 		u32 reg;
 
 		rval = of_property_read_u32(node, "reg", &reg);
@@ -555,11 +616,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;
 			of_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;
 			of_property_read_u32(node, "led-max-microamp",
 					     &flash->max_torch_brt[reg]);
 
@@ -617,24 +678,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


  parent reply	other threads:[~2026-05-03 16:45 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-03 16:44 [PATCH v5 0/6] media: lm3560: convert to use OF bindings Svyatoslav Ryhel
2026-05-03 16:44 ` [PATCH v5 1/6] dt-bindings: leds: Document TI LM3560 Synchronous Boost Flash Driver Svyatoslav Ryhel
2026-05-03 16:44 ` [PATCH v5 2/6] media: i2c: lm3560: Fix v4l2 subdev registration Svyatoslav Ryhel
2026-05-03 16:44 ` [PATCH v5 3/6] media: i2c: lm3560: Optimize mutex lock usage Svyatoslav Ryhel
2026-05-04  6:26   ` Sakari Ailus
2026-05-04  7:37     ` Svyatoslav Ryhel
2026-05-04  7:56       ` Sakari Ailus
2026-05-03 16:44 ` [PATCH v5 4/6] media: i2c: lm3560: Convert to use OF bindings Svyatoslav Ryhel
2026-05-04  6:36   ` Sakari Ailus
2026-05-04  7:40     ` Svyatoslav Ryhel
2026-05-04  8:08       ` Sakari Ailus
2026-05-04  8:42         ` Svyatoslav Ryhel
2026-05-03 16:44 ` [PATCH v5 5/6] media: i2c: lm3560: Add support for PM features Svyatoslav Ryhel
2026-05-04  6:37   ` Sakari Ailus
2026-05-04  7:40     ` Svyatoslav Ryhel
2026-05-04  9:37       ` Svyatoslav Ryhel
2026-05-04 10:14         ` Sakari Ailus
2026-05-03 16:44 ` Svyatoslav Ryhel [this message]
2026-05-04  5:35 ` [PATCH v5 0/6] media: lm3560: convert to use OF bindings Svyatoslav Ryhel
2026-05-04  6:25   ` Sakari Ailus

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260503164445.215540-7-clamor95@gmail.com \
    --to=clamor95@gmail.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=lee@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-leds@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=mchehab@kernel.org \
    --cc=pavel@kernel.org \
    --cc=robh@kernel.org \
    --cc=sakari.ailus@linux.intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox