From 00d76e6608fc6f352bf38980f46240a761916eda Mon Sep 17 00:00:00 2001 From: Hu Kejun Date: Tue, 14 Feb 2023 10:59:19 +0800 Subject: [PATCH] media: i2c: dw9714: fix i2c error in resume function Signed-off-by: Hu Kejun Change-Id: Ieb2b4f0c7a3bdef20392b746c7330fb6a0a72df1 --- drivers/media/i2c/dw9714.c | 345 ++++++++++++++++++++++++------------- 1 file changed, 222 insertions(+), 123 deletions(-) diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c index df0d80a56e9d..685234a5b50a 100644 --- a/drivers/media/i2c/dw9714.c +++ b/drivers/media/i2c/dw9714.c @@ -15,6 +15,7 @@ #include #include #include +#include #define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x0) #define DW9714_NAME "dw9714" @@ -95,6 +96,9 @@ struct dw9714_device { struct rk_cam_vcm_cfg vcm_cfg; struct gpio_desc *xsd_gpio; + struct regulator *supply; + struct i2c_client *client; + bool power_on; }; struct TimeTabel_s { @@ -631,8 +635,98 @@ static const struct v4l2_ctrl_ops dw9714_vcm_ctrl_ops = { .s_ctrl = dw9714_set_ctrl, }; +static int dw9714_init(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct dw9714_device *dw9714_dev = sd_to_dw9714_vcm(sd); + unsigned char data = 0x0; + int ret = 0; + + if (dw9714_dev->adcanced_mode) { + // need to wait 1ms after poweron + usleep_range(1000, 1200); + // Advanced Mode + ret = dw9714_write_msg(client, 0xED, 0xAB); + if (ret) + goto err; + // Power down + ret = dw9714_write_msg(client, DW9714_ADVMODE_CONTROL, 0x01); + if (ret) + goto err; + // active + ret = dw9714_write_msg(client, DW9714_ADVMODE_CONTROL, 0x00); + if (ret) + goto err; + // delay 1ms + usleep_range(1000, 1200); + // SAC mode & nrc_time & nrc_infl + data = DW9714_ADVMODE_RING_EN << 7 | + (dw9714_dev->nrc_infl & 0x3) << 5 | + (dw9714_dev->nrc_time & 0x1) << 4 | + (dw9714_dev->sac_mode & 0xF); + ret = dw9714_write_msg(client, DW9714_ADVMODE_SAC_CFG, data); + if (ret) + goto err; + // Set Tvib (PRESC[1:0] ) + ret = dw9714_write_msg(client, DW9714_ADVMODE_PRESC, dw9714_dev->sac_prescl); + if (ret) + goto err; + // Set Tvib (SACT[6:0] ) + ret = dw9714_write_msg(client, DW9714_ADVMODE_SAC_TIME, dw9714_dev->sac_time); + if (ret) + goto err; + // nrc preset + ret = dw9714_write_msg(client, DW9714_ADVMODE_PRESET, dw9714_dev->nrc_preset); + if (ret) + goto err; + // nrc en & nrc mode + data = (dw9714_dev->nrc_en & 0x1) << 1 | + (dw9714_dev->nrc_mode & 0x1); + ret = dw9714_write_msg(client, DW9714_ADVMODE_NRC, data); + if (ret) + goto err; + } else { + // need to wait 12ms after poweron + usleep_range(12000, 12500); + + ret = dw9714_write_msg(client, 0xEC, 0xA3); + if (ret) + goto err; + + data = (dw9714_dev->mclk & 0x3) | 0x04 | + ((dw9714_dev->dlc_enable << 0x3) & 0x08); + ret = dw9714_write_msg(client, 0xA1, data); + if (ret) + goto err; + + data = (dw9714_dev->t_src << 0x3) & 0xf8; + ret = dw9714_write_msg(client, 0xF2, data); + if (ret) + goto err; + + ret = dw9714_write_msg(client, 0xDC, 0x51); + if (ret) + goto err; + + /* set normal mode */ + ret = dw9714_write_msg(client, 0xDF, 0x5B); + if (ret != 0) + dev_err(&client->dev, + "%s: failed with error %d\n", __func__, ret); + } + + return 0; +err: + dev_err(&client->dev, "failed with error %d\n", ret); + return -1; +} + static int dw9714_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { + struct dw9714_device *dev_vcm = sd_to_dw9714_vcm(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + unsigned int move_time; + int dac = 0; int rval; rval = pm_runtime_get_sync(sd->dev); @@ -641,11 +735,50 @@ static int dw9714_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) return rval; } + dw9714_init(client); + + dev_dbg(&client->dev, "%s: current_lens_pos %d, current_related_pos %d\n", + __func__, dev_vcm->current_lens_pos, dev_vcm->current_related_pos); + move_time = 1000 * dw9714_move_time(dev_vcm, DW9714_GRADUAL_MOVELENS_STEPS); + while (dac <= dev_vcm->current_lens_pos) { + dw9714_set_dac(dev_vcm, dac); + usleep_range(move_time, move_time + 1000); + dac += DW9714_GRADUAL_MOVELENS_STEPS; + if (dac >= dev_vcm->current_lens_pos) + break; + } + + if (dac > dev_vcm->current_lens_pos) { + dac = dev_vcm->current_lens_pos; + dw9714_set_dac(dev_vcm, dac); + } + return 0; } static int dw9714_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { + struct dw9714_device *dev_vcm = sd_to_dw9714_vcm(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int dac = dev_vcm->current_lens_pos; + unsigned int move_time; + + dev_dbg(&client->dev, "%s: current_lens_pos %d, current_related_pos %d\n", + __func__, dev_vcm->current_lens_pos, dev_vcm->current_related_pos); + move_time = 1000 * dw9714_move_time(dev_vcm, DW9714_GRADUAL_MOVELENS_STEPS); + while (dac >= 0) { + dw9714_set_dac(dev_vcm, dac); + usleep_range(move_time, move_time + 1000); + dac -= DW9714_GRADUAL_MOVELENS_STEPS; + if (dac <= 0) + break; + } + + if (dac < 0) { + dac = 0; + dw9714_set_dac(dev_vcm, dac); + } + pm_runtime_put(sd->dev); return 0; @@ -899,6 +1032,76 @@ static inline int remove_sysfs_interfaces(struct device *dev) } #endif +static int dw9714_set_power(struct dw9714_device *dw9714, bool on) +{ + struct i2c_client *client = dw9714->client; + int ret = 0; + + dev_info(&client->dev, "%s(%d) on(%d)\n", __func__, __LINE__, on); + + if (dw9714->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = regulator_enable(dw9714->supply); + if (ret < 0) { + dev_err(&client->dev, "Failed to enable regulator\n"); + goto unlock_and_return; + } + dw9714->power_on = true; + } else { + ret = regulator_disable(dw9714->supply); + if (ret < 0) { + dev_err(&client->dev, "Failed to disable regulator\n"); + goto unlock_and_return; + } + dw9714->power_on = false; + } + +unlock_and_return: + return ret; +} + +static int dw9714_check_i2c(struct dw9714_device *dw9714, + struct i2c_client *client) +{ + struct device *dev = &client->dev; + int ret; + + if (dw9714->adcanced_mode) { + // need to wait 1ms after poweron + usleep_range(1000, 1200); + // Advanced Mode + ret = dw9714_write_msg(client, 0xED, 0xAB); + } else { + // need to wait 12ms after poweron + usleep_range(12000, 12500); + ret = dw9714_write_msg(client, 0xEC, 0xA3); + } + if (!ret) + dev_info(dev, "check dw9714 connection OK!\n"); + else + dev_info(dev, "dw9714 not connect!\n"); + + return ret; +} + +static int dw9714_configure_regulator(struct dw9714_device *dw9714) +{ + struct i2c_client *client = dw9714->client; + int ret = 0; + + dw9714->supply = devm_regulator_get(&client->dev, "avdd"); + if (IS_ERR(dw9714->supply)) { + ret = PTR_ERR(dw9714->supply); + if (ret != -EPROBE_DEFER) + dev_err(&client->dev, "could not get regulator avdd\n"); + return ret; + } + dw9714->power_on = false; + return ret; +} + static int dw9714_parse_dt_property(struct i2c_client *client, struct dw9714_device *dev_vcm) { @@ -1051,6 +1254,13 @@ static int dw9714_parse_dt_property(struct i2c_client *client, return -EINVAL; } + dev_vcm->client = client; + ret = dw9714_configure_regulator(dev_vcm); + if (ret) { + dev_err(&client->dev, "Failed to get power regulator!\n"); + return ret; + } + dev_dbg(&client->dev, "current: %d, %d, %d, dlc_en: %d, t_src: %d, mclk: %d", dev_vcm->max_current, dev_vcm->start_current, @@ -1104,6 +1314,14 @@ static int dw9714_probe(struct i2c_client *client, if (ret < 0) goto err_cleanup; + ret = dw9714_set_power(dw9714_dev, true); + if (ret) + goto err_cleanup; + + ret = dw9714_check_i2c(dw9714_dev, client); + if (ret) + goto err_power_off; + sd = &dw9714_dev->sd; sd->entity.function = MEDIA_ENT_F_LENS; @@ -1135,6 +1353,8 @@ static int dw9714_probe(struct i2c_client *client, return 0; +err_power_off: + dw9714_set_power(dw9714_dev, false); err_cleanup: dw9714_subdev_cleanup(dw9714_dev); dev_err(&client->dev, "Probe failed: %d\n", ret); @@ -1153,115 +1373,13 @@ static int dw9714_remove(struct i2c_client *client) return 0; } -static int dw9714_init(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct dw9714_device *dw9714_dev = sd_to_dw9714_vcm(sd); - unsigned char data = 0x0; - int ret = 0; - - if (dw9714_dev->adcanced_mode) { - // need to wait 1ms after poweron - usleep_range(1000, 1200); - // Advanced Mode - ret = dw9714_write_msg(client, 0xED, 0xAB); - if (ret) - goto err; - // Power down - ret = dw9714_write_msg(client, DW9714_ADVMODE_CONTROL, 0x01); - if (ret) - goto err; - // active - ret = dw9714_write_msg(client, DW9714_ADVMODE_CONTROL, 0x00); - if (ret) - goto err; - // delay 1ms - usleep_range(1000, 1200); - // SAC mode & nrc_time & nrc_infl - data = DW9714_ADVMODE_RING_EN << 7 | - (dw9714_dev->nrc_infl & 0x3) << 5 | - (dw9714_dev->nrc_time & 0x1) << 4 | - (dw9714_dev->sac_mode & 0xF); - ret = dw9714_write_msg(client, DW9714_ADVMODE_SAC_CFG, data); - if (ret) - goto err; - // Set Tvib (PRESC[1:0] ) - ret = dw9714_write_msg(client, DW9714_ADVMODE_PRESC, dw9714_dev->sac_prescl); - if (ret) - goto err; - // Set Tvib (SACT[6:0] ) - ret = dw9714_write_msg(client, DW9714_ADVMODE_SAC_TIME, dw9714_dev->sac_time); - if (ret) - goto err; - // nrc preset - ret = dw9714_write_msg(client, DW9714_ADVMODE_PRESET, dw9714_dev->nrc_preset); - if (ret) - goto err; - // nrc en & nrc mode - data = (dw9714_dev->nrc_en & 0x1) << 1 | - (dw9714_dev->nrc_mode & 0x1); - ret = dw9714_write_msg(client, DW9714_ADVMODE_NRC, data); - if (ret) - goto err; - } else { - // need to wait 12ms after poweron - usleep_range(12000, 12500); - - ret = dw9714_write_msg(client, 0xEC, 0xA3); - if (ret) - goto err; - - data = (dw9714_dev->mclk & 0x3) | 0x04 | - ((dw9714_dev->dlc_enable << 0x3) & 0x08); - ret = dw9714_write_msg(client, 0xA1, data); - if (ret) - goto err; - - data = (dw9714_dev->t_src << 0x3) & 0xf8; - ret = dw9714_write_msg(client, 0xF2, data); - if (ret) - goto err; - - ret = dw9714_write_msg(client, 0xDC, 0x51); - if (ret) - goto err; - - /* set normal mode */ - ret = dw9714_write_msg(client, 0xDF, 0x5B); - if (ret != 0) - dev_err(&client->dev, - "%s: failed with error %d\n", __func__, ret); - } - - return 0; -err: - dev_err(&client->dev, "failed with error %d\n", ret); - return -1; -} - static int __maybe_unused dw9714_vcm_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct v4l2_subdev *sd = i2c_get_clientdata(client); struct dw9714_device *dev_vcm = sd_to_dw9714_vcm(sd); - int dac = dev_vcm->current_lens_pos; - unsigned int move_time; - dev_dbg(&client->dev, "%s: current_lens_pos %d, current_related_pos %d\n", - __func__, dev_vcm->current_lens_pos, dev_vcm->current_related_pos); - move_time = 1000 * dw9714_move_time(dev_vcm, DW9714_GRADUAL_MOVELENS_STEPS); - while (dac >= 0) { - dw9714_set_dac(dev_vcm, dac); - usleep_range(move_time, move_time + 1000); - dac -= DW9714_GRADUAL_MOVELENS_STEPS; - if (dac <= 0) - break; - } - - if (dac < 0) { - dac = 0; - dw9714_set_dac(dev_vcm, dac); - } + dw9714_set_power(dev_vcm, false); return 0; } @@ -1270,27 +1388,8 @@ static int __maybe_unused dw9714_vcm_resume(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct v4l2_subdev *sd = i2c_get_clientdata(client); struct dw9714_device *dev_vcm = sd_to_dw9714_vcm(sd); - unsigned int move_time; - int dac = 0; - - dw9714_init(client); - - dev_dbg(&client->dev, "%s: current_lens_pos %d, current_related_pos %d\n", - __func__, dev_vcm->current_lens_pos, dev_vcm->current_related_pos); - move_time = 1000 * dw9714_move_time(dev_vcm, DW9714_GRADUAL_MOVELENS_STEPS); - while (dac <= dev_vcm->current_lens_pos) { - dw9714_set_dac(dev_vcm, dac); - usleep_range(move_time, move_time + 1000); - dac += DW9714_GRADUAL_MOVELENS_STEPS; - if (dac >= dev_vcm->current_lens_pos) - break; - } - - if (dac > dev_vcm->current_lens_pos) { - dac = dev_vcm->current_lens_pos; - dw9714_set_dac(dev_vcm, dac); - } + dw9714_set_power(dev_vcm, true); return 0; }