瑞芯微Rockchip开发者社区
直播中

青sky

8年用户 1257经验值
擅长:模拟技术
私信 关注
[经验]

RK3568 VCM驱动移植是如何去实现的

VCM 设备注册(DTS)
RK 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 时需要有这个设置
  ......
};

VCM 驱动说明
驱动移植步骤
1.实现标准的 i2c 子设备驱动部分
根据 struct i2c_driver 描述,主要实现以下几部分:
struct driver.name
struct driver.pm
struct driver. of_match_table
probe 函数
remove 函数

probe 函数实现细节描述
VCM 设备资源获取,主要获取 DTS 资源
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!
");
    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
");

VCM v4l2 设备以及 media 实体的初始化

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;

AF 算法
RK AF 算法将模组镜头整个可移动行程的位置参数定义为[0,64],模组镜头整个可移动行程在 VCM 驱动电流上对应的变化范围为[启动电流,额定电流],该函数中建议实现这 2 者间的映射换算关系;
实现 v4l2 子设备驱动,主要实现以下 2 个成员:
1、struct v4l2_subdev_core_ops
v4l2_subdev_core_ops主要实现.ioctl(.compat_ioctl32)回调函数。
该回调主要实现 RK 私有控制命令,涉及:

2、struct v4l2_ctrl_ops
v4l2_ctrl_ops 主要实现,g_volatile_ctrl和.s_ctrl回调函数。
以标准的 v4l2 control 实现了以下命令:


数据类型简要说明
1、struct v4l2_subdev_core_ops
Define core ops callbacks for subdevs.自定义 ioctl 的实现函数,主要包含获取马达移动的时间信息,

static const struct v4l2_subdev_core_ops vm149c_core_ops = {
  .ioctl = vm149c_ioctl,
#ifdef CONFIG_COMPAT
  .compat_ioctl32 = vm149c_compat_ioctl32
#endif
};
2、struct v4l2_ctrl_ops
The control operations that the driver has to provide.

static const struct v4l2_ctrl_ops vm149c_vcm_ctrl_ops = {
  .g_volatile_ctrl = vm149c_get_ctrl,
  .s_ctrl = vm149c_set_ctrl,
};

API 简要说明
1、xxxx_get_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;
}
2、xxxx_set_ctrl : 设置马达的移动位置。
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
",
        __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
",
        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;
}
3、xxxx_ioctl/xxxx_compat_ioctl32
自定义 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
",
      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
", cmd);
    return -EINVAL;
  }
  return ret;
}

原作者:悲伤的小强

回帖(1)

张明

2022-5-1 11:56:18
举报

更多回帖

发帖
×
20
完善资料,
赚取积分