名称 | 定义 |
---|---|
启动电流 | VCM 刚好能推动模组镜头从模组镜头可移动行程最近端(模组远焦)移动,此时 VCM driver ic 的输出电流值定义为启动电流 |
额定电流 | VCM 刚好推动模组镜头至模组镜头可移动行程的最远端(模组近焦),此时 VCM driver ic 的输出电流值定义为额定电流 |
VCM 电流输出模式 | VCM 移动过程中会产生振荡,VCM driver ic 电流输出变化需要考虑vcm 的振荡周期,以便最大程度减小振荡,输出模式决定了输出电流改变至目标值的时间 |
例子:
vm149c: vm149c@0c { // vcm 驱动配置,支持 AF 时需要有这个设置
compatible = "silicon touch,vm149c";
status = "okay";
reg = <0x0c>;
rockchip,vcm-start-current = <0>; // 马达的启动电流
rockchip,vcm-rated-current = <100>; // 马达的额定电流
rockchip,vcm-step-mode = <4>; // 马达驱动 ic 的电流输出模式
rockchip,camera-module-index = <0>; // 模组编号
rockchip,camera-module-facing = "back"; // 模组朝向,有"back"和"front"
};
gc8034: gc8034@37 {
......
lens-focus = <&vm149c>; // vcm 驱动设置,支持 AF 时需要有这个设置
......
};
根据 struct i2c_driver 描述,主要实现以下几部分:
struct driver.name
struct driver.pm
struct driver. of_match_table
probe 函数
remove 函数
1、RK 私有资源定义, 命名方式如 rockchip,camera-module-xxx,主要是提供设备参数和 Camera 设备进行匹配。
2、VCM 参数定义,命名方式如 rockchip,vcm-xxx, 主要涉及硬件参数启动电流、额定电流、移动模式, 参数跟马达移动的范围和速度相关。
ret = of_property_read_u32(np, RKMODULE_CAMERA_MODULE_INDEX,
&vm149c_dev->module_index);
ret |= of_property_read_string(np, RKMODULE_CAMERA_MODULE_FACING,
&vm149c_dev->module_facing);
if (ret) {
dev_err(&client->dev,
"could not get module information!\n");
return -EINVAL;
}
...
memset(facing, 0, sizeof(facing));
if (strcmp(vm149c_dev->module_facing, "back") == 0)
facing[0] = 'b';
else
facing[0] = 'f';
snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
vm149c_dev->module_index, facing,
VM149C_NAME, dev_name(sd->dev));
ret = v4l2_async_register_subdev(sd);
if (ret)
dev_err(&client->dev, "v4l2 async register subdev failed\n");
v4l2 子设备: v4l2_i2c_subdev_init, RK VCM 驱动要求 subdev 拥有自己的设备节点供用户态 camera_engine 访问,通过该设备节点实现调焦控制;
v4l2_i2c_subdev_init(&vm149c_dev->sd, client, &vm149c_ops);
vm149c_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
vm149c_dev->sd.internal_ops = &vm149c_int_ops;
ret = vm149c_init_controls(vm149c_dev);
if (ret)
goto err_cleanup;
ret = media_entity_pads_init(&vm149c_dev->sd.entity, 0, NULL);
if (ret < 0)
goto err_cleanup;
sd = &vm149c_dev->sd;
sd->entity.function = MEDIA_ENT_F_LENS;
RK AF 算法将模组镜头整个可移动行程的位置参数定义为[0,64],模组镜头整个可移动行程在 VCM 驱动电流上对应的变化范围为[启动电流,额定电流],该函数中建议实现这 2 者间的映射换算关系;
v4l2_subdev_core_ops主要实现.ioctl(.compat_ioctl32)回调函数。
该回调主要实现 RK 私有控制命令,涉及:
RK_VIDIOC_VCM_TIMEINFO | camera_engine通过该命令获取此次镜头移动所需时间, 据此来判断镜头何时停止以及 CIS 帧曝光时间段是否与镜头移动时间段有重叠;镜头移动时间与镜头移动距离、 VCM driver ic 电流输出模式相关。 |
---|
v4l2_ctrl_ops 主要实现,g_volatile_ctrl和.s_ctrl回调函数。
以标准的 v4l2 control 实现了以下命令:
V4L2_CID_FOCUS_ABSOLUTE | camera_engine 通过该命令来设置和获取镜头的绝对位置, RK AF 算法中将镜头整个可移动行程的位置参数定义为[0,64]。 |
---|
Define core ops callbacks for subdevs.自定义 ioctl 的实现函数,主要包含获取马达移动的时间信息,
成员名称 | 描述 |
---|---|
.ioctl | called at the end of ioctl() syscall handler at the V4L2core.used to provide support for private ioctls used on the driver. |
.compat_ioctl32 | called when a 32 bits application uses a 64 bits Kernel, in order to fix data passed from/to userspace.in order to fix data passed from/to userspace. |
static const struct v4l2_subdev_core_ops vm149c_core_ops = {
.ioctl = vm149c_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = vm149c_compat_ioctl32
#endif
};
The control operations that the driver has to provide.
成员名称 | 描述 |
---|---|
.g_volatile_ctrl | Get a new value for this control. Generally only relevant for volatile (and usually read-only) controls such as a control that returns the current signal strength which changes continuously. |
.s_ctrl | Actually set the new control value. s_ctrl is compulsory.The ctrl->handler->lock is held when these ops are called,so no one else can access controls owned by that handler. |
static const struct v4l2_ctrl_ops vm149c_vcm_ctrl_ops = {
.g_volatile_ctrl = vm149c_get_ctrl,
.s_ctrl = vm149c_set_ctrl,
};
static int vm149c_get_ctrl(struct v4l2_ctrl *ctrl)
{
struct vm149c_device *dev_vcm = to_vm149c_vcm(ctrl);
if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE)
return vm149c_get_pos(dev_vcm, &ctrl->val);
return -EINVAL;
}
static int vm149c_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct vm149c_device *dev_vcm = to_vm149c_vcm(ctrl);
struct i2c_client *client = v4l2_get_subdevdata(&dev_vcm->sd);
unsigned int dest_pos = ctrl->val;
int move_pos;
long int mv_us;
int ret = 0;
if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) {
if (dest_pos > VCMDRV_MAX_LOG) {
dev_info(&client->dev,
"%s dest_pos is error. %d > %d\n",
__func__, dest_pos, VCMDRV_MAX_LOG);
return -EINVAL;
}
/* calculate move time */
move_pos = dev_vcm->current_related_pos - dest_pos;
if (move_pos < 0)
move_pos = -move_pos;
ret = vm149c_set_pos(dev_vcm, dest_pos);
dev_vcm->move_ms =
((dev_vcm->vcm_movefull_t *
(uint32_t)move_pos) /
VCMDRV_MAX_LOG);
dev_dbg(&client->dev, "dest_pos %d, move_ms %ld\n",
dest_pos, dev_vcm->move_ms);
dev_vcm->start_move_tv = ns_to_timeval(ktime_get_ns());
mv_us = dev_vcm->start_move_tv.tv_usec +
dev_vcm->move_ms * 1000;
if (mv_us >= 1000000) {
dev_vcm->end_move_tv.tv_sec =
dev_vcm->start_move_tv.tv_sec + 1;
dev_vcm->end_move_tv.tv_usec = mv_us - 1000000;
} else {
dev_vcm->end_move_tv.tv_sec =
dev_vcm->start_move_tv.tv_sec;
dev_vcm->end_move_tv.tv_usec = mv_us;
}
}
return ret;
}
自定义 ioctl 的实现函数,主要包含获取马达移动的时间信息,
实现了自定义 RK_VIDIOC_COMPAT_VCM_TIMEINFO。
static long vm149c_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct vm149c_device *vm149c_dev = sd_to_vm149c_vcm(sd);
struct rk_cam_vcm_tim *vcm_tim;
struct rk_cam_vcm_cfg *vcm_cfg;
int ret = 0;
if (cmd == RK_VIDIOC_VCM_TIMEINFO) {
vcm_tim = (struct rk_cam_vcm_tim *)arg;
vcm_tim->vcm_start_t.tv_sec = vm149c_dev->start_move_tv.tv_sec;
vcm_tim->vcm_start_t.tv_usec = vm149c_dev->start_move_tv.tv_usec;
vcm_tim->vcm_end_t.tv_sec = vm149c_dev->end_move_tv.tv_sec;
vcm_tim->vcm_end_t.tv_usec = vm149c_dev->end_move_tv.tv_usec;
dev_dbg(&client->dev, "vm149c_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, vcm_tim->vcm_end_t.tv_usec);
} else if (cmd == RK_VIDIOC_GET_VCM_CFG) {
vcm_cfg = (struct rk_cam_vcm_cfg *)arg;
vcm_cfg->start_ma = vm149c_dev->vcm_cfg.start_ma;
vcm_cfg->rated_ma = vm149c_dev->vcm_cfg.rated_ma;
vcm_cfg->step_mode = vm149c_dev->vcm_cfg.step_mode;
} else if (cmd == RK_VIDIOC_SET_VCM_CFG) {
vcm_cfg = (struct rk_cam_vcm_cfg *)arg;
vm149c_dev->vcm_cfg.start_ma = vcm_cfg->start_ma;
vm149c_dev->vcm_cfg.rated_ma = vcm_cfg->rated_ma;
vm149c_dev->vcm_cfg.step_mode = vcm_cfg->step_mode;
vm149c_update_vcm_cfg(vm149c_dev);
} else {
dev_err(&client->dev,
"cmd 0x%x not supported\n", cmd);
return -EINVAL;
}
return ret;
}