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, buf: &status_addr, count: 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, buf: &status_result, count: 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, buf: tx_data, count: 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(sd: &dev_vcm->sd); |
119 | |
120 | dev_vcm->current_val = ctrl->val; |
121 | return dw9807_set_dac(client, data: 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 | return pm_runtime_resume_and_get(dev: sd->dev); |
134 | } |
135 | |
136 | static int dw9807_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) |
137 | { |
138 | pm_runtime_put(dev: sd->dev); |
139 | |
140 | return 0; |
141 | } |
142 | |
143 | static const struct v4l2_subdev_internal_ops dw9807_int_ops = { |
144 | .open = dw9807_open, |
145 | .close = dw9807_close, |
146 | }; |
147 | |
148 | static const struct v4l2_subdev_ops dw9807_ops = { }; |
149 | |
150 | static void dw9807_subdev_cleanup(struct dw9807_device *dw9807_dev) |
151 | { |
152 | v4l2_async_unregister_subdev(sd: &dw9807_dev->sd); |
153 | v4l2_ctrl_handler_free(hdl: &dw9807_dev->ctrls_vcm); |
154 | media_entity_cleanup(entity: &dw9807_dev->sd.entity); |
155 | } |
156 | |
157 | static int dw9807_init_controls(struct dw9807_device *dev_vcm) |
158 | { |
159 | struct v4l2_ctrl_handler *hdl = &dev_vcm->ctrls_vcm; |
160 | const struct v4l2_ctrl_ops *ops = &dw9807_vcm_ctrl_ops; |
161 | struct i2c_client *client = v4l2_get_subdevdata(sd: &dev_vcm->sd); |
162 | |
163 | v4l2_ctrl_handler_init(hdl, 1); |
164 | |
165 | v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE, |
166 | min: 0, DW9807_MAX_FOCUS_POS, DW9807_FOCUS_STEPS, def: 0); |
167 | |
168 | dev_vcm->sd.ctrl_handler = hdl; |
169 | if (hdl->error) { |
170 | dev_err(&client->dev, "%s fail error: 0x%x\n" , |
171 | __func__, hdl->error); |
172 | return hdl->error; |
173 | } |
174 | |
175 | return 0; |
176 | } |
177 | |
178 | static int dw9807_probe(struct i2c_client *client) |
179 | { |
180 | struct dw9807_device *dw9807_dev; |
181 | int rval; |
182 | |
183 | dw9807_dev = devm_kzalloc(dev: &client->dev, size: sizeof(*dw9807_dev), |
184 | GFP_KERNEL); |
185 | if (dw9807_dev == NULL) |
186 | return -ENOMEM; |
187 | |
188 | v4l2_i2c_subdev_init(sd: &dw9807_dev->sd, client, ops: &dw9807_ops); |
189 | dw9807_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; |
190 | dw9807_dev->sd.internal_ops = &dw9807_int_ops; |
191 | |
192 | rval = dw9807_init_controls(dev_vcm: dw9807_dev); |
193 | if (rval) |
194 | goto err_cleanup; |
195 | |
196 | rval = media_entity_pads_init(entity: &dw9807_dev->sd.entity, num_pads: 0, NULL); |
197 | if (rval < 0) |
198 | goto err_cleanup; |
199 | |
200 | dw9807_dev->sd.entity.function = MEDIA_ENT_F_LENS; |
201 | |
202 | rval = v4l2_async_register_subdev(sd: &dw9807_dev->sd); |
203 | if (rval < 0) |
204 | goto err_cleanup; |
205 | |
206 | pm_runtime_set_active(dev: &client->dev); |
207 | pm_runtime_enable(dev: &client->dev); |
208 | pm_runtime_idle(dev: &client->dev); |
209 | |
210 | return 0; |
211 | |
212 | err_cleanup: |
213 | v4l2_ctrl_handler_free(hdl: &dw9807_dev->ctrls_vcm); |
214 | media_entity_cleanup(entity: &dw9807_dev->sd.entity); |
215 | |
216 | return rval; |
217 | } |
218 | |
219 | static void dw9807_remove(struct i2c_client *client) |
220 | { |
221 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
222 | struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(subdev: sd); |
223 | |
224 | pm_runtime_disable(dev: &client->dev); |
225 | |
226 | dw9807_subdev_cleanup(dw9807_dev); |
227 | } |
228 | |
229 | /* |
230 | * This function sets the vcm position, so it consumes least current |
231 | * The lens position is gradually moved in units of DW9807_CTRL_STEPS, |
232 | * to make the movements smoothly. |
233 | */ |
234 | static int __maybe_unused dw9807_vcm_suspend(struct device *dev) |
235 | { |
236 | struct i2c_client *client = to_i2c_client(dev); |
237 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
238 | struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(subdev: sd); |
239 | const char tx_data[2] = { DW9807_CTL_ADDR, 0x01 }; |
240 | int ret, val; |
241 | |
242 | for (val = dw9807_dev->current_val & ~(DW9807_CTRL_STEPS - 1); |
243 | val >= 0; val -= DW9807_CTRL_STEPS) { |
244 | ret = dw9807_set_dac(client, data: val); |
245 | if (ret) |
246 | dev_err_once(dev, "%s I2C failure: %d" , __func__, ret); |
247 | usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10); |
248 | } |
249 | |
250 | /* Power down */ |
251 | ret = i2c_master_send(client, buf: tx_data, count: sizeof(tx_data)); |
252 | if (ret < 0) { |
253 | dev_err(&client->dev, "I2C write CTL fail ret = %d\n" , ret); |
254 | return ret; |
255 | } |
256 | |
257 | return 0; |
258 | } |
259 | |
260 | /* |
261 | * This function sets the vcm position to the value set by the user |
262 | * through v4l2_ctrl_ops s_ctrl handler |
263 | * The lens position is gradually moved in units of DW9807_CTRL_STEPS, |
264 | * to make the movements smoothly. |
265 | */ |
266 | static int __maybe_unused dw9807_vcm_resume(struct device *dev) |
267 | { |
268 | struct i2c_client *client = to_i2c_client(dev); |
269 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
270 | struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(subdev: sd); |
271 | const char tx_data[2] = { DW9807_CTL_ADDR, 0x00 }; |
272 | int ret, val; |
273 | |
274 | /* Power on */ |
275 | ret = i2c_master_send(client, buf: tx_data, count: sizeof(tx_data)); |
276 | if (ret < 0) { |
277 | dev_err(&client->dev, "I2C write CTL fail ret = %d\n" , ret); |
278 | return ret; |
279 | } |
280 | |
281 | for (val = dw9807_dev->current_val % DW9807_CTRL_STEPS; |
282 | val < dw9807_dev->current_val + DW9807_CTRL_STEPS - 1; |
283 | val += DW9807_CTRL_STEPS) { |
284 | ret = dw9807_set_dac(client, data: val); |
285 | if (ret) |
286 | dev_err_ratelimited(dev, "%s I2C failure: %d" , |
287 | __func__, ret); |
288 | usleep_range(DW9807_CTRL_DELAY_US, DW9807_CTRL_DELAY_US + 10); |
289 | } |
290 | |
291 | return 0; |
292 | } |
293 | |
294 | static const struct of_device_id dw9807_of_table[] = { |
295 | { .compatible = "dongwoon,dw9807-vcm" }, |
296 | /* Compatibility for older firmware, NEVER USE THIS IN FIRMWARE! */ |
297 | { .compatible = "dongwoon,dw9807" }, |
298 | { /* sentinel */ } |
299 | }; |
300 | MODULE_DEVICE_TABLE(of, dw9807_of_table); |
301 | |
302 | static const struct dev_pm_ops dw9807_pm_ops = { |
303 | SET_SYSTEM_SLEEP_PM_OPS(dw9807_vcm_suspend, dw9807_vcm_resume) |
304 | SET_RUNTIME_PM_OPS(dw9807_vcm_suspend, dw9807_vcm_resume, NULL) |
305 | }; |
306 | |
307 | static struct i2c_driver dw9807_i2c_driver = { |
308 | .driver = { |
309 | .name = "dw9807" , |
310 | .pm = &dw9807_pm_ops, |
311 | .of_match_table = dw9807_of_table, |
312 | }, |
313 | .probe = dw9807_probe, |
314 | .remove = dw9807_remove, |
315 | }; |
316 | |
317 | module_i2c_driver(dw9807_i2c_driver); |
318 | |
319 | MODULE_AUTHOR("Chiang, Alan" ); |
320 | MODULE_DESCRIPTION("DW9807 VCM driver" ); |
321 | MODULE_LICENSE("GPL v2" ); |
322 | |