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