13d0407baSopenharmony_ci// SPDX-License-Identifier: GPL-2.0
23d0407baSopenharmony_ci/*
33d0407baSopenharmony_ci * dw9763 vcm driver
43d0407baSopenharmony_ci *
53d0407baSopenharmony_ci * Copyright (C) 2019 Fuzhou Rockchip Electronics Co., Ltd.
63d0407baSopenharmony_ci */
73d0407baSopenharmony_ci
83d0407baSopenharmony_ci#include <linux/delay.h>
93d0407baSopenharmony_ci#include <linux/i2c.h>
103d0407baSopenharmony_ci#include <linux/module.h>
113d0407baSopenharmony_ci#include <linux/pm_runtime.h>
123d0407baSopenharmony_ci#include <linux/rk-camera-module.h>
133d0407baSopenharmony_ci#include <linux/version.h>
143d0407baSopenharmony_ci#include <media/v4l2-ctrls.h>
153d0407baSopenharmony_ci#include <media/v4l2-device.h>
163d0407baSopenharmony_ci#include <linux/rk_vcm_head.h>
173d0407baSopenharmony_ci#include <linux/compat.h>
183d0407baSopenharmony_ci
193d0407baSopenharmony_ci#define DRIVER_VERSION			KERNEL_VERSION(0, 0x01, 0x0)
203d0407baSopenharmony_ci#define DW9763_NAME			"dw9763"
213d0407baSopenharmony_ci
223d0407baSopenharmony_ci#define DW9763_RING_PD_CONTROL_REG		0x02
233d0407baSopenharmony_ci#define DW9763_DATAM_REG 0X03
243d0407baSopenharmony_ci#define DW9763_DATAL_REG 0X04
253d0407baSopenharmony_ci
263d0407baSopenharmony_ci#define DW9763_STATUS_ADDR			0x05
273d0407baSopenharmony_ci#define DW9763_SAC_PRESC_REG			0x06
283d0407baSopenharmony_ci#define DW9763_SAC_TIME_REG	0x07
293d0407baSopenharmony_ci#define DW9763_FLAG_REG 0X10
303d0407baSopenharmony_ci
313d0407baSopenharmony_ci#define DW9763_MAX_CURRENT		100U
323d0407baSopenharmony_ci#define DW9763_MAX_REG			1023U
333d0407baSopenharmony_ci
343d0407baSopenharmony_ci#define DW9763_DEFAULT_START_CURRENT	0
353d0407baSopenharmony_ci#define DW9763_DEFAULT_RATED_CURRENT	100
363d0407baSopenharmony_ci#define DW9763_DEFAULT_STEP_MODE	0xd
373d0407baSopenharmony_ci#define REG_NULL			0xFF
383d0407baSopenharmony_ci
393d0407baSopenharmony_ci#define OF_CAMERA_VCMDRV_CONTROL_MODE     "rockchip,vcm-control-mode"
403d0407baSopenharmony_ci#define OF_CAMERA_VCMDRV_SACDIV_MODE      "rockchip,vcm-sacdiv-mode"
413d0407baSopenharmony_ci#define VCMDRV_DEFAULT_CONTROL_MODE       4
423d0407baSopenharmony_ci#define VCMDRV_DEFAULT_SACDIV_MODE        1
433d0407baSopenharmony_ci
443d0407baSopenharmony_ci/* dw9763 device structure */
453d0407baSopenharmony_cistruct dw9763_device {
463d0407baSopenharmony_ci	struct v4l2_ctrl_handler ctrls_vcm;
473d0407baSopenharmony_ci	struct v4l2_subdev sd;
483d0407baSopenharmony_ci	struct v4l2_device vdev;
493d0407baSopenharmony_ci	u16 current_val;
503d0407baSopenharmony_ci
513d0407baSopenharmony_ci	unsigned short current_related_pos;
523d0407baSopenharmony_ci	unsigned short current_lens_pos;
533d0407baSopenharmony_ci	unsigned int start_current;
543d0407baSopenharmony_ci	unsigned int rated_current;
553d0407baSopenharmony_ci	unsigned int step;
563d0407baSopenharmony_ci	unsigned int step_mode;
573d0407baSopenharmony_ci	unsigned int control_mode;
583d0407baSopenharmony_ci	unsigned int sacdiv_mode;
593d0407baSopenharmony_ci	unsigned long mv_time_per_pos;
603d0407baSopenharmony_ci
613d0407baSopenharmony_ci	struct __kernel_old_timeval start_move_tv;
623d0407baSopenharmony_ci	struct __kernel_old_timeval end_move_tv;
633d0407baSopenharmony_ci	unsigned long move_us;
643d0407baSopenharmony_ci
653d0407baSopenharmony_ci	u32 module_index;
663d0407baSopenharmony_ci	const char *module_facing;
673d0407baSopenharmony_ci};
683d0407baSopenharmony_ci
693d0407baSopenharmony_cistatic inline struct dw9763_device *to_dw9763_vcm(struct v4l2_ctrl *ctrl)
703d0407baSopenharmony_ci{
713d0407baSopenharmony_ci	return container_of(ctrl->handler, struct dw9763_device, ctrls_vcm);
723d0407baSopenharmony_ci}
733d0407baSopenharmony_ci
743d0407baSopenharmony_cistatic inline struct dw9763_device *sd_to_dw9763_vcm(struct v4l2_subdev *subdev)
753d0407baSopenharmony_ci{
763d0407baSopenharmony_ci	return container_of(subdev, struct dw9763_device, sd);
773d0407baSopenharmony_ci}
783d0407baSopenharmony_ci
793d0407baSopenharmony_cistatic int dw9763_read_reg(struct i2c_client *client,
803d0407baSopenharmony_ci	u8 addr, u32 *val, u8 len)
813d0407baSopenharmony_ci{
823d0407baSopenharmony_ci	struct i2c_msg msgs[2];
833d0407baSopenharmony_ci	u8 *data_be_p;
843d0407baSopenharmony_ci	__be32 data_be = 0;
853d0407baSopenharmony_ci	int ret;
863d0407baSopenharmony_ci
873d0407baSopenharmony_ci	if (len > 4 || !len)
883d0407baSopenharmony_ci		return -EINVAL;
893d0407baSopenharmony_ci
903d0407baSopenharmony_ci	data_be_p = (u8 *)&data_be;
913d0407baSopenharmony_ci	/* Write register address */
923d0407baSopenharmony_ci	msgs[0].addr = client->addr;
933d0407baSopenharmony_ci	msgs[0].flags = 0;
943d0407baSopenharmony_ci	msgs[0].len = 1;
953d0407baSopenharmony_ci	msgs[0].buf = (u8 *)&addr;
963d0407baSopenharmony_ci
973d0407baSopenharmony_ci	/* Read data from register */
983d0407baSopenharmony_ci	msgs[1].addr = client->addr;
993d0407baSopenharmony_ci	msgs[1].flags = I2C_M_RD;
1003d0407baSopenharmony_ci	msgs[1].len = len;
1013d0407baSopenharmony_ci	msgs[1].buf = &data_be_p[4 - len];
1023d0407baSopenharmony_ci
1033d0407baSopenharmony_ci	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
1043d0407baSopenharmony_ci	if (ret != ARRAY_SIZE(msgs))
1053d0407baSopenharmony_ci		return -EIO;
1063d0407baSopenharmony_ci
1073d0407baSopenharmony_ci	*val = be32_to_cpu(data_be);
1083d0407baSopenharmony_ci
1093d0407baSopenharmony_ci	return 0;
1103d0407baSopenharmony_ci}
1113d0407baSopenharmony_ci
1123d0407baSopenharmony_cistatic int dw9763_write_reg(struct i2c_client *client,
1133d0407baSopenharmony_ci	u8 addr, u32 val, u8 len)
1143d0407baSopenharmony_ci{
1153d0407baSopenharmony_ci	u32 buf_i, val_i;
1163d0407baSopenharmony_ci	u8 buf[5];
1173d0407baSopenharmony_ci	u8 *val_p;
1183d0407baSopenharmony_ci	__be32 val_be;
1193d0407baSopenharmony_ci
1203d0407baSopenharmony_ci	if (len > 4)
1213d0407baSopenharmony_ci		return -EINVAL;
1223d0407baSopenharmony_ci
1233d0407baSopenharmony_ci	buf[0] = addr & 0xff;
1243d0407baSopenharmony_ci
1253d0407baSopenharmony_ci	val_be = cpu_to_be32(val);
1263d0407baSopenharmony_ci	val_p = (u8 *)&val_be;
1273d0407baSopenharmony_ci	buf_i = 1;
1283d0407baSopenharmony_ci	val_i = 4 - len;
1293d0407baSopenharmony_ci
1303d0407baSopenharmony_ci	while (val_i < 4)
1313d0407baSopenharmony_ci		buf[buf_i++] = val_p[val_i++];
1323d0407baSopenharmony_ci
1333d0407baSopenharmony_ci	if (i2c_master_send(client, buf, len + 1) != len + 1)
1343d0407baSopenharmony_ci		return -EIO;
1353d0407baSopenharmony_ci
1363d0407baSopenharmony_ci	return 0;
1373d0407baSopenharmony_ci}
1383d0407baSopenharmony_ci
1393d0407baSopenharmony_cistatic int dw9763_get_pos(struct dw9763_device *dev_vcm,
1403d0407baSopenharmony_ci	unsigned int *cur_pos)
1413d0407baSopenharmony_ci{
1423d0407baSopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(&dev_vcm->sd);
1433d0407baSopenharmony_ci	int ret;
1443d0407baSopenharmony_ci	u32 val;
1453d0407baSopenharmony_ci	unsigned int abs_step;
1463d0407baSopenharmony_ci
1473d0407baSopenharmony_ci	ret = dw9763_read_reg(client, DW9763_DATAM_REG, &val, 2);
1483d0407baSopenharmony_ci	if (ret != 0)
1493d0407baSopenharmony_ci		goto err;
1503d0407baSopenharmony_ci
1513d0407baSopenharmony_ci	abs_step = val & 0x3ff;
1523d0407baSopenharmony_ci	if (abs_step <= dev_vcm->start_current)
1533d0407baSopenharmony_ci		abs_step = VCMDRV_MAX_LOG;
1543d0407baSopenharmony_ci	else if ((abs_step > dev_vcm->start_current) &&
1553d0407baSopenharmony_ci		 (abs_step <= dev_vcm->rated_current))
1563d0407baSopenharmony_ci		abs_step = (dev_vcm->rated_current - abs_step) / dev_vcm->step;
1573d0407baSopenharmony_ci	else
1583d0407baSopenharmony_ci		abs_step = 0;
1593d0407baSopenharmony_ci
1603d0407baSopenharmony_ci	*cur_pos = abs_step;
1613d0407baSopenharmony_ci	dev_dbg(&client->dev, "%s: get position %d\n", __func__, *cur_pos);
1623d0407baSopenharmony_ci	return 0;
1633d0407baSopenharmony_ci
1643d0407baSopenharmony_cierr:
1653d0407baSopenharmony_ci	dev_err(&client->dev,
1663d0407baSopenharmony_ci		"%s: failed with error %d\n", __func__, ret);
1673d0407baSopenharmony_ci	return ret;
1683d0407baSopenharmony_ci}
1693d0407baSopenharmony_ci
1703d0407baSopenharmony_cistatic int dw9763_set_pos(struct dw9763_device *dev_vcm,
1713d0407baSopenharmony_ci	unsigned int dest_pos)
1723d0407baSopenharmony_ci{
1733d0407baSopenharmony_ci	int ret;
1743d0407baSopenharmony_ci	unsigned int position = 0;
1753d0407baSopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(&dev_vcm->sd);
1763d0407baSopenharmony_ci
1773d0407baSopenharmony_ci	if (dest_pos >= VCMDRV_MAX_LOG)
1783d0407baSopenharmony_ci		position = dev_vcm->start_current;
1793d0407baSopenharmony_ci	else
1803d0407baSopenharmony_ci		position = dev_vcm->start_current +
1813d0407baSopenharmony_ci			   (dev_vcm->step * (VCMDRV_MAX_LOG - dest_pos));
1823d0407baSopenharmony_ci
1833d0407baSopenharmony_ci	if (position > DW9763_MAX_REG)
1843d0407baSopenharmony_ci		position = DW9763_MAX_REG;
1853d0407baSopenharmony_ci
1863d0407baSopenharmony_ci	dev_vcm->current_lens_pos = position;
1873d0407baSopenharmony_ci	dev_vcm->current_related_pos = dest_pos;
1883d0407baSopenharmony_ci	ret = dw9763_write_reg(client, DW9763_DATAM_REG, position & 0x3ff, 2);
1893d0407baSopenharmony_ci	if (ret != 0)
1903d0407baSopenharmony_ci		goto err;
1913d0407baSopenharmony_ci
1923d0407baSopenharmony_ci	dev_dbg(&client->dev, "@@@@@@@@ %s: get position %d\n", __func__, position);
1933d0407baSopenharmony_ci	return ret;
1943d0407baSopenharmony_cierr:
1953d0407baSopenharmony_ci	dev_err(&client->dev,
1963d0407baSopenharmony_ci		"%s: failed with error %d\n", __func__, ret);
1973d0407baSopenharmony_ci	return ret;
1983d0407baSopenharmony_ci}
1993d0407baSopenharmony_ci
2003d0407baSopenharmony_cistatic int dw9763_get_ctrl(struct v4l2_ctrl *ctrl)
2013d0407baSopenharmony_ci{
2023d0407baSopenharmony_ci	struct dw9763_device *dev_vcm = to_dw9763_vcm(ctrl);
2033d0407baSopenharmony_ci
2043d0407baSopenharmony_ci	if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE)
2053d0407baSopenharmony_ci		return dw9763_get_pos(dev_vcm, &ctrl->val);
2063d0407baSopenharmony_ci
2073d0407baSopenharmony_ci	return -EINVAL;
2083d0407baSopenharmony_ci}
2093d0407baSopenharmony_ci
2103d0407baSopenharmony_cistatic int dw9763_set_ctrl(struct v4l2_ctrl *ctrl)
2113d0407baSopenharmony_ci{
2123d0407baSopenharmony_ci	struct dw9763_device *dev_vcm = to_dw9763_vcm(ctrl);
2133d0407baSopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(&dev_vcm->sd);
2143d0407baSopenharmony_ci	unsigned int dest_pos = ctrl->val;
2153d0407baSopenharmony_ci	int move_pos;
2163d0407baSopenharmony_ci	long mv_us;
2173d0407baSopenharmony_ci	int ret = 0;
2183d0407baSopenharmony_ci
2193d0407baSopenharmony_ci	if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) {
2203d0407baSopenharmony_ci		if (dest_pos > VCMDRV_MAX_LOG) {
2213d0407baSopenharmony_ci			dev_info(&client->dev,
2223d0407baSopenharmony_ci				"%s dest_pos is error. %d > %d\n",
2233d0407baSopenharmony_ci				__func__, dest_pos, VCMDRV_MAX_LOG);
2243d0407baSopenharmony_ci			return -EINVAL;
2253d0407baSopenharmony_ci		}
2263d0407baSopenharmony_ci		/* calculate move time */
2273d0407baSopenharmony_ci		move_pos = dev_vcm->current_related_pos - dest_pos;
2283d0407baSopenharmony_ci		if (move_pos < 0)
2293d0407baSopenharmony_ci			move_pos = -move_pos;
2303d0407baSopenharmony_ci
2313d0407baSopenharmony_ci		ret = dw9763_set_pos(dev_vcm, dest_pos);
2323d0407baSopenharmony_ci
2333d0407baSopenharmony_ci		if (dev_vcm->control_mode == 1)
2343d0407baSopenharmony_ci			dev_vcm->move_us = dev_vcm->mv_time_per_pos * move_pos;
2353d0407baSopenharmony_ci		else
2363d0407baSopenharmony_ci			dev_vcm->move_us = dev_vcm->mv_time_per_pos;
2373d0407baSopenharmony_ci		dev_dbg(&client->dev,
2383d0407baSopenharmony_ci			"dest_pos %d, move_us %ld\n",
2393d0407baSopenharmony_ci			dest_pos, dev_vcm->move_us);
2403d0407baSopenharmony_ci
2413d0407baSopenharmony_ci		dev_vcm->start_move_tv = ns_to_kernel_old_timeval(ktime_get_ns());
2423d0407baSopenharmony_ci		mv_us = dev_vcm->start_move_tv.tv_usec + dev_vcm->move_us;
2433d0407baSopenharmony_ci		if (mv_us >= 1000000) {
2443d0407baSopenharmony_ci			dev_vcm->end_move_tv.tv_sec =
2453d0407baSopenharmony_ci				dev_vcm->start_move_tv.tv_sec + 1;
2463d0407baSopenharmony_ci			dev_vcm->end_move_tv.tv_usec = mv_us - 1000000;
2473d0407baSopenharmony_ci		} else {
2483d0407baSopenharmony_ci			dev_vcm->end_move_tv.tv_sec =
2493d0407baSopenharmony_ci					dev_vcm->start_move_tv.tv_sec;
2503d0407baSopenharmony_ci			dev_vcm->end_move_tv.tv_usec = mv_us;
2513d0407baSopenharmony_ci		}
2523d0407baSopenharmony_ci	}
2533d0407baSopenharmony_ci
2543d0407baSopenharmony_ci	return ret;
2553d0407baSopenharmony_ci}
2563d0407baSopenharmony_ci
2573d0407baSopenharmony_cistatic const struct v4l2_ctrl_ops dw9763_vcm_ctrl_ops = {
2583d0407baSopenharmony_ci	.g_volatile_ctrl = dw9763_get_ctrl,
2593d0407baSopenharmony_ci	.s_ctrl = dw9763_set_ctrl,
2603d0407baSopenharmony_ci};
2613d0407baSopenharmony_ci
2623d0407baSopenharmony_cistatic int dw9763t_init(struct dw9763_device *dev)
2633d0407baSopenharmony_ci{
2643d0407baSopenharmony_ci	int ret = 0;
2653d0407baSopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(&dev->sd);
2663d0407baSopenharmony_ci	u32 control_mode = 0;
2673d0407baSopenharmony_ci	u32 step_mode = 0;
2683d0407baSopenharmony_ci
2693d0407baSopenharmony_ci	ret = dw9763_write_reg(client, DW9763_RING_PD_CONTROL_REG, 0x01, 1);
2703d0407baSopenharmony_ci	if (ret < 0)
2713d0407baSopenharmony_ci		goto err;
2723d0407baSopenharmony_ci
2733d0407baSopenharmony_ci	ret = dw9763_write_reg(client, DW9763_RING_PD_CONTROL_REG, 0x00, 1);
2743d0407baSopenharmony_ci	if (ret < 0)
2753d0407baSopenharmony_ci		goto err;
2763d0407baSopenharmony_ci
2773d0407baSopenharmony_ci	/*There is need a sleep after power on for write i2c*/
2783d0407baSopenharmony_ci	usleep_range(10000, 11000);
2793d0407baSopenharmony_ci
2803d0407baSopenharmony_ci	ret = dw9763_write_reg(client, DW9763_RING_PD_CONTROL_REG, 0x02, 1);
2813d0407baSopenharmony_ci	if (ret < 0)
2823d0407baSopenharmony_ci		goto err;
2833d0407baSopenharmony_ci
2843d0407baSopenharmony_ci
2853d0407baSopenharmony_ci	ret = dw9763_write_reg(client, DW9763_SAC_PRESC_REG, 0x61, 2);
2863d0407baSopenharmony_ci	if (ret < 0)
2873d0407baSopenharmony_ci		goto err;
2883d0407baSopenharmony_ci
2893d0407baSopenharmony_ci	ret = dw9763_write_reg(client, DW9763_SAC_TIME_REG, 0x39, 2);
2903d0407baSopenharmony_ci	if (ret < 0)
2913d0407baSopenharmony_ci		goto err;
2923d0407baSopenharmony_ci
2933d0407baSopenharmony_ci	dev_info(&client->dev, "enter vcm driver init\n");
2943d0407baSopenharmony_ci	/*There is need a sleep after out of standby status*/
2953d0407baSopenharmony_ci	msleep(100);
2963d0407baSopenharmony_ci
2973d0407baSopenharmony_ci	/*step_mode = (dev->sacdiv_mode << 6) | (dev->step_mode & 0x3f);
2983d0407baSopenharmony_ci	ret = dw9763_write_reg(client, DW9763_SACT_REG, step_mode, 1);
2993d0407baSopenharmony_ci	if (ret < 0)
3003d0407baSopenharmony_ci		goto err;
3013d0407baSopenharmony_ci	control_mode = 0x31;
3023d0407baSopenharmony_ci	control_mode |= (dev->control_mode & 0x07) << 1;
3033d0407baSopenharmony_ci	ret = dw9763_write_reg(client, DW9763_CONTROL, control_mode, 1);
3043d0407baSopenharmony_ci	if (ret < 0)
3053d0407baSopenharmony_ci		goto err;
3063d0407baSopenharmony_ci*/
3073d0407baSopenharmony_ci	dev_info(&client->dev, "dw9763t_init OK!!!\n");
3083d0407baSopenharmony_ci	return 0;
3093d0407baSopenharmony_cierr:
3103d0407baSopenharmony_ci	dev_err(&client->dev, "failed with error %d\n", ret);
3113d0407baSopenharmony_ci	return ret;
3123d0407baSopenharmony_ci}
3133d0407baSopenharmony_ci
3143d0407baSopenharmony_cistatic int dw9763_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
3153d0407baSopenharmony_ci{
3163d0407baSopenharmony_ci	int rval;
3173d0407baSopenharmony_ci	struct dw9763_device *dev_vcm = sd_to_dw9763_vcm(sd);
3183d0407baSopenharmony_ci
3193d0407baSopenharmony_ci	rval = pm_runtime_get_sync(sd->dev);
3203d0407baSopenharmony_ci	if (rval < 0) {
3213d0407baSopenharmony_ci		pm_runtime_put_noidle(sd->dev);
3223d0407baSopenharmony_ci		return rval;
3233d0407baSopenharmony_ci	}
3243d0407baSopenharmony_ci
3253d0407baSopenharmony_ci	rval = dw9763t_init(dev_vcm);
3263d0407baSopenharmony_ci	if (rval < 0) {
3273d0407baSopenharmony_ci		pm_runtime_put_noidle(sd->dev);
3283d0407baSopenharmony_ci		return rval;
3293d0407baSopenharmony_ci	}
3303d0407baSopenharmony_ci	return 0;
3313d0407baSopenharmony_ci}
3323d0407baSopenharmony_ci
3333d0407baSopenharmony_cistatic int dw9763_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
3343d0407baSopenharmony_ci{
3353d0407baSopenharmony_ci	pm_runtime_put(sd->dev);
3363d0407baSopenharmony_ci
3373d0407baSopenharmony_ci	return 0;
3383d0407baSopenharmony_ci}
3393d0407baSopenharmony_ci
3403d0407baSopenharmony_cistatic const struct v4l2_subdev_internal_ops dw9763_int_ops = {
3413d0407baSopenharmony_ci	.open = dw9763_open,
3423d0407baSopenharmony_ci	.close = dw9763_close,
3433d0407baSopenharmony_ci};
3443d0407baSopenharmony_ci
3453d0407baSopenharmony_cistatic long dw9763_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
3463d0407baSopenharmony_ci{
3473d0407baSopenharmony_ci	int ret = 0;
3483d0407baSopenharmony_ci	struct rk_cam_vcm_tim *vcm_tim;
3493d0407baSopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
3503d0407baSopenharmony_ci	struct dw9763_device *dw9763_dev = sd_to_dw9763_vcm(sd);
3513d0407baSopenharmony_ci
3523d0407baSopenharmony_ci//	if (cmd == RK_VIDIOC_VCM_TIMEINFO) {
3533d0407baSopenharmony_ci		vcm_tim = (struct rk_cam_vcm_tim *)arg;
3543d0407baSopenharmony_ci
3553d0407baSopenharmony_ci		vcm_tim->vcm_start_t.tv_sec = dw9763_dev->start_move_tv.tv_sec;
3563d0407baSopenharmony_ci		vcm_tim->vcm_start_t.tv_usec =
3573d0407baSopenharmony_ci				dw9763_dev->start_move_tv.tv_usec;
3583d0407baSopenharmony_ci		vcm_tim->vcm_end_t.tv_sec = dw9763_dev->end_move_tv.tv_sec;
3593d0407baSopenharmony_ci		vcm_tim->vcm_end_t.tv_usec = dw9763_dev->end_move_tv.tv_usec;
3603d0407baSopenharmony_ci
3613d0407baSopenharmony_ci		dev_dbg(&client->dev, "dw9763_get_move_res 0x%lx, 0x%lx, 0x%lx, 0x%lx\n",
3623d0407baSopenharmony_ci			vcm_tim->vcm_start_t.tv_sec,
3633d0407baSopenharmony_ci			vcm_tim->vcm_start_t.tv_usec,
3643d0407baSopenharmony_ci			vcm_tim->vcm_end_t.tv_sec,
3653d0407baSopenharmony_ci			vcm_tim->vcm_end_t.tv_usec);
3663d0407baSopenharmony_ci	//} else {
3673d0407baSopenharmony_ci	//	dev_err(&client->dev,
3683d0407baSopenharmony_ci	//		"cmd 0x%x not supported\n", cmd);
3693d0407baSopenharmony_ci	//	return -EINVAL;
3703d0407baSopenharmony_ci	//}
3713d0407baSopenharmony_ci
3723d0407baSopenharmony_ci	return ret;
3733d0407baSopenharmony_ci}
3743d0407baSopenharmony_ci
3753d0407baSopenharmony_ci#ifdef CONFIG_COMPAT
3763d0407baSopenharmony_cistatic long dw9763_compat_ioctl32(struct v4l2_subdev *sd,
3773d0407baSopenharmony_ci	unsigned int cmd, unsigned long arg)
3783d0407baSopenharmony_ci{
3793d0407baSopenharmony_ci	struct rk_cam_vcm_tim vcm_tim;
3803d0407baSopenharmony_ci	struct rk_cam_compat_vcm_tim compat_vcm_tim;
3813d0407baSopenharmony_ci	void __user *up = compat_ptr(arg);
3823d0407baSopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
3833d0407baSopenharmony_ci	long ret;
3843d0407baSopenharmony_ci
3853d0407baSopenharmony_ci	if (cmd == RK_VIDIOC_COMPAT_VCM_TIMEINFO) {
3863d0407baSopenharmony_ci		struct rk_cam_compat_vcm_tim __user *p32 = up;
3873d0407baSopenharmony_ci
3883d0407baSopenharmony_ci		ret = dw9763_ioctl(sd, RK_VIDIOC_VCM_TIMEINFO, &vcm_tim);
3893d0407baSopenharmony_ci		compat_vcm_tim.vcm_start_t.tv_sec = vcm_tim.vcm_start_t.tv_sec;
3903d0407baSopenharmony_ci		compat_vcm_tim.vcm_start_t.tv_usec =
3913d0407baSopenharmony_ci				vcm_tim.vcm_start_t.tv_usec;
3923d0407baSopenharmony_ci		compat_vcm_tim.vcm_end_t.tv_sec = vcm_tim.vcm_end_t.tv_sec;
3933d0407baSopenharmony_ci		compat_vcm_tim.vcm_end_t.tv_usec = vcm_tim.vcm_end_t.tv_usec;
3943d0407baSopenharmony_ci
3953d0407baSopenharmony_ci		put_user(compat_vcm_tim.vcm_start_t.tv_sec,
3963d0407baSopenharmony_ci			&p32->vcm_start_t.tv_sec);
3973d0407baSopenharmony_ci		put_user(compat_vcm_tim.vcm_start_t.tv_usec,
3983d0407baSopenharmony_ci			&p32->vcm_start_t.tv_usec);
3993d0407baSopenharmony_ci		put_user(compat_vcm_tim.vcm_end_t.tv_sec,
4003d0407baSopenharmony_ci			&p32->vcm_end_t.tv_sec);
4013d0407baSopenharmony_ci		put_user(compat_vcm_tim.vcm_end_t.tv_usec,
4023d0407baSopenharmony_ci			&p32->vcm_end_t.tv_usec);
4033d0407baSopenharmony_ci	} else {
4043d0407baSopenharmony_ci		dev_err(&client->dev,
4053d0407baSopenharmony_ci			"cmd 0x%x not supported\n", cmd);
4063d0407baSopenharmony_ci		return -EINVAL;
4073d0407baSopenharmony_ci	}
4083d0407baSopenharmony_ci
4093d0407baSopenharmony_ci	return ret;
4103d0407baSopenharmony_ci}
4113d0407baSopenharmony_ci#endif
4123d0407baSopenharmony_ci
4133d0407baSopenharmony_cistatic const struct v4l2_subdev_core_ops dw9763_core_ops = {
4143d0407baSopenharmony_ci	.ioctl = dw9763_ioctl,
4153d0407baSopenharmony_ci#ifdef CONFIG_COMPAT
4163d0407baSopenharmony_ci	.compat_ioctl32 = dw9763_compat_ioctl32
4173d0407baSopenharmony_ci#endif
4183d0407baSopenharmony_ci};
4193d0407baSopenharmony_ci
4203d0407baSopenharmony_cistatic const struct v4l2_subdev_ops dw9763_ops = {
4213d0407baSopenharmony_ci	.core = &dw9763_core_ops,
4223d0407baSopenharmony_ci};
4233d0407baSopenharmony_ci
4243d0407baSopenharmony_cistatic void dw9763_subdev_cleanup(struct dw9763_device *dw9763_dev)
4253d0407baSopenharmony_ci{
4263d0407baSopenharmony_ci	v4l2_device_unregister_subdev(&dw9763_dev->sd);
4273d0407baSopenharmony_ci	v4l2_device_unregister(&dw9763_dev->vdev);
4283d0407baSopenharmony_ci	v4l2_ctrl_handler_free(&dw9763_dev->ctrls_vcm);
4293d0407baSopenharmony_ci	media_entity_cleanup(&dw9763_dev->sd.entity);
4303d0407baSopenharmony_ci}
4313d0407baSopenharmony_ci
4323d0407baSopenharmony_cistatic int dw9763_init_controls(struct dw9763_device *dev_vcm)
4333d0407baSopenharmony_ci{
4343d0407baSopenharmony_ci	struct v4l2_ctrl_handler *hdl = &dev_vcm->ctrls_vcm;
4353d0407baSopenharmony_ci	const struct v4l2_ctrl_ops *ops = &dw9763_vcm_ctrl_ops;
4363d0407baSopenharmony_ci
4373d0407baSopenharmony_ci	v4l2_ctrl_handler_init(hdl, 1);
4383d0407baSopenharmony_ci
4393d0407baSopenharmony_ci	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE,
4403d0407baSopenharmony_ci			  0, VCMDRV_MAX_LOG, 1, VCMDRV_MAX_LOG);
4413d0407baSopenharmony_ci
4423d0407baSopenharmony_ci	if (hdl->error)
4433d0407baSopenharmony_ci		dev_err(dev_vcm->sd.dev, "%s fail error: 0x%x\n",
4443d0407baSopenharmony_ci			__func__, hdl->error);
4453d0407baSopenharmony_ci	dev_vcm->sd.ctrl_handler = hdl;
4463d0407baSopenharmony_ci	return hdl->error;
4473d0407baSopenharmony_ci}
4483d0407baSopenharmony_ci
4493d0407baSopenharmony_cistatic int dw9763_probe(struct i2c_client *client,
4503d0407baSopenharmony_ci			const struct i2c_device_id *id)
4513d0407baSopenharmony_ci{
4523d0407baSopenharmony_ci	struct device_node *np = of_node_get(client->dev.of_node);
4533d0407baSopenharmony_ci	struct dw9763_device *dw9763_dev;
4543d0407baSopenharmony_ci	int ret;
4553d0407baSopenharmony_ci	int current_distance;
4563d0407baSopenharmony_ci	unsigned int start_current;
4573d0407baSopenharmony_ci	unsigned int rated_current;
4583d0407baSopenharmony_ci	unsigned int step_mode;
4593d0407baSopenharmony_ci	unsigned int control_mode;
4603d0407baSopenharmony_ci	unsigned int sacdiv_mode;
4613d0407baSopenharmony_ci	struct v4l2_subdev *sd;
4623d0407baSopenharmony_ci	char facing[2];
4633d0407baSopenharmony_ci
4643d0407baSopenharmony_ci	printk("XXXXXXXXXXXXXXX dw9763_probe\n");
4653d0407baSopenharmony_ci
4663d0407baSopenharmony_ci	dev_info(&client->dev, "probing...\n");
4673d0407baSopenharmony_ci	if (of_property_read_u32(np,
4683d0407baSopenharmony_ci		OF_CAMERA_VCMDRV_START_CURRENT,
4693d0407baSopenharmony_ci		(unsigned int *)&start_current)) {
4703d0407baSopenharmony_ci		start_current = DW9763_DEFAULT_START_CURRENT;
4713d0407baSopenharmony_ci		dev_info(&client->dev,
4723d0407baSopenharmony_ci			"could not get module %s from dts!\n",
4733d0407baSopenharmony_ci			OF_CAMERA_VCMDRV_START_CURRENT);
4743d0407baSopenharmony_ci	}
4753d0407baSopenharmony_ci	if (of_property_read_u32(np,
4763d0407baSopenharmony_ci		OF_CAMERA_VCMDRV_RATED_CURRENT,
4773d0407baSopenharmony_ci		(unsigned int *)&rated_current)) {
4783d0407baSopenharmony_ci		rated_current = DW9763_DEFAULT_RATED_CURRENT;
4793d0407baSopenharmony_ci		dev_info(&client->dev,
4803d0407baSopenharmony_ci			"could not get module %s from dts!\n",
4813d0407baSopenharmony_ci			OF_CAMERA_VCMDRV_RATED_CURRENT);
4823d0407baSopenharmony_ci	}
4833d0407baSopenharmony_ci	if (of_property_read_u32(np,
4843d0407baSopenharmony_ci		OF_CAMERA_VCMDRV_STEP_MODE,
4853d0407baSopenharmony_ci		(unsigned int *)&step_mode)) {
4863d0407baSopenharmony_ci		step_mode = DW9763_DEFAULT_STEP_MODE;
4873d0407baSopenharmony_ci		dev_info(&client->dev,
4883d0407baSopenharmony_ci			"could not get module %s from dts!\n",
4893d0407baSopenharmony_ci			OF_CAMERA_VCMDRV_STEP_MODE);
4903d0407baSopenharmony_ci	}
4913d0407baSopenharmony_ci	if (of_property_read_u32(np,
4923d0407baSopenharmony_ci		OF_CAMERA_VCMDRV_CONTROL_MODE,
4933d0407baSopenharmony_ci		(unsigned int *)&control_mode)) {
4943d0407baSopenharmony_ci		control_mode = VCMDRV_DEFAULT_CONTROL_MODE;
4953d0407baSopenharmony_ci		dev_info(&client->dev,
4963d0407baSopenharmony_ci			"could not get module %s from dts!\n",
4973d0407baSopenharmony_ci			OF_CAMERA_VCMDRV_CONTROL_MODE);
4983d0407baSopenharmony_ci	}
4993d0407baSopenharmony_ci	if (of_property_read_u32(np,
5003d0407baSopenharmony_ci		OF_CAMERA_VCMDRV_SACDIV_MODE,
5013d0407baSopenharmony_ci		(unsigned int *)&sacdiv_mode)) {
5023d0407baSopenharmony_ci		sacdiv_mode = VCMDRV_DEFAULT_SACDIV_MODE;
5033d0407baSopenharmony_ci		dev_info(&client->dev,
5043d0407baSopenharmony_ci			"could not get module %s from dts!\n",
5053d0407baSopenharmony_ci			OF_CAMERA_VCMDRV_SACDIV_MODE);
5063d0407baSopenharmony_ci	}
5073d0407baSopenharmony_ci
5083d0407baSopenharmony_ci	dw9763_dev = devm_kzalloc(&client->dev, sizeof(*dw9763_dev),
5093d0407baSopenharmony_ci				  GFP_KERNEL);
5103d0407baSopenharmony_ci	if (dw9763_dev == NULL)
5113d0407baSopenharmony_ci		return -ENOMEM;
5123d0407baSopenharmony_ci
5133d0407baSopenharmony_ci	ret = of_property_read_u32(np, RKMODULE_CAMERA_MODULE_INDEX,
5143d0407baSopenharmony_ci				   &dw9763_dev->module_index);
5153d0407baSopenharmony_ci	ret |= of_property_read_string(np, RKMODULE_CAMERA_MODULE_FACING,
5163d0407baSopenharmony_ci				       &dw9763_dev->module_facing);
5173d0407baSopenharmony_ci	if (ret) {
5183d0407baSopenharmony_ci		dev_err(&client->dev,
5193d0407baSopenharmony_ci			"could not get module information!\n");
5203d0407baSopenharmony_ci		return -EINVAL;
5213d0407baSopenharmony_ci	}
5223d0407baSopenharmony_ci
5233d0407baSopenharmony_ci	v4l2_i2c_subdev_init(&dw9763_dev->sd, client, &dw9763_ops);
5243d0407baSopenharmony_ci	dw9763_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
5253d0407baSopenharmony_ci	dw9763_dev->sd.internal_ops = &dw9763_int_ops;
5263d0407baSopenharmony_ci
5273d0407baSopenharmony_ci	ret = dw9763_init_controls(dw9763_dev);
5283d0407baSopenharmony_ci	if (ret)
5293d0407baSopenharmony_ci		goto err_cleanup;
5303d0407baSopenharmony_ci
5313d0407baSopenharmony_ci	ret = media_entity_pads_init(&dw9763_dev->sd.entity, 0, NULL);
5323d0407baSopenharmony_ci	if (ret < 0)
5333d0407baSopenharmony_ci		goto err_cleanup;
5343d0407baSopenharmony_ci
5353d0407baSopenharmony_ci	sd = &dw9763_dev->sd;
5363d0407baSopenharmony_ci	sd->entity.function = MEDIA_ENT_F_LENS;
5373d0407baSopenharmony_ci
5383d0407baSopenharmony_ci	memset(facing, 0, sizeof(facing));
5393d0407baSopenharmony_ci	if (strcmp(dw9763_dev->module_facing, "back") == 0)
5403d0407baSopenharmony_ci		facing[0] = 'b';
5413d0407baSopenharmony_ci	else
5423d0407baSopenharmony_ci		facing[0] = 'f';
5433d0407baSopenharmony_ci
5443d0407baSopenharmony_ci	snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
5453d0407baSopenharmony_ci		 dw9763_dev->module_index, facing,
5463d0407baSopenharmony_ci		 DW9763_NAME, dev_name(sd->dev));
5473d0407baSopenharmony_ci	ret = v4l2_async_register_subdev(sd);
5483d0407baSopenharmony_ci	if (ret)
5493d0407baSopenharmony_ci		dev_err(&client->dev, "v4l2 async register subdev failed\n");
5503d0407baSopenharmony_ci
5513d0407baSopenharmony_ci	current_distance = rated_current - start_current;
5523d0407baSopenharmony_ci	current_distance = current_distance * DW9763_MAX_REG /
5533d0407baSopenharmony_ci						DW9763_MAX_CURRENT;
5543d0407baSopenharmony_ci	dw9763_dev->step = (current_distance + (VCMDRV_MAX_LOG - 1)) /
5553d0407baSopenharmony_ci						VCMDRV_MAX_LOG;
5563d0407baSopenharmony_ci	dw9763_dev->start_current = start_current * DW9763_MAX_REG /
5573d0407baSopenharmony_ci						DW9763_MAX_CURRENT;
5583d0407baSopenharmony_ci	dw9763_dev->rated_current = dw9763_dev->start_current +
5593d0407baSopenharmony_ci						VCMDRV_MAX_LOG *
5603d0407baSopenharmony_ci						dw9763_dev->step;
5613d0407baSopenharmony_ci	dw9763_dev->step_mode     = step_mode;
5623d0407baSopenharmony_ci	dw9763_dev->move_us       = 0;
5633d0407baSopenharmony_ci	dw9763_dev->current_related_pos = VCMDRV_MAX_LOG;
5643d0407baSopenharmony_ci	dw9763_dev->start_move_tv = ns_to_kernel_old_timeval(ktime_get_ns());
5653d0407baSopenharmony_ci	dw9763_dev->end_move_tv = ns_to_kernel_old_timeval(ktime_get_ns());
5663d0407baSopenharmony_ci
5673d0407baSopenharmony_ci	switch (control_mode) {
5683d0407baSopenharmony_ci	case 0:
5693d0407baSopenharmony_ci		dev_err(&client->dev, "control_mode is derect mode, not support\n");
5703d0407baSopenharmony_ci		return -EINVAL;
5713d0407baSopenharmony_ci	case 1:
5723d0407baSopenharmony_ci		dw9763_dev->mv_time_per_pos = (126 + step_mode * 2) * dw9763_dev->step;
5733d0407baSopenharmony_ci		dev_dbg(&client->dev, "control_mode is LSC mode\n");
5743d0407baSopenharmony_ci		break;
5753d0407baSopenharmony_ci	case 2:
5763d0407baSopenharmony_ci	case 3:
5773d0407baSopenharmony_ci	case 4:
5783d0407baSopenharmony_ci	case 5:
5793d0407baSopenharmony_ci	case 6:
5803d0407baSopenharmony_ci	case 7:
5813d0407baSopenharmony_ci		dw9763_dev->mv_time_per_pos = (6300 + step_mode * 100);
5823d0407baSopenharmony_ci		dev_dbg(&client->dev, "control_mode is LSC mode\n");
5833d0407baSopenharmony_ci		break;
5843d0407baSopenharmony_ci	default:
5853d0407baSopenharmony_ci		dev_err(&client->dev, "set unknown control_mode\n");
5863d0407baSopenharmony_ci		return -EINVAL;
5873d0407baSopenharmony_ci	}
5883d0407baSopenharmony_ci
5893d0407baSopenharmony_ci	switch (sacdiv_mode) {
5903d0407baSopenharmony_ci	case 0:
5913d0407baSopenharmony_ci		dw9763_dev->mv_time_per_pos *= 2;
5923d0407baSopenharmony_ci		dev_dbg(&client->dev, "sacdiv_mode is %d\n", sacdiv_mode);
5933d0407baSopenharmony_ci		break;
5943d0407baSopenharmony_ci	case 1:
5953d0407baSopenharmony_ci		dev_dbg(&client->dev, "sacdiv_mode is %d\n", sacdiv_mode);
5963d0407baSopenharmony_ci		break;
5973d0407baSopenharmony_ci	case 2:
5983d0407baSopenharmony_ci		dw9763_dev->mv_time_per_pos /= 2;
5993d0407baSopenharmony_ci		dev_dbg(&client->dev, "sacdiv_mode is %d\n", sacdiv_mode);
6003d0407baSopenharmony_ci		break;
6013d0407baSopenharmony_ci	case 3:
6023d0407baSopenharmony_ci		dw9763_dev->mv_time_per_pos /= 4;
6033d0407baSopenharmony_ci		dev_dbg(&client->dev, "sacdiv_mode is %d\n", sacdiv_mode);
6043d0407baSopenharmony_ci		break;
6053d0407baSopenharmony_ci	default:
6063d0407baSopenharmony_ci		dev_err(&client->dev, "set unknown control_mode\n");
6073d0407baSopenharmony_ci		return -EINVAL;
6083d0407baSopenharmony_ci	}
6093d0407baSopenharmony_ci
6103d0407baSopenharmony_ci	pm_runtime_set_active(&client->dev);
6113d0407baSopenharmony_ci	pm_runtime_enable(&client->dev);
6123d0407baSopenharmony_ci	pm_runtime_idle(&client->dev);
6133d0407baSopenharmony_ci
6143d0407baSopenharmony_ci	dev_info(&client->dev, "probing successful\n");
6153d0407baSopenharmony_ci
6163d0407baSopenharmony_ci	printk("XXXXXXXXXXXXXXX dw9763_probe OK\n");
6173d0407baSopenharmony_ci
6183d0407baSopenharmony_ci	return 0;
6193d0407baSopenharmony_ci
6203d0407baSopenharmony_cierr_cleanup:
6213d0407baSopenharmony_ci	dw9763_subdev_cleanup(dw9763_dev);
6223d0407baSopenharmony_ci	dev_err(&client->dev, "Probe failed: %d\n", ret);
6233d0407baSopenharmony_ci	return ret;
6243d0407baSopenharmony_ci}
6253d0407baSopenharmony_ci
6263d0407baSopenharmony_cistatic int dw9763_remove(struct i2c_client *client)
6273d0407baSopenharmony_ci{
6283d0407baSopenharmony_ci	struct v4l2_subdev *sd = i2c_get_clientdata(client);
6293d0407baSopenharmony_ci	struct dw9763_device *dw9763_dev = sd_to_dw9763_vcm(sd);
6303d0407baSopenharmony_ci
6313d0407baSopenharmony_ci	pm_runtime_disable(&client->dev);
6323d0407baSopenharmony_ci	dw9763_subdev_cleanup(dw9763_dev);
6333d0407baSopenharmony_ci
6343d0407baSopenharmony_ci	return 0;
6353d0407baSopenharmony_ci}
6363d0407baSopenharmony_ci
6373d0407baSopenharmony_cistatic int __maybe_unused dw9763_vcm_suspend(struct device *dev)
6383d0407baSopenharmony_ci{
6393d0407baSopenharmony_ci	return 0;
6403d0407baSopenharmony_ci}
6413d0407baSopenharmony_ci
6423d0407baSopenharmony_cistatic int __maybe_unused dw9763_vcm_resume(struct device *dev)
6433d0407baSopenharmony_ci{
6443d0407baSopenharmony_ci	return 0;
6453d0407baSopenharmony_ci}
6463d0407baSopenharmony_ci
6473d0407baSopenharmony_cistatic const struct i2c_device_id dw9763_id_table[] = {
6483d0407baSopenharmony_ci	{ DW9763_NAME, 0 },
6493d0407baSopenharmony_ci	{ { 0 } }
6503d0407baSopenharmony_ci};
6513d0407baSopenharmony_ciMODULE_DEVICE_TABLE(i2c, dw9763_id_table);
6523d0407baSopenharmony_ci
6533d0407baSopenharmony_cistatic const struct of_device_id dw9763_of_table[] = {
6543d0407baSopenharmony_ci	{ .compatible = "dongwoon,dw9763" },
6553d0407baSopenharmony_ci	{ { 0 } }
6563d0407baSopenharmony_ci};
6573d0407baSopenharmony_ciMODULE_DEVICE_TABLE(of, dw9763_of_table);
6583d0407baSopenharmony_ci
6593d0407baSopenharmony_cistatic const struct dev_pm_ops dw9763_pm_ops = {
6603d0407baSopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(dw9763_vcm_suspend, dw9763_vcm_resume)
6613d0407baSopenharmony_ci	SET_RUNTIME_PM_OPS(dw9763_vcm_suspend, dw9763_vcm_resume, NULL)
6623d0407baSopenharmony_ci};
6633d0407baSopenharmony_ci
6643d0407baSopenharmony_cistatic struct i2c_driver dw9763_i2c_driver = {
6653d0407baSopenharmony_ci	.driver = {
6663d0407baSopenharmony_ci		.name = DW9763_NAME,
6673d0407baSopenharmony_ci		.pm = &dw9763_pm_ops,
6683d0407baSopenharmony_ci		.of_match_table = dw9763_of_table,
6693d0407baSopenharmony_ci	},
6703d0407baSopenharmony_ci	.probe = &dw9763_probe,
6713d0407baSopenharmony_ci	.remove = &dw9763_remove,
6723d0407baSopenharmony_ci	.id_table = dw9763_id_table,
6733d0407baSopenharmony_ci};
6743d0407baSopenharmony_ci
6753d0407baSopenharmony_cimodule_i2c_driver(dw9763_i2c_driver);
6763d0407baSopenharmony_ci
6773d0407baSopenharmony_ciMODULE_DESCRIPTION("DW9763 VCM driver");
6783d0407baSopenharmony_ciMODULE_LICENSE("GPL v2");
679