diff options
| -rw-r--r-- | drivers/media/i2c/lm3560.c | 117 |
1 files 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, |
