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