media: i2c: dw9763: optimize open/close procedure to reduce noise

1. add regulotor control to fix open i2c error issue.
2. fix move time unit to ms not us.
3. optimize open/close procedure, reduce vcm collision noise.
4. use v4l2_dbg replace dev_dbg for dynamic print debug info.

Signed-off-by: Wang Panzhenzhuan <randy.wang@rock-chips.com>
Change-Id: I20c65ef2d4623b6338699874c4c05b42d9d2d863
This commit is contained in:
Wang Panzhenzhuan
2023-08-16 06:54:01 +00:00
committed by Tao Huang
parent fb0c004d7e
commit df3536ceae

View File

@@ -15,12 +15,14 @@
#include <media/v4l2-device.h>
#include <linux/rk_vcm_head.h>
#include <linux/compat.h>
#include <linux/regulator/consumer.h>
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x0)
#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x1)
#define DW9763_NAME "dw9763"
#define DW9763_MAX_CURRENT 120U
#define DW9763_MAX_REG 1023U
#define DW9763_GRADUAL_MOVELENS_STEPS 32
#define DW9763_DEFAULT_START_CURRENT 20
#define DW9763_DEFAULT_RATED_CURRENT 90
@@ -41,6 +43,10 @@ enum mode_e {
DIRECT_MODE,
};
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "debug level (0-2)");
/* dw9763 device structure */
struct dw9763_device {
struct v4l2_ctrl_handler ctrls_vcm;
@@ -69,6 +75,8 @@ struct dw9763_device {
struct rk_cam_vcm_cfg vcm_cfg;
int max_ma;
struct mutex lock;
struct regulator *supply;
bool power_on;
};
static inline struct dw9763_device *to_dw9763_vcm(struct v4l2_ctrl *ctrl)
@@ -88,6 +96,7 @@ static int dw9763_write_reg(struct i2c_client *client, u8 reg,
u8 buf[5];
u8 *val_p;
__be32 val_be;
struct dw9763_device *dev_vcm = i2c_get_clientdata(client);
if (len > 4)
return -EINVAL;
@@ -106,7 +115,7 @@ static int dw9763_write_reg(struct i2c_client *client, u8 reg,
dev_err(&client->dev, "Failed to write 0x%04x,0x%x\n", reg, val);
return -EIO;
}
dev_dbg(&client->dev, "succeed to write 0x%04x,0x%x\n", reg, val);
v4l2_dbg(1, debug, &dev_vcm->sd, "succeed to write 0x%04x,0x%x\n", reg, val);
return 0;
}
@@ -120,6 +129,7 @@ static int dw9763_read_reg(struct i2c_client *client,
u8 *data_be_p;
__be32 data_be = 0;
int ret;
struct dw9763_device *dev_vcm = i2c_get_clientdata(client);
if (len > 4 || !len)
return -EINVAL;
@@ -143,6 +153,8 @@ static int dw9763_read_reg(struct i2c_client *client,
*val = be32_to_cpu(data_be);
v4l2_dbg(1, debug, &dev_vcm->sd, "succeed to read 0x%04x,0x%x\n", reg, *val);
return 0;
}
@@ -204,11 +216,11 @@ static unsigned int dw9763_move_time(struct dw9763_device *dev_vcm,
break;
}
dev_dbg(&client->dev,
v4l2_dbg(1, debug, &dev_vcm->sd,
"%s: vcm_movefull_t is: %d us\n",
__func__, move_time_us);
return move_time_us;
return ((move_time_us + 500) / 1000);
}
static int dw9763_set_dac(struct dw9763_device *dev_vcm,
@@ -228,7 +240,7 @@ static int dw9763_set_dac(struct dw9763_device *dev_vcm,
ret = dw9763_write_reg(client, 0x03, 2, dest_dac);
if (ret != 0)
goto err;
dev_dbg(&client->dev,
v4l2_dbg(1, debug, &dev_vcm->sd,
"%s: set reg val %d\n", __func__, dest_dac);
return ret;
@@ -249,7 +261,7 @@ static int dw9763_get_dac(struct dw9763_device *dev_vcm, unsigned int *cur_dac)
goto err;
*cur_dac = abs_step;
dev_dbg(&client->dev, "%s: get dac %d\n", __func__, *cur_dac);
v4l2_dbg(1, debug, &dev_vcm->sd, "%s: get dac %d\n", __func__, *cur_dac);
return 0;
@@ -279,7 +291,7 @@ static int dw9763_get_pos(struct dw9763_device *dev_vcm,
abs_step = 0;
*cur_pos = abs_step;
dev_dbg(&client->dev, "%s: get position %d\n", __func__, *cur_pos);
v4l2_dbg(1, debug, &dev_vcm->sd, "%s: get position %d\n", __func__, *cur_pos);
return 0;
err:
@@ -293,7 +305,6 @@ static int dw9763_set_pos(struct dw9763_device *dev_vcm,
{
int ret;
unsigned int position = 0;
struct i2c_client *client = dev_vcm->client;
if (dest_pos >= VCMDRV_MAX_LOG)
position = dev_vcm->start_current;
@@ -308,7 +319,8 @@ static int dw9763_set_pos(struct dw9763_device *dev_vcm,
dev_vcm->current_related_pos = dest_pos;
ret = dw9763_set_dac(dev_vcm, position);
dev_dbg(&client->dev, "%s: set position %d, dac %d\n", __func__, dest_pos, position);
v4l2_dbg(1, debug, &dev_vcm->sd, "%s: set position %d, dac %d\n",
__func__, dest_pos, position);
return ret;
}
@@ -331,7 +343,7 @@ static int dw9763_set_ctrl(struct v4l2_ctrl *ctrl)
long mv_us;
int ret = 0;
dev_dbg(&client->dev, "ctrl->id: 0x%x, ctrl->val: 0x%x\n",
v4l2_dbg(1, debug, &dev_vcm->sd, "ctrl->id: 0x%x, ctrl->val: 0x%x\n",
ctrl->id, ctrl->val);
if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) {
@@ -345,9 +357,9 @@ static int dw9763_set_ctrl(struct v4l2_ctrl *ctrl)
ret = dw9763_set_pos(dev_vcm, dest_pos);
dev_vcm->move_us = dev_vcm->vcm_movefull_t;
dev_vcm->move_us = dev_vcm->vcm_movefull_t * 1000;
dev_dbg(&client->dev,
v4l2_dbg(1, debug, &dev_vcm->sd,
"dest_pos %d, move_us %ld\n",
dest_pos, dev_vcm->move_us);
@@ -373,9 +385,19 @@ static const struct v4l2_ctrl_ops dw9763_vcm_ctrl_ops = {
.s_ctrl = dw9763_set_ctrl,
};
static int dw9763_init(struct i2c_client *client);
static int dw9763_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
int rval;
struct dw9763_device *dev_vcm = sd_to_dw9763_vcm(sd);
unsigned int move_time;
int dac = dev_vcm->start_current;
struct i2c_client *client = dev_vcm->client;
#ifdef CONFIG_PM
v4l2_info(sd, "%s: enter, power.usage_count(%d)!\n", __func__,
atomic_read(&sd->dev->power.usage_count));
#endif
rval = pm_runtime_get_sync(sd->dev);
if (rval < 0) {
@@ -383,12 +405,72 @@ static int dw9763_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
return rval;
}
dw9763_init(client);
v4l2_dbg(1, debug, sd, "%s: current_lens_pos %d, current_related_pos %d\n",
__func__, dev_vcm->current_lens_pos, dev_vcm->current_related_pos);
move_time = 1000 * dw9763_move_time(dev_vcm, DW9763_GRADUAL_MOVELENS_STEPS);
while (dac <= dev_vcm->current_lens_pos) {
dw9763_set_dac(dev_vcm, dac);
usleep_range(move_time, move_time + 100);
dac += DW9763_GRADUAL_MOVELENS_STEPS;
if (dac > dev_vcm->current_lens_pos)
break;
}
if (dac > dev_vcm->current_lens_pos) {
dac = dev_vcm->current_lens_pos;
dw9763_set_dac(dev_vcm, dac);
}
#ifdef CONFIG_PM
v4l2_info(sd, "%s: exit, power.usage_count(%d)!\n", __func__,
atomic_read(&sd->dev->power.usage_count));
#endif
return 0;
}
static int dw9763_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
struct dw9763_device *dev_vcm = sd_to_dw9763_vcm(sd);
int dac = dev_vcm->current_lens_pos;
unsigned int move_time;
int ret;
struct i2c_client *client = dev_vcm->client;
#ifdef CONFIG_PM
v4l2_info(sd, "%s: enter, power.usage_count(%d)!\n", __func__,
atomic_read(&sd->dev->power.usage_count));
#endif
v4l2_dbg(1, debug, sd, "%s: current_lens_pos %d, current_related_pos %d\n",
__func__, dev_vcm->current_lens_pos, dev_vcm->current_related_pos);
dac -= DW9763_GRADUAL_MOVELENS_STEPS;
move_time = 1000 * dw9763_move_time(dev_vcm, DW9763_GRADUAL_MOVELENS_STEPS);
while (dac >= DW9763_GRADUAL_MOVELENS_STEPS) {
dw9763_set_dac(dev_vcm, dac);
usleep_range(move_time, move_time + 1000);
dac -= DW9763_GRADUAL_MOVELENS_STEPS;
if (dac <= 0)
break;
}
if (dac < DW9763_GRADUAL_MOVELENS_STEPS) {
dac = DW9763_GRADUAL_MOVELENS_STEPS / 2;
dw9763_set_dac(dev_vcm, dac);
}
/* set to power down mode */
ret = dw9763_write_reg(client, 0x02, 1, 0x01);
if (ret)
dev_err(&client->dev, "failed to set power down mode!\n");
pm_runtime_put(sd->dev);
#ifdef CONFIG_PM
v4l2_info(sd, "%s: exit, power.usage_count(%d)!\n", __func__,
atomic_read(&sd->dev->power.usage_count));
#endif
return 0;
}
@@ -442,7 +524,7 @@ static long dw9763_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
vcm_tim->vcm_end_t.tv_sec = dev_vcm->end_move_tv.tv_sec;
vcm_tim->vcm_end_t.tv_usec = dev_vcm->end_move_tv.tv_usec;
dev_dbg(&client->dev, "dw9763_get_move_res 0x%lx, 0x%lx, 0x%lx, 0x%lx\n",
v4l2_dbg(1, debug, &dev_vcm->sd, "dw9763_get_move_res 0x%lx, 0x%lx, 0x%lx, 0x%lx\n",
vcm_tim->vcm_start_t.tv_sec,
vcm_tim->vcm_start_t.tv_usec,
vcm_tim->vcm_end_t.tv_sec,
@@ -634,13 +716,50 @@ static inline int remove_sysfs_interfaces(struct device *dev)
}
#endif
static int __dw9763_set_power(struct dw9763_device *dw9763_dev, bool on)
static int __dw9763_set_power(struct dw9763_device *dw9763, bool on)
{
if (dw9763_dev->power_gpio)
gpiod_direction_output(dw9763_dev->power_gpio, on);
usleep_range(10000, 11000);
struct i2c_client *client = dw9763->client;
int ret = 0;
return 0;
dev_info(&client->dev, "%s(%d) on(%d)\n", __func__, __LINE__, on);
if (dw9763->power_on == !!on)
goto unlock_and_return;
if (on) {
ret = regulator_enable(dw9763->supply);
if (ret < 0) {
dev_err(&client->dev, "Failed to enable regulator\n");
goto unlock_and_return;
}
dw9763->power_on = true;
} else {
ret = regulator_disable(dw9763->supply);
if (ret < 0) {
dev_err(&client->dev, "Failed to disable regulator\n");
goto unlock_and_return;
}
dw9763->power_on = false;
}
unlock_and_return:
return ret;
}
static int dw9763_configure_regulator(struct dw9763_device *dw9763)
{
struct i2c_client *client = dw9763->client;
int ret = 0;
dw9763->supply = devm_regulator_get(&client->dev, "avdd");
if (IS_ERR(dw9763->supply)) {
ret = PTR_ERR(dw9763->supply);
if (ret != -EPROBE_DEFER)
dev_err(&client->dev, "could not get regulator avdd\n");
return ret;
}
dw9763->power_on = false;
return ret;
}
static int __maybe_unused dw9763_check_id(struct dw9763_device *dw9763_dev)
@@ -662,20 +781,6 @@ static int __maybe_unused dw9763_check_id(struct dw9763_device *dw9763_dev)
"Detected dw9763 vcm id:0x%x\n", DW9763_CHIP_ID);
return 0;
}
static int dw9763_probe_init(struct i2c_client *client)
{
int ret = 0;
/* Default goto power down mode when finished probe */
ret = dw9763_write_reg(client, 0x02, 1, 0x01);
if (ret)
goto err;
return 0;
err:
dev_err(&client->dev, "probe init failed with error %d\n", ret);
return -1;
}
static int dw9763_probe(struct i2c_client *client,
const struct i2c_device_id *id)
@@ -765,9 +870,11 @@ static int dw9763_probe(struct i2c_client *client,
dev_warn(&client->dev,
"Failed to get power-gpios, maybe no use\n");
}
/* enter power down mode */
dw9763_probe_init(client);
ret = dw9763_configure_regulator(dw9763_dev);
if (ret) {
dev_err(&client->dev, "Failed to get power regulator!\n");
return ret;
}
v4l2_i2c_subdev_init(&dw9763_dev->sd, client, &dw9763_ops);
dw9763_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
@@ -781,6 +888,10 @@ static int dw9763_probe(struct i2c_client *client,
if (ret < 0)
goto err_cleanup;
ret = dw9763_check_id(dw9763_dev);
if (ret)
goto err_power_off;
sd = &dw9763_dev->sd;
sd->entity.function = MEDIA_ENT_F_LENS;
@@ -823,6 +934,9 @@ static int dw9763_probe(struct i2c_client *client,
dev_info(&client->dev, "probing successful\n");
return 0;
err_power_off:
__dw9763_set_power(dw9763_dev, false);
err_cleanup:
dw9763_subdev_cleanup(dw9763_dev);
@@ -847,21 +961,18 @@ static int dw9763_init(struct i2c_client *client)
{
struct dw9763_device *dev_vcm = i2c_get_clientdata(client);
int ret = 0;
u32 ring = 0;
u32 mode_val = 0;
u32 algo_time = 0;
if (dev_vcm->step_mode == DIRECT_MODE)
return 0;
/* Delay 200us~300us */
usleep_range(200, 300);
ret = dw9763_write_reg(client, 0x02, 1, 0x00);
if (ret)
goto err;
usleep_range(100, 200);
if (dev_vcm->step_mode != DIRECT_MODE)
ring = 0x02;
ret = dw9763_write_reg(client, 0x02, 1, ring);
usleep_range(200, 300);
ret = dw9763_write_reg(client, 0x02, 1, 0x02);
if (ret)
goto err;
switch (dev_vcm->step_mode) {
@@ -895,13 +1006,15 @@ err:
static int __maybe_unused dw9763_vcm_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
int ret = 0;
struct dw9763_device *dev_vcm = i2c_get_clientdata(client);
struct v4l2_subdev *sd = &(dev_vcm->sd);
/* set to power down mode */
ret = dw9763_write_reg(client, 0x02, 1, 0x01);
if (ret)
dev_err(&client->dev, "failed to set power down mode!\n");
#ifdef CONFIG_PM
v4l2_dbg(1, debug, sd, "%s: enter, power.usage_count(%d)!\n", __func__,
atomic_read(&sd->dev->power.usage_count));
#endif
__dw9763_set_power(dev_vcm, false);
return 0;
}
@@ -909,9 +1022,14 @@ static int __maybe_unused dw9763_vcm_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct dw9763_device *dev_vcm = i2c_get_clientdata(client);
struct v4l2_subdev *sd = &(dev_vcm->sd);
#ifdef CONFIG_PM
v4l2_dbg(1, debug, sd, "%s: enter, power.usage_count(%d)!\n", __func__,
atomic_read(&sd->dev->power.usage_count));
#endif
__dw9763_set_power(dev_vcm, true);
dw9763_init(client);
dw9763_set_pos(dev_vcm, dev_vcm->current_related_pos);
return 0;
}