diff --git a/drivers/media/i2c/dw9807-vcm.c b/drivers/media/i2c/dw9807-vcm.c index 3ecdbee66b60..5e7f5404178e 100644 --- a/drivers/media/i2c/dw9807-vcm.c +++ b/drivers/media/i2c/dw9807-vcm.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -46,6 +47,9 @@ #define MAX_RETRY 10 +#define DW9807_PW_MIN_DELAY_US 100 +#define DW9807_PW_DELAY_RANGE_US 10 + struct dw9807_cfg { unsigned int idle_pos; unsigned int default_pos; @@ -56,6 +60,8 @@ struct dw9807_device { struct v4l2_subdev sd; u16 current_val; u16 idle_pos; + struct regulator *vdd; + struct notifier_block notifier; }; static inline struct dw9807_device *sd_to_dw9807_vcm( @@ -157,6 +163,66 @@ static int dw9807_ramp(struct i2c_client *client, int start, int end) return ret; } +static int dw9807_active(struct dw9807_device *dw9807_dev) +{ + struct i2c_client *client = v4l2_get_subdevdata(&dw9807_dev->sd); + const char tx_data[2] = { DW9807_CTL_ADDR, 0x00 }; + int ret; + + /* Power on */ + ret = i2c_master_send(client, tx_data, sizeof(tx_data)); + if (ret < 0) { + dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret); + return ret; + } + + return dw9807_ramp(client, dw9807_dev->idle_pos, dw9807_dev->current_val); +} + +static int dw9807_standby(struct dw9807_device *dw9807_dev) +{ + struct i2c_client *client = v4l2_get_subdevdata(&dw9807_dev->sd); + const char tx_data[2] = { DW9807_CTL_ADDR, 0x01 }; + int ret; + + if (abs(dw9807_dev->current_val - dw9807_dev->idle_pos) > DW9807_CTRL_STEPS) + dw9807_ramp(client, dw9807_dev->current_val, dw9807_dev->idle_pos); + + /* Power down */ + ret = i2c_master_send(client, tx_data, sizeof(tx_data)); + if (ret < 0) { + dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret); + return ret; + } + + return 0; +} + +static int dw9807_regulator_event(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct dw9807_device *dw9807_dev = + container_of(nb, struct dw9807_device, notifier); + + if (action & REGULATOR_EVENT_ENABLE) { + /* + * Initialisation delay between VDD low->high and the moment + * when the i2c command is available. + * From the datasheet, it should be 10ms + 2ms (max power + * up sequence duration) + */ + usleep_range(DW9807_PW_MIN_DELAY_US, + DW9807_PW_MIN_DELAY_US + + DW9807_PW_DELAY_RANGE_US); + + dw9807_active(dw9807_dev); + } else if (action & REGULATOR_EVENT_PRE_DISABLE) { + dw9807_standby(dw9807_dev); + } + + return 0; +} + static int dw9807_set_ctrl(struct v4l2_ctrl *ctrl) { struct dw9807_device *dev_vcm = container_of(ctrl->handler, @@ -256,6 +322,24 @@ static int dw9807_probe(struct i2c_client *client) if (dw9807_dev == NULL) return -ENOMEM; + dw9807_dev->vdd = devm_regulator_get_optional(&client->dev, "VDD"); + if (IS_ERR(dw9807_dev->vdd)) { + if (PTR_ERR(dw9807_dev->vdd) != -ENODEV) + return PTR_ERR(dw9807_dev->vdd); + + dw9807_dev->vdd = NULL; + } else { + dw9807_dev->notifier.notifier_call = dw9807_regulator_event; + + rval = regulator_register_notifier(dw9807_dev->vdd, + &dw9807_dev->notifier); + if (rval) { + dev_err(&client->dev, + "could not register regulator notifier\n"); + return rval; + } + } + cfg = (const struct dw9807_cfg *)i2c_get_match_data(client); if (cfg) { dw9807_dev->idle_pos = cfg->idle_pos; @@ -318,20 +402,11 @@ static int __maybe_unused dw9807_vcm_suspend(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct v4l2_subdev *sd = i2c_get_clientdata(client); struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); - const char tx_data[2] = { DW9807_CTL_ADDR, 0x01 }; - int ret; - if (abs(dw9807_dev->current_val - dw9807_dev->idle_pos) > DW9807_CTRL_STEPS) - dw9807_ramp(client, dw9807_dev->current_val, dw9807_dev->idle_pos); + if (dw9807_dev->vdd) + return regulator_disable(dw9807_dev->vdd); - /* Power down */ - ret = i2c_master_send(client, tx_data, sizeof(tx_data)); - if (ret < 0) { - dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret); - return ret; - } - - return 0; + return dw9807_standby(dw9807_dev); } /* @@ -345,19 +420,11 @@ static int __maybe_unused dw9807_vcm_resume(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct v4l2_subdev *sd = i2c_get_clientdata(client); struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); - const char tx_data[2] = { DW9807_CTL_ADDR, 0x00 }; - int ret; - /* Power on */ - ret = i2c_master_send(client, tx_data, sizeof(tx_data)); - if (ret < 0) { - dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret); - return ret; - } + if (dw9807_dev->vdd) + return regulator_enable(dw9807_dev->vdd); - dw9807_ramp(client, dw9807_dev->idle_pos, dw9807_dev->current_val); - - return 0; + return dw9807_active(dw9807_dev); } MODULE_DEVICE_TABLE(of, dw9807_of_table);