xref: /openbmc/linux/drivers/media/i2c/dw9807-vcm.c (revision aaeb31c0)
1e6c17adaSSakari Ailus // SPDX-License-Identifier: GPL-2.0
2e6c17adaSSakari Ailus // Copyright (C) 2018 Intel Corporation
3e6c17adaSSakari Ailus 
4e6c17adaSSakari Ailus #include <linux/acpi.h>
5e6c17adaSSakari Ailus #include <linux/delay.h>
6e6c17adaSSakari Ailus #include <linux/i2c.h>
7e6c17adaSSakari Ailus #include <linux/iopoll.h>
8e6c17adaSSakari Ailus #include <linux/module.h>
9e6c17adaSSakari Ailus #include <linux/pm_runtime.h>
10e6c17adaSSakari Ailus #include <media/v4l2-ctrls.h>
11e6c17adaSSakari Ailus #include <media/v4l2-device.h>
12e6c17adaSSakari Ailus 
13e6c17adaSSakari Ailus #define DW9807_MAX_FOCUS_POS	1023
14e6c17adaSSakari Ailus /*
15e6c17adaSSakari Ailus  * This sets the minimum granularity for the focus positions.
16e6c17adaSSakari Ailus  * A value of 1 gives maximum accuracy for a desired focus position.
17e6c17adaSSakari Ailus  */
18e6c17adaSSakari Ailus #define DW9807_FOCUS_STEPS	1
19e6c17adaSSakari Ailus /*
20e6c17adaSSakari Ailus  * This acts as the minimum granularity of lens movement.
21e6c17adaSSakari Ailus  * Keep this value power of 2, so the control steps can be
22e6c17adaSSakari Ailus  * uniformly adjusted for gradual lens movement, with desired
23e6c17adaSSakari Ailus  * number of control steps.
24e6c17adaSSakari Ailus  */
25e6c17adaSSakari Ailus #define DW9807_CTRL_STEPS	16
26e6c17adaSSakari Ailus #define DW9807_CTRL_DELAY_US	1000
27e6c17adaSSakari Ailus 
28e6c17adaSSakari Ailus #define DW9807_CTL_ADDR		0x02
29e6c17adaSSakari Ailus /*
30e6c17adaSSakari Ailus  * DW9807 separates two registers to control the VCM position.
31e6c17adaSSakari Ailus  * One for MSB value, another is LSB value.
32e6c17adaSSakari Ailus  */
33e6c17adaSSakari Ailus #define DW9807_MSB_ADDR		0x03
34e6c17adaSSakari Ailus #define DW9807_LSB_ADDR		0x04
35e6c17adaSSakari Ailus #define DW9807_STATUS_ADDR	0x05
36e6c17adaSSakari Ailus #define DW9807_MODE_ADDR	0x06
37e6c17adaSSakari Ailus #define DW9807_RESONANCE_ADDR	0x07
38e6c17adaSSakari Ailus 
39e6c17adaSSakari Ailus #define MAX_RETRY		10
40e6c17adaSSakari Ailus 
41e6c17adaSSakari Ailus struct dw9807_device {
42e6c17adaSSakari Ailus 	struct v4l2_ctrl_handler ctrls_vcm;
43e6c17adaSSakari Ailus 	struct v4l2_subdev sd;
44e6c17adaSSakari Ailus 	u16 current_val;
45e6c17adaSSakari Ailus };
46e6c17adaSSakari Ailus 
sd_to_dw9807_vcm(struct v4l2_subdev * subdev)47e6c17adaSSakari Ailus static inline struct dw9807_device *sd_to_dw9807_vcm(
48e6c17adaSSakari Ailus 					struct v4l2_subdev *subdev)
49e6c17adaSSakari Ailus {
50e6c17adaSSakari Ailus 	return container_of(subdev, struct dw9807_device, sd);
51e6c17adaSSakari Ailus }
52e6c17adaSSakari Ailus 
dw9807_i2c_check(struct i2c_client * client)53e6c17adaSSakari Ailus static int dw9807_i2c_check(struct i2c_client *client)
54e6c17adaSSakari Ailus {
55e6c17adaSSakari Ailus 	const char status_addr = DW9807_STATUS_ADDR;
56e6c17adaSSakari Ailus 	char status_result;
57e6c17adaSSakari Ailus 	int ret;
58e6c17adaSSakari Ailus 
59e6c17adaSSakari Ailus 	ret = i2c_master_send(client, &status_addr, sizeof(status_addr));
60e6c17adaSSakari Ailus 	if (ret < 0) {
61e6c17adaSSakari Ailus 		dev_err(&client->dev, "I2C write STATUS address fail ret = %d\n",
62e6c17adaSSakari Ailus 			ret);
63e6c17adaSSakari Ailus 		return ret;
64e6c17adaSSakari Ailus 	}
65e6c17adaSSakari Ailus 
66e6c17adaSSakari Ailus 	ret = i2c_master_recv(client, &status_result, sizeof(status_result));
67e6c17adaSSakari Ailus 	if (ret < 0) {
68e6c17adaSSakari Ailus 		dev_err(&client->dev, "I2C read STATUS value fail ret = %d\n",
69e6c17adaSSakari Ailus 			ret);
70e6c17adaSSakari Ailus 		return ret;
71e6c17adaSSakari Ailus 	}
72e6c17adaSSakari Ailus 
73e6c17adaSSakari Ailus 	return status_result;
74e6c17adaSSakari Ailus }
75e6c17adaSSakari Ailus 
dw9807_set_dac(struct i2c_client * client,u16 data)76e6c17adaSSakari Ailus static int dw9807_set_dac(struct i2c_client *client, u16 data)
77e6c17adaSSakari Ailus {
78e6c17adaSSakari Ailus 	const char tx_data[3] = {
79e6c17adaSSakari Ailus 		DW9807_MSB_ADDR, ((data >> 8) & 0x03), (data & 0xff)
80e6c17adaSSakari Ailus 	};
81e6c17adaSSakari Ailus 	int val, ret;
82e6c17adaSSakari Ailus 
83e6c17adaSSakari Ailus 	/*
84e6c17adaSSakari Ailus 	 * According to the datasheet, need to check the bus status before we
85e6c17adaSSakari Ailus 	 * write VCM position. This ensure that we really write the value
86e6c17adaSSakari Ailus 	 * into the register
87e6c17adaSSakari Ailus 	 */
88e6c17adaSSakari Ailus 	ret = readx_poll_timeout(dw9807_i2c_check, client, val, val <= 0,
89e6c17adaSSakari Ailus 			DW9807_CTRL_DELAY_US, MAX_RETRY * DW9807_CTRL_DELAY_US);
90e6c17adaSSakari Ailus 
91e6c17adaSSakari Ailus 	if (ret || val < 0) {
92e6c17adaSSakari Ailus 		if (ret) {
93e6c17adaSSakari Ailus 			dev_warn(&client->dev,
94e6c17adaSSakari Ailus 				"Cannot do the write operation because VCM is busy\n");
95e6c17adaSSakari Ailus 		}
96e6c17adaSSakari Ailus 
97e6c17adaSSakari Ailus 		return ret ? -EBUSY : val;
98e6c17adaSSakari Ailus 	}
99e6c17adaSSakari Ailus 
100e6c17adaSSakari Ailus 	/* Write VCM position to registers */
101e6c17adaSSakari Ailus 	ret = i2c_master_send(client, tx_data, sizeof(tx_data));
102e6c17adaSSakari Ailus 	if (ret < 0) {
103e6c17adaSSakari Ailus 		dev_err(&client->dev,
104e6c17adaSSakari Ailus 			"I2C write MSB fail ret=%d\n", ret);
105e6c17adaSSakari Ailus 
106e6c17adaSSakari Ailus 		return ret;
107e6c17adaSSakari Ailus 	}
108e6c17adaSSakari Ailus 
109e6c17adaSSakari Ailus 	return 0;
110e6c17adaSSakari Ailus }
111e6c17adaSSakari Ailus 
dw9807_set_ctrl(struct v4l2_ctrl * ctrl)112e6c17adaSSakari Ailus static int dw9807_set_ctrl(struct v4l2_ctrl *ctrl)
113e6c17adaSSakari Ailus {
114e6c17adaSSakari Ailus 	struct dw9807_device *dev_vcm = container_of(ctrl->handler,
115e6c17adaSSakari Ailus 		struct dw9807_device, ctrls_vcm);
116e6c17adaSSakari Ailus 
117e6c17adaSSakari Ailus 	if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) {
118e6c17adaSSakari Ailus 		struct i2c_client *client = v4l2_get_subdevdata(&dev_vcm->sd);
119e6c17adaSSakari Ailus 
120e6c17adaSSakari Ailus 		dev_vcm->current_val = ctrl->val;
121e6c17adaSSakari Ailus 		return dw9807_set_dac(client, ctrl->val);
122e6c17adaSSakari Ailus 	}
123e6c17adaSSakari Ailus 
124e6c17adaSSakari Ailus 	return -EINVAL;
125e6c17adaSSakari Ailus }
126e6c17adaSSakari Ailus 
127e6c17adaSSakari Ailus static const struct v4l2_ctrl_ops dw9807_vcm_ctrl_ops = {
128e6c17adaSSakari Ailus 	.s_ctrl = dw9807_set_ctrl,
129e6c17adaSSakari Ailus };
130e6c17adaSSakari Ailus 
dw9807_open(struct v4l2_subdev * sd,struct v4l2_subdev_fh * fh)131e6c17adaSSakari Ailus static int dw9807_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
132e6c17adaSSakari Ailus {
133d5e75e8bSMauro Carvalho Chehab 	return pm_runtime_resume_and_get(sd->dev);
134e6c17adaSSakari Ailus }
135e6c17adaSSakari Ailus 
dw9807_close(struct v4l2_subdev * sd,struct v4l2_subdev_fh * fh)136e6c17adaSSakari Ailus static int dw9807_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
137e6c17adaSSakari Ailus {
138e6c17adaSSakari Ailus 	pm_runtime_put(sd->dev);
139e6c17adaSSakari Ailus 
140e6c17adaSSakari Ailus 	return 0;
141e6c17adaSSakari Ailus }
142e6c17adaSSakari Ailus 
143e6c17adaSSakari Ailus static const struct v4l2_subdev_internal_ops dw9807_int_ops = {
144e6c17adaSSakari Ailus 	.open = dw9807_open,
145e6c17adaSSakari Ailus 	.close = dw9807_close,
146e6c17adaSSakari Ailus };
147e6c17adaSSakari Ailus 
148e6c17adaSSakari Ailus static const struct v4l2_subdev_ops dw9807_ops = { };
149e6c17adaSSakari Ailus 
dw9807_subdev_cleanup(struct dw9807_device * dw9807_dev)150e6c17adaSSakari Ailus static void dw9807_subdev_cleanup(struct dw9807_device *dw9807_dev)
151e6c17adaSSakari Ailus {
152e6c17adaSSakari Ailus 	v4l2_async_unregister_subdev(&dw9807_dev->sd);
153e6c17adaSSakari Ailus 	v4l2_ctrl_handler_free(&dw9807_dev->ctrls_vcm);
154e6c17adaSSakari Ailus 	media_entity_cleanup(&dw9807_dev->sd.entity);
155e6c17adaSSakari Ailus }
156e6c17adaSSakari Ailus 
dw9807_init_controls(struct dw9807_device * dev_vcm)157e6c17adaSSakari Ailus static int dw9807_init_controls(struct dw9807_device *dev_vcm)
158e6c17adaSSakari Ailus {
159e6c17adaSSakari Ailus 	struct v4l2_ctrl_handler *hdl = &dev_vcm->ctrls_vcm;
160e6c17adaSSakari Ailus 	const struct v4l2_ctrl_ops *ops = &dw9807_vcm_ctrl_ops;
161e6c17adaSSakari Ailus 	struct i2c_client *client = v4l2_get_subdevdata(&dev_vcm->sd);
162e6c17adaSSakari Ailus 
163e6c17adaSSakari Ailus 	v4l2_ctrl_handler_init(hdl, 1);
164e6c17adaSSakari Ailus 
165e6c17adaSSakari Ailus 	v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE,
166e6c17adaSSakari Ailus 			  0, DW9807_MAX_FOCUS_POS, DW9807_FOCUS_STEPS, 0);
167e6c17adaSSakari Ailus 
168e6c17adaSSakari Ailus 	dev_vcm->sd.ctrl_handler = hdl;
169e6c17adaSSakari Ailus 	if (hdl->error) {
170e6c17adaSSakari Ailus 		dev_err(&client->dev, "%s fail error: 0x%x\n",
171e6c17adaSSakari Ailus 			__func__, hdl->error);
172e6c17adaSSakari Ailus 		return hdl->error;
173e6c17adaSSakari Ailus 	}
174e6c17adaSSakari Ailus 
175e6c17adaSSakari Ailus 	return 0;
176e6c17adaSSakari Ailus }
177e6c17adaSSakari Ailus 
dw9807_probe(struct i2c_client * client)178e6c17adaSSakari Ailus static int dw9807_probe(struct i2c_client *client)
179e6c17adaSSakari Ailus {
180e6c17adaSSakari Ailus 	struct dw9807_device *dw9807_dev;
181e6c17adaSSakari Ailus 	int rval;
182e6c17adaSSakari Ailus 
183e6c17adaSSakari Ailus 	dw9807_dev = devm_kzalloc(&client->dev, sizeof(*dw9807_dev),
184e6c17adaSSakari Ailus 				  GFP_KERNEL);
185e6c17adaSSakari Ailus 	if (dw9807_dev == NULL)
186e6c17adaSSakari Ailus 		return -ENOMEM;
187e6c17adaSSakari Ailus 
188e6c17adaSSakari Ailus 	v4l2_i2c_subdev_init(&dw9807_dev->sd, client, &dw9807_ops);
189e6c17adaSSakari Ailus 	dw9807_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
190e6c17adaSSakari Ailus 	dw9807_dev->sd.internal_ops = &dw9807_int_ops;
191e6c17adaSSakari Ailus 
192e6c17adaSSakari Ailus 	rval = dw9807_init_controls(dw9807_dev);
193e6c17adaSSakari Ailus 	if (rval)
194e6c17adaSSakari Ailus 		goto err_cleanup;
195e6c17adaSSakari Ailus 
196e6c17adaSSakari Ailus 	rval = media_entity_pads_init(&dw9807_dev->sd.entity, 0, NULL);
197e6c17adaSSakari Ailus 	if (rval < 0)
198e6c17adaSSakari Ailus 		goto err_cleanup;
199e6c17adaSSakari Ailus 
200e6c17adaSSakari Ailus 	dw9807_dev->sd.entity.function = MEDIA_ENT_F_LENS;
201e6c17adaSSakari Ailus 
202e6c17adaSSakari Ailus 	rval = v4l2_async_register_subdev(&dw9807_dev->sd);
203e6c17adaSSakari Ailus 	if (rval < 0)
204e6c17adaSSakari Ailus 		goto err_cleanup;
205e6c17adaSSakari Ailus 
206e6c17adaSSakari Ailus 	pm_runtime_set_active(&client->dev);
207e6c17adaSSakari Ailus 	pm_runtime_enable(&client->dev);
208e6c17adaSSakari Ailus 	pm_runtime_idle(&client->dev);
209e6c17adaSSakari Ailus 
210e6c17adaSSakari Ailus 	return 0;
211e6c17adaSSakari Ailus 
212e6c17adaSSakari Ailus err_cleanup:
2139e5b5081SSakari Ailus 	v4l2_ctrl_handler_free(&dw9807_dev->ctrls_vcm);
2149e5b5081SSakari Ailus 	media_entity_cleanup(&dw9807_dev->sd.entity);
215e6c17adaSSakari Ailus 
216e6c17adaSSakari Ailus 	return rval;
217e6c17adaSSakari Ailus }
218e6c17adaSSakari Ailus 
dw9807_remove(struct i2c_client * client)219ed5c2f5fSUwe Kleine-König static void dw9807_remove(struct i2c_client *client)
220e6c17adaSSakari Ailus {
221e6c17adaSSakari Ailus 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
222e6c17adaSSakari Ailus 	struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd);
223e6c17adaSSakari Ailus 
224e6c17adaSSakari Ailus 	pm_runtime_disable(&client->dev);
225e6c17adaSSakari Ailus 
226e6c17adaSSakari Ailus 	dw9807_subdev_cleanup(dw9807_dev);
227e6c17adaSSakari Ailus }
228e6c17adaSSakari Ailus 
229e6c17adaSSakari Ailus /*
230e6c17adaSSakari Ailus  * This function sets the vcm position, so it consumes least current
231e6c17adaSSakari Ailus  * The lens position is gradually moved in units of DW9807_CTRL_STEPS,
232e6c17adaSSakari Ailus  * to make the movements smoothly.
233e6c17adaSSakari Ailus  */
dw9807_vcm_suspend(struct device * dev)234e6c17adaSSakari Ailus static int __maybe_unused dw9807_vcm_suspend(struct device *dev)
235e6c17adaSSakari Ailus {
236e6c17adaSSakari Ailus 	struct i2c_client *client = to_i2c_client(dev);
237e6c17adaSSakari Ailus 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
238e6c17adaSSakari Ailus 	struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd);
239e6c17adaSSakari Ailus 	const char tx_data[2] = { DW9807_CTL_ADDR, 0x01 };
240e6c17adaSSakari Ailus 	int ret, val;
241e6c17adaSSakari Ailus 
242e6c17adaSSakari Ailus 	for (val = dw9807_dev->current_val & ~(DW9807_CTRL_STEPS - 1);
243e6c17adaSSakari Ailus 	     val >= 0; val -= DW9807_CTRL_STEPS) {
244e6c17adaSSakari Ailus 		ret = dw9807_set_dac(client, val);
245e6c17adaSSakari Ailus 		if (ret)
246e6c17adaSSakari Ailus 			dev_err_once(dev, "%s I2C failure: %d", __func__, ret);
247e6c17adaSSakari Ailus 		usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10);
248e6c17adaSSakari Ailus 	}
249e6c17adaSSakari Ailus 
250e6c17adaSSakari Ailus 	/* Power down */
251e6c17adaSSakari Ailus 	ret = i2c_master_send(client, tx_data, sizeof(tx_data));
252e6c17adaSSakari Ailus 	if (ret < 0) {
253e6c17adaSSakari Ailus 		dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret);
254e6c17adaSSakari Ailus 		return ret;
255e6c17adaSSakari Ailus 	}
256e6c17adaSSakari Ailus 
257e6c17adaSSakari Ailus 	return 0;
258e6c17adaSSakari Ailus }
259e6c17adaSSakari Ailus 
260e6c17adaSSakari Ailus /*
261e6c17adaSSakari Ailus  * This function sets the vcm position to the value set by the user
262e6c17adaSSakari Ailus  * through v4l2_ctrl_ops s_ctrl handler
263e6c17adaSSakari Ailus  * The lens position is gradually moved in units of DW9807_CTRL_STEPS,
264e6c17adaSSakari Ailus  * to make the movements smoothly.
265e6c17adaSSakari Ailus  */
dw9807_vcm_resume(struct device * dev)266e6c17adaSSakari Ailus static int  __maybe_unused dw9807_vcm_resume(struct device *dev)
267e6c17adaSSakari Ailus {
268e6c17adaSSakari Ailus 	struct i2c_client *client = to_i2c_client(dev);
269e6c17adaSSakari Ailus 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
270e6c17adaSSakari Ailus 	struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd);
271e6c17adaSSakari Ailus 	const char tx_data[2] = { DW9807_CTL_ADDR, 0x00 };
272e6c17adaSSakari Ailus 	int ret, val;
273e6c17adaSSakari Ailus 
274e6c17adaSSakari Ailus 	/* Power on */
275e6c17adaSSakari Ailus 	ret = i2c_master_send(client, tx_data, sizeof(tx_data));
276e6c17adaSSakari Ailus 	if (ret < 0) {
277e6c17adaSSakari Ailus 		dev_err(&client->dev, "I2C write CTL fail ret = %d\n", ret);
278e6c17adaSSakari Ailus 		return ret;
279e6c17adaSSakari Ailus 	}
280e6c17adaSSakari Ailus 
281e6c17adaSSakari Ailus 	for (val = dw9807_dev->current_val % DW9807_CTRL_STEPS;
282e6c17adaSSakari Ailus 	     val < dw9807_dev->current_val + DW9807_CTRL_STEPS - 1;
283e6c17adaSSakari Ailus 	     val += DW9807_CTRL_STEPS) {
284e6c17adaSSakari Ailus 		ret = dw9807_set_dac(client, val);
285e6c17adaSSakari Ailus 		if (ret)
286e6c17adaSSakari Ailus 			dev_err_ratelimited(dev, "%s I2C failure: %d",
287e6c17adaSSakari Ailus 						__func__, ret);
288e6c17adaSSakari Ailus 		usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10);
289e6c17adaSSakari Ailus 	}
290e6c17adaSSakari Ailus 
291e6c17adaSSakari Ailus 	return 0;
292e6c17adaSSakari Ailus }
293e6c17adaSSakari Ailus 
294e6c17adaSSakari Ailus static const struct of_device_id dw9807_of_table[] = {
295e6c17adaSSakari Ailus 	{ .compatible = "dongwoon,dw9807-vcm" },
296c1b77f25SSakari Ailus 	/* Compatibility for older firmware, NEVER USE THIS IN FIRMWARE! */
297c1b77f25SSakari Ailus 	{ .compatible = "dongwoon,dw9807" },
298e6c17adaSSakari Ailus 	{ /* sentinel */ }
299e6c17adaSSakari Ailus };
300e6c17adaSSakari Ailus MODULE_DEVICE_TABLE(of, dw9807_of_table);
301e6c17adaSSakari Ailus 
302e6c17adaSSakari Ailus static const struct dev_pm_ops dw9807_pm_ops = {
303e6c17adaSSakari Ailus 	SET_SYSTEM_SLEEP_PM_OPS(dw9807_vcm_suspend, dw9807_vcm_resume)
304e6c17adaSSakari Ailus 	SET_RUNTIME_PM_OPS(dw9807_vcm_suspend, dw9807_vcm_resume, NULL)
305e6c17adaSSakari Ailus };
306e6c17adaSSakari Ailus 
307e6c17adaSSakari Ailus static struct i2c_driver dw9807_i2c_driver = {
308e6c17adaSSakari Ailus 	.driver = {
309e6c17adaSSakari Ailus 		.name = "dw9807",
310e6c17adaSSakari Ailus 		.pm = &dw9807_pm_ops,
311e6c17adaSSakari Ailus 		.of_match_table = dw9807_of_table,
312e6c17adaSSakari Ailus 	},
313*aaeb31c0SUwe Kleine-König 	.probe = dw9807_probe,
314e6c17adaSSakari Ailus 	.remove = dw9807_remove,
315e6c17adaSSakari Ailus };
316e6c17adaSSakari Ailus 
317e6c17adaSSakari Ailus module_i2c_driver(dw9807_i2c_driver);
318e6c17adaSSakari Ailus 
3192f248f7fSSakari Ailus MODULE_AUTHOR("Chiang, Alan");
320e6c17adaSSakari Ailus MODULE_DESCRIPTION("DW9807 VCM driver");
321e6c17adaSSakari Ailus MODULE_LICENSE("GPL v2");
322