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 v4 5/5] media: i2c: lm3560: Add support for PM features
Date: Tue, 28 Apr 2026 14:39:22 +0300 [thread overview]
Message-ID: <20260428113923.112920-6-clamor95@gmail.com> (raw)
In-Reply-To: <20260428113923.112920-1-clamor95@gmail.com>
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 1052c11f42e8..29f3ee98e0ca 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_in_use(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_in_use(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 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 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;
@@ -441,6 +493,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);
@@ -468,9 +531,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(flash->dev, node) {
rval = fwnode_property_read_u32(node, "reg", ®);
@@ -490,10 +563,10 @@ static int lm3560_probe(struct i2c_client *client)
rval = lm3560_subdev_init(flash, reg, node);
if (rval < 0) {
fwnode_handle_put(node);
- 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
prev parent reply other threads:[~2026-04-28 11:39 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-28 11:39 [PATCH v4 0/5] media: lm3560: convert to use OF bindings Svyatoslav Ryhel
2026-04-28 11:39 ` [PATCH v4 1/5] dt-bindings: leds: Document TI LM3560 Synchronous Boost Flash Driver Svyatoslav Ryhel
2026-04-28 11:39 ` [PATCH v4 2/5] media: i2c: lm3560: Fix v4l2 subdev registration Svyatoslav Ryhel
2026-04-28 11:39 ` [PATCH v4 3/5] media: i2c: lm3560: Optimize mutex lock usage Svyatoslav Ryhel
2026-04-28 11:39 ` [PATCH v4 4/5] media: i2c: lm3560: Convert to use OF bindings Svyatoslav Ryhel
2026-04-28 11:39 ` Svyatoslav Ryhel [this message]
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=20260428113923.112920-6-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