]>
Commit | Line | Data |
---|---|---|
1802d0be | 1 | // SPDX-License-Identifier: GPL-2.0-only |
bee3d511 PM |
2 | /* |
3 | * drivers/media/i2c/ad5820.c | |
4 | * | |
5 | * AD5820 DAC driver for camera voice coil focus. | |
6 | * | |
7 | * Copyright (C) 2008 Nokia Corporation | |
8 | * Copyright (C) 2007 Texas Instruments | |
9 | * Copyright (C) 2016 Pavel Machek <pavel@ucw.cz> | |
10 | * | |
11 | * Contact: Tuukka Toivonen <tuukkat76@gmail.com> | |
12 | * Sakari Ailus <sakari.ailus@iki.fi> | |
13 | * | |
14 | * Based on af_d88.c by Texas Instruments. | |
bee3d511 PM |
15 | */ |
16 | ||
17 | #include <linux/errno.h> | |
18 | #include <linux/i2c.h> | |
19 | #include <linux/kernel.h> | |
20 | #include <linux/module.h> | |
21 | #include <linux/regulator/consumer.h> | |
22 | ||
23 | #include <media/v4l2-ctrls.h> | |
24 | #include <media/v4l2-device.h> | |
25 | #include <media/v4l2-subdev.h> | |
26 | ||
27 | #define AD5820_NAME "ad5820" | |
28 | ||
29 | /* Register definitions */ | |
30 | #define AD5820_POWER_DOWN (1 << 15) | |
31 | #define AD5820_DAC_SHIFT 4 | |
32 | #define AD5820_RAMP_MODE_LINEAR (0 << 3) | |
33 | #define AD5820_RAMP_MODE_64_16 (1 << 3) | |
34 | ||
35 | #define CODE_TO_RAMP_US(s) ((s) == 0 ? 0 : (1 << ((s) - 1)) * 50) | |
36 | #define RAMP_US_TO_CODE(c) fls(((c) + ((c)>>1)) / 50) | |
37 | ||
38 | #define to_ad5820_device(sd) container_of(sd, struct ad5820_device, subdev) | |
39 | ||
40 | struct ad5820_device { | |
41 | struct v4l2_subdev subdev; | |
42 | struct ad5820_platform_data *platform_data; | |
43 | struct regulator *vana; | |
44 | ||
45 | struct v4l2_ctrl_handler ctrls; | |
46 | u32 focus_absolute; | |
47 | u32 focus_ramp_time; | |
48 | u32 focus_ramp_mode; | |
49 | ||
50 | struct mutex power_lock; | |
51 | int power_count; | |
52 | ||
3933186a | 53 | bool standby; |
bee3d511 PM |
54 | }; |
55 | ||
56 | static int ad5820_write(struct ad5820_device *coil, u16 data) | |
57 | { | |
58 | struct i2c_client *client = v4l2_get_subdevdata(&coil->subdev); | |
59 | struct i2c_msg msg; | |
1ff52fa0 | 60 | __be16 be_data; |
bee3d511 PM |
61 | int r; |
62 | ||
63 | if (!client->adapter) | |
64 | return -ENODEV; | |
65 | ||
1ff52fa0 | 66 | be_data = cpu_to_be16(data); |
bee3d511 PM |
67 | msg.addr = client->addr; |
68 | msg.flags = 0; | |
69 | msg.len = 2; | |
1ff52fa0 | 70 | msg.buf = (u8 *)&be_data; |
bee3d511 PM |
71 | |
72 | r = i2c_transfer(client->adapter, &msg, 1); | |
73 | if (r < 0) { | |
74 | dev_err(&client->dev, "write failed, error %d\n", r); | |
75 | return r; | |
76 | } | |
77 | ||
78 | return 0; | |
79 | } | |
80 | ||
81 | /* | |
82 | * Calculate status word and write it to the device based on current | |
83 | * values of V4L2 controls. It is assumed that the stored V4L2 control | |
84 | * values are properly limited and rounded. | |
85 | */ | |
86 | static int ad5820_update_hw(struct ad5820_device *coil) | |
87 | { | |
88 | u16 status; | |
89 | ||
90 | status = RAMP_US_TO_CODE(coil->focus_ramp_time); | |
91 | status |= coil->focus_ramp_mode | |
92 | ? AD5820_RAMP_MODE_64_16 : AD5820_RAMP_MODE_LINEAR; | |
93 | status |= coil->focus_absolute << AD5820_DAC_SHIFT; | |
94 | ||
95 | if (coil->standby) | |
96 | status |= AD5820_POWER_DOWN; | |
97 | ||
98 | return ad5820_write(coil, status); | |
99 | } | |
100 | ||
101 | /* | |
102 | * Power handling | |
103 | */ | |
3933186a | 104 | static int ad5820_power_off(struct ad5820_device *coil, bool standby) |
bee3d511 PM |
105 | { |
106 | int ret = 0, ret2; | |
107 | ||
108 | /* | |
109 | * Go to standby first as real power off my be denied by the hardware | |
110 | * (single power line control for both coil and sensor). | |
111 | */ | |
112 | if (standby) { | |
3933186a | 113 | coil->standby = true; |
bee3d511 PM |
114 | ret = ad5820_update_hw(coil); |
115 | } | |
116 | ||
117 | ret2 = regulator_disable(coil->vana); | |
118 | if (ret) | |
119 | return ret; | |
120 | return ret2; | |
121 | } | |
122 | ||
3933186a | 123 | static int ad5820_power_on(struct ad5820_device *coil, bool restore) |
bee3d511 PM |
124 | { |
125 | int ret; | |
126 | ||
127 | ret = regulator_enable(coil->vana); | |
128 | if (ret < 0) | |
129 | return ret; | |
130 | ||
131 | if (restore) { | |
132 | /* Restore the hardware settings. */ | |
3933186a | 133 | coil->standby = false; |
bee3d511 PM |
134 | ret = ad5820_update_hw(coil); |
135 | if (ret) | |
136 | goto fail; | |
137 | } | |
138 | return 0; | |
139 | ||
140 | fail: | |
3933186a | 141 | coil->standby = true; |
bee3d511 PM |
142 | regulator_disable(coil->vana); |
143 | ||
144 | return ret; | |
145 | } | |
146 | ||
147 | /* | |
148 | * V4L2 controls | |
149 | */ | |
150 | static int ad5820_set_ctrl(struct v4l2_ctrl *ctrl) | |
151 | { | |
152 | struct ad5820_device *coil = | |
153 | container_of(ctrl->handler, struct ad5820_device, ctrls); | |
154 | ||
155 | switch (ctrl->id) { | |
156 | case V4L2_CID_FOCUS_ABSOLUTE: | |
157 | coil->focus_absolute = ctrl->val; | |
158 | return ad5820_update_hw(coil); | |
159 | } | |
160 | ||
161 | return 0; | |
162 | } | |
163 | ||
164 | static const struct v4l2_ctrl_ops ad5820_ctrl_ops = { | |
165 | .s_ctrl = ad5820_set_ctrl, | |
166 | }; | |
167 | ||
168 | ||
169 | static int ad5820_init_controls(struct ad5820_device *coil) | |
170 | { | |
171 | v4l2_ctrl_handler_init(&coil->ctrls, 1); | |
172 | ||
173 | /* | |
174 | * V4L2_CID_FOCUS_ABSOLUTE | |
175 | * | |
176 | * Minimum current is 0 mA, maximum is 100 mA. Thus, 1 code is | |
177 | * equivalent to 100/1023 = 0.0978 mA. Nevertheless, we do not use [mA] | |
178 | * for focus position, because it is meaningless for user. Meaningful | |
179 | * would be to use focus distance or even its inverse, but since the | |
180 | * driver doesn't have sufficiently knowledge to do the conversion, we | |
181 | * will just use abstract codes here. In any case, smaller value = focus | |
182 | * position farther from camera. The default zero value means focus at | |
183 | * infinity, and also least current consumption. | |
184 | */ | |
185 | v4l2_ctrl_new_std(&coil->ctrls, &ad5820_ctrl_ops, | |
186 | V4L2_CID_FOCUS_ABSOLUTE, 0, 1023, 1, 0); | |
187 | ||
188 | if (coil->ctrls.error) | |
189 | return coil->ctrls.error; | |
190 | ||
191 | coil->focus_absolute = 0; | |
192 | coil->focus_ramp_time = 0; | |
193 | coil->focus_ramp_mode = 0; | |
194 | ||
195 | coil->subdev.ctrl_handler = &coil->ctrls; | |
196 | ||
197 | return 0; | |
198 | } | |
199 | ||
200 | /* | |
201 | * V4L2 subdev operations | |
202 | */ | |
203 | static int ad5820_registered(struct v4l2_subdev *subdev) | |
204 | { | |
205 | struct ad5820_device *coil = to_ad5820_device(subdev); | |
206 | ||
207 | return ad5820_init_controls(coil); | |
208 | } | |
209 | ||
210 | static int | |
211 | ad5820_set_power(struct v4l2_subdev *subdev, int on) | |
212 | { | |
213 | struct ad5820_device *coil = to_ad5820_device(subdev); | |
214 | int ret = 0; | |
215 | ||
216 | mutex_lock(&coil->power_lock); | |
217 | ||
218 | /* | |
219 | * If the power count is modified from 0 to != 0 or from != 0 to 0, | |
220 | * update the power state. | |
221 | */ | |
222 | if (coil->power_count == !on) { | |
3933186a SA |
223 | ret = on ? ad5820_power_on(coil, true) : |
224 | ad5820_power_off(coil, true); | |
bee3d511 PM |
225 | if (ret < 0) |
226 | goto done; | |
227 | } | |
228 | ||
229 | /* Update the power count. */ | |
230 | coil->power_count += on ? 1 : -1; | |
231 | WARN_ON(coil->power_count < 0); | |
232 | ||
233 | done: | |
234 | mutex_unlock(&coil->power_lock); | |
235 | return ret; | |
236 | } | |
237 | ||
238 | static int ad5820_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | |
239 | { | |
240 | return ad5820_set_power(sd, 1); | |
241 | } | |
242 | ||
243 | static int ad5820_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | |
244 | { | |
245 | return ad5820_set_power(sd, 0); | |
246 | } | |
247 | ||
248 | static const struct v4l2_subdev_core_ops ad5820_core_ops = { | |
249 | .s_power = ad5820_set_power, | |
250 | }; | |
251 | ||
252 | static const struct v4l2_subdev_ops ad5820_ops = { | |
253 | .core = &ad5820_core_ops, | |
254 | }; | |
255 | ||
256 | static const struct v4l2_subdev_internal_ops ad5820_internal_ops = { | |
257 | .registered = ad5820_registered, | |
258 | .open = ad5820_open, | |
259 | .close = ad5820_close, | |
260 | }; | |
261 | ||
262 | /* | |
263 | * I2C driver | |
264 | */ | |
37f0644f | 265 | static int __maybe_unused ad5820_suspend(struct device *dev) |
bee3d511 PM |
266 | { |
267 | struct i2c_client *client = container_of(dev, struct i2c_client, dev); | |
268 | struct v4l2_subdev *subdev = i2c_get_clientdata(client); | |
269 | struct ad5820_device *coil = to_ad5820_device(subdev); | |
270 | ||
271 | if (!coil->power_count) | |
272 | return 0; | |
273 | ||
3933186a | 274 | return ad5820_power_off(coil, false); |
bee3d511 PM |
275 | } |
276 | ||
37f0644f | 277 | static int __maybe_unused ad5820_resume(struct device *dev) |
bee3d511 PM |
278 | { |
279 | struct i2c_client *client = container_of(dev, struct i2c_client, dev); | |
280 | struct v4l2_subdev *subdev = i2c_get_clientdata(client); | |
281 | struct ad5820_device *coil = to_ad5820_device(subdev); | |
282 | ||
283 | if (!coil->power_count) | |
284 | return 0; | |
285 | ||
3933186a | 286 | return ad5820_power_on(coil, true); |
bee3d511 PM |
287 | } |
288 | ||
bee3d511 PM |
289 | static int ad5820_probe(struct i2c_client *client, |
290 | const struct i2c_device_id *devid) | |
291 | { | |
292 | struct ad5820_device *coil; | |
293 | int ret; | |
294 | ||
295 | coil = devm_kzalloc(&client->dev, sizeof(*coil), GFP_KERNEL); | |
296 | if (!coil) | |
297 | return -ENOMEM; | |
298 | ||
299 | coil->vana = devm_regulator_get(&client->dev, "VANA"); | |
300 | if (IS_ERR(coil->vana)) { | |
301 | ret = PTR_ERR(coil->vana); | |
302 | if (ret != -EPROBE_DEFER) | |
303 | dev_err(&client->dev, "could not get regulator for vana\n"); | |
304 | return ret; | |
305 | } | |
306 | ||
307 | mutex_init(&coil->power_lock); | |
308 | ||
309 | v4l2_i2c_subdev_init(&coil->subdev, client, &ad5820_ops); | |
310 | coil->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | |
311 | coil->subdev.internal_ops = &ad5820_internal_ops; | |
cc1e6315 | 312 | strscpy(coil->subdev.name, "ad5820 focus", sizeof(coil->subdev.name)); |
bee3d511 PM |
313 | |
314 | ret = media_entity_pads_init(&coil->subdev.entity, 0, NULL); | |
315 | if (ret < 0) | |
316 | goto cleanup2; | |
317 | ||
318 | ret = v4l2_async_register_subdev(&coil->subdev); | |
319 | if (ret < 0) | |
320 | goto cleanup; | |
321 | ||
322 | return ret; | |
323 | ||
324 | cleanup2: | |
325 | mutex_destroy(&coil->power_lock); | |
326 | cleanup: | |
327 | media_entity_cleanup(&coil->subdev.entity); | |
328 | return ret; | |
329 | } | |
330 | ||
78cea55c | 331 | static int ad5820_remove(struct i2c_client *client) |
bee3d511 PM |
332 | { |
333 | struct v4l2_subdev *subdev = i2c_get_clientdata(client); | |
334 | struct ad5820_device *coil = to_ad5820_device(subdev); | |
335 | ||
586248a7 | 336 | v4l2_async_unregister_subdev(&coil->subdev); |
bee3d511 PM |
337 | v4l2_ctrl_handler_free(&coil->ctrls); |
338 | media_entity_cleanup(&coil->subdev.entity); | |
339 | mutex_destroy(&coil->power_lock); | |
340 | return 0; | |
341 | } | |
342 | ||
343 | static const struct i2c_device_id ad5820_id_table[] = { | |
344 | { AD5820_NAME, 0 }, | |
345 | { } | |
346 | }; | |
347 | MODULE_DEVICE_TABLE(i2c, ad5820_id_table); | |
348 | ||
349 | static SIMPLE_DEV_PM_OPS(ad5820_pm, ad5820_suspend, ad5820_resume); | |
350 | ||
351 | static struct i2c_driver ad5820_i2c_driver = { | |
352 | .driver = { | |
353 | .name = AD5820_NAME, | |
354 | .pm = &ad5820_pm, | |
355 | }, | |
356 | .probe = ad5820_probe, | |
78cea55c | 357 | .remove = ad5820_remove, |
bee3d511 PM |
358 | .id_table = ad5820_id_table, |
359 | }; | |
360 | ||
361 | module_i2c_driver(ad5820_i2c_driver); | |
362 | ||
363 | MODULE_AUTHOR("Tuukka Toivonen"); | |
364 | MODULE_DESCRIPTION("AD5820 camera lens driver"); | |
365 | MODULE_LICENSE("GPL"); |