]> Git Repo - J-linux.git/blob - drivers/media/i2c/dw9719.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / media / i2c / dw9719.c
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2012 Intel Corporation
3
4 /*
5  * Based on linux/modules/camera/drivers/media/i2c/imx/dw9719.c in this repo:
6  * https://github.com/ZenfoneArea/android_kernel_asus_zenfone5
7  */
8
9 #include <linux/delay.h>
10 #include <linux/i2c.h>
11 #include <linux/pm_runtime.h>
12 #include <linux/regulator/consumer.h>
13 #include <linux/types.h>
14
15 #include <media/v4l2-cci.h>
16 #include <media/v4l2-common.h>
17 #include <media/v4l2-ctrls.h>
18 #include <media/v4l2-subdev.h>
19
20 #define DW9719_MAX_FOCUS_POS    1023
21 #define DW9719_CTRL_STEPS       16
22 #define DW9719_CTRL_DELAY_US    1000
23
24 #define DW9719_INFO                     CCI_REG8(0)
25 #define DW9719_ID                       0xF1
26
27 #define DW9719_CONTROL                  CCI_REG8(2)
28 #define DW9719_ENABLE_RINGING           0x02
29
30 #define DW9719_VCM_CURRENT              CCI_REG16(3)
31
32 #define DW9719_MODE                     CCI_REG8(6)
33 #define DW9719_MODE_SAC_SHIFT           4
34 #define DW9719_MODE_SAC3                4
35
36 #define DW9719_VCM_FREQ                 CCI_REG8(7)
37 #define DW9719_DEFAULT_VCM_FREQ         0x60
38
39 #define to_dw9719_device(x) container_of(x, struct dw9719_device, sd)
40
41 struct dw9719_device {
42         struct v4l2_subdev sd;
43         struct device *dev;
44         struct regmap *regmap;
45         struct regulator *regulator;
46         u32 sac_mode;
47         u32 vcm_freq;
48
49         struct dw9719_v4l2_ctrls {
50                 struct v4l2_ctrl_handler handler;
51                 struct v4l2_ctrl *focus;
52         } ctrls;
53 };
54
55 static int dw9719_detect(struct dw9719_device *dw9719)
56 {
57         int ret;
58         u64 val;
59
60         ret = cci_read(dw9719->regmap, DW9719_INFO, &val, NULL);
61         if (ret < 0)
62                 return ret;
63
64         if (val != DW9719_ID) {
65                 dev_err(dw9719->dev, "Failed to detect correct id\n");
66                 return -ENXIO;
67         }
68
69         return 0;
70 }
71
72 static int dw9719_power_down(struct dw9719_device *dw9719)
73 {
74         return regulator_disable(dw9719->regulator);
75 }
76
77 static int dw9719_power_up(struct dw9719_device *dw9719)
78 {
79         int ret;
80
81         ret = regulator_enable(dw9719->regulator);
82         if (ret)
83                 return ret;
84
85         /* Jiggle SCL pin to wake up device */
86         cci_write(dw9719->regmap, DW9719_CONTROL, 1, &ret);
87
88         /* Need 100us to transit from SHUTDOWN to STANDBY */
89         fsleep(100);
90
91         cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_ENABLE_RINGING, &ret);
92         cci_write(dw9719->regmap, DW9719_MODE,
93                   dw9719->sac_mode << DW9719_MODE_SAC_SHIFT, &ret);
94         cci_write(dw9719->regmap, DW9719_VCM_FREQ, dw9719->vcm_freq, &ret);
95
96         if (ret)
97                 dw9719_power_down(dw9719);
98
99         return ret;
100 }
101
102 static int dw9719_t_focus_abs(struct dw9719_device *dw9719, s32 value)
103 {
104         return cci_write(dw9719->regmap, DW9719_VCM_CURRENT, value, NULL);
105 }
106
107 static int dw9719_set_ctrl(struct v4l2_ctrl *ctrl)
108 {
109         struct dw9719_device *dw9719 = container_of(ctrl->handler,
110                                                     struct dw9719_device,
111                                                     ctrls.handler);
112         int ret;
113
114         /* Only apply changes to the controls if the device is powered up */
115         if (!pm_runtime_get_if_in_use(dw9719->dev))
116                 return 0;
117
118         switch (ctrl->id) {
119         case V4L2_CID_FOCUS_ABSOLUTE:
120                 ret = dw9719_t_focus_abs(dw9719, ctrl->val);
121                 break;
122         default:
123                 ret = -EINVAL;
124         }
125
126         pm_runtime_put(dw9719->dev);
127
128         return ret;
129 }
130
131 static const struct v4l2_ctrl_ops dw9719_ctrl_ops = {
132         .s_ctrl = dw9719_set_ctrl,
133 };
134
135 static int dw9719_suspend(struct device *dev)
136 {
137         struct v4l2_subdev *sd = dev_get_drvdata(dev);
138         struct dw9719_device *dw9719 = to_dw9719_device(sd);
139         int ret;
140         int val;
141
142         for (val = dw9719->ctrls.focus->val; val >= 0;
143              val -= DW9719_CTRL_STEPS) {
144                 ret = dw9719_t_focus_abs(dw9719, val);
145                 if (ret)
146                         return ret;
147
148                 usleep_range(DW9719_CTRL_DELAY_US, DW9719_CTRL_DELAY_US + 10);
149         }
150
151         return dw9719_power_down(dw9719);
152 }
153
154 static int dw9719_resume(struct device *dev)
155 {
156         struct v4l2_subdev *sd = dev_get_drvdata(dev);
157         struct dw9719_device *dw9719 = to_dw9719_device(sd);
158         int current_focus = dw9719->ctrls.focus->val;
159         int ret;
160         int val;
161
162         ret = dw9719_power_up(dw9719);
163         if (ret)
164                 return ret;
165
166         for (val = current_focus % DW9719_CTRL_STEPS; val < current_focus;
167              val += DW9719_CTRL_STEPS) {
168                 ret = dw9719_t_focus_abs(dw9719, val);
169                 if (ret)
170                         goto err_power_down;
171
172                 usleep_range(DW9719_CTRL_DELAY_US, DW9719_CTRL_DELAY_US + 10);
173         }
174
175         return 0;
176
177 err_power_down:
178         dw9719_power_down(dw9719);
179         return ret;
180 }
181
182 static int dw9719_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
183 {
184         return pm_runtime_resume_and_get(sd->dev);
185 }
186
187 static int dw9719_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
188 {
189         pm_runtime_put(sd->dev);
190
191         return 0;
192 }
193
194 static const struct v4l2_subdev_internal_ops dw9719_internal_ops = {
195         .open = dw9719_open,
196         .close = dw9719_close,
197 };
198
199 static int dw9719_init_controls(struct dw9719_device *dw9719)
200 {
201         const struct v4l2_ctrl_ops *ops = &dw9719_ctrl_ops;
202         int ret;
203
204         v4l2_ctrl_handler_init(&dw9719->ctrls.handler, 1);
205
206         dw9719->ctrls.focus = v4l2_ctrl_new_std(&dw9719->ctrls.handler, ops,
207                                                 V4L2_CID_FOCUS_ABSOLUTE, 0,
208                                                 DW9719_MAX_FOCUS_POS, 1, 0);
209
210         if (dw9719->ctrls.handler.error) {
211                 dev_err(dw9719->dev, "Error initialising v4l2 ctrls\n");
212                 ret = dw9719->ctrls.handler.error;
213                 goto err_free_handler;
214         }
215
216         dw9719->sd.ctrl_handler = &dw9719->ctrls.handler;
217         return 0;
218
219 err_free_handler:
220         v4l2_ctrl_handler_free(&dw9719->ctrls.handler);
221         return ret;
222 }
223
224 static const struct v4l2_subdev_ops dw9719_ops = { };
225
226 static int dw9719_probe(struct i2c_client *client)
227 {
228         struct dw9719_device *dw9719;
229         int ret;
230
231         dw9719 = devm_kzalloc(&client->dev, sizeof(*dw9719), GFP_KERNEL);
232         if (!dw9719)
233                 return -ENOMEM;
234
235         dw9719->regmap = devm_cci_regmap_init_i2c(client, 8);
236         if (IS_ERR(dw9719->regmap))
237                 return PTR_ERR(dw9719->regmap);
238
239         dw9719->dev = &client->dev;
240         dw9719->sac_mode = DW9719_MODE_SAC3;
241         dw9719->vcm_freq = DW9719_DEFAULT_VCM_FREQ;
242
243         /* Optional indication of SAC mode select */
244         device_property_read_u32(&client->dev, "dongwoon,sac-mode",
245                                  &dw9719->sac_mode);
246
247         /* Optional indication of VCM frequency */
248         device_property_read_u32(&client->dev, "dongwoon,vcm-freq",
249                                  &dw9719->vcm_freq);
250
251         dw9719->regulator = devm_regulator_get(&client->dev, "vdd");
252         if (IS_ERR(dw9719->regulator))
253                 return dev_err_probe(&client->dev, PTR_ERR(dw9719->regulator),
254                                      "getting regulator\n");
255
256         v4l2_i2c_subdev_init(&dw9719->sd, client, &dw9719_ops);
257         dw9719->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
258         dw9719->sd.internal_ops = &dw9719_internal_ops;
259
260         ret = dw9719_init_controls(dw9719);
261         if (ret)
262                 return ret;
263
264         ret = media_entity_pads_init(&dw9719->sd.entity, 0, NULL);
265         if (ret < 0)
266                 goto err_free_ctrl_handler;
267
268         dw9719->sd.entity.function = MEDIA_ENT_F_LENS;
269
270         /*
271          * We need the driver to work in the event that pm runtime is disable in
272          * the kernel, so power up and verify the chip now. In the event that
273          * runtime pm is disabled this will leave the chip on, so that the lens
274          * will work.
275          */
276
277         ret = dw9719_power_up(dw9719);
278         if (ret)
279                 goto err_cleanup_media;
280
281         ret = dw9719_detect(dw9719);
282         if (ret)
283                 goto err_powerdown;
284
285         pm_runtime_set_active(&client->dev);
286         pm_runtime_get_noresume(&client->dev);
287         pm_runtime_enable(&client->dev);
288
289         ret = v4l2_async_register_subdev(&dw9719->sd);
290         if (ret < 0)
291                 goto err_pm_runtime;
292
293         pm_runtime_set_autosuspend_delay(&client->dev, 1000);
294         pm_runtime_use_autosuspend(&client->dev);
295         pm_runtime_put_autosuspend(&client->dev);
296
297         return ret;
298
299 err_pm_runtime:
300         pm_runtime_disable(&client->dev);
301         pm_runtime_put_noidle(&client->dev);
302 err_powerdown:
303         dw9719_power_down(dw9719);
304 err_cleanup_media:
305         media_entity_cleanup(&dw9719->sd.entity);
306 err_free_ctrl_handler:
307         v4l2_ctrl_handler_free(&dw9719->ctrls.handler);
308
309         return ret;
310 }
311
312 static void dw9719_remove(struct i2c_client *client)
313 {
314         struct v4l2_subdev *sd = i2c_get_clientdata(client);
315         struct dw9719_device *dw9719 =
316                 container_of(sd, struct dw9719_device, sd);
317
318         v4l2_async_unregister_subdev(sd);
319         v4l2_ctrl_handler_free(&dw9719->ctrls.handler);
320         media_entity_cleanup(&dw9719->sd.entity);
321
322         pm_runtime_disable(&client->dev);
323         if (!pm_runtime_status_suspended(&client->dev))
324                 dw9719_power_down(dw9719);
325         pm_runtime_set_suspended(&client->dev);
326 }
327
328 static const struct i2c_device_id dw9719_id_table[] = {
329         { "dw9719" },
330         { }
331 };
332 MODULE_DEVICE_TABLE(i2c, dw9719_id_table);
333
334 static DEFINE_RUNTIME_DEV_PM_OPS(dw9719_pm_ops, dw9719_suspend, dw9719_resume,
335                                  NULL);
336
337 static struct i2c_driver dw9719_i2c_driver = {
338         .driver = {
339                 .name = "dw9719",
340                 .pm = pm_sleep_ptr(&dw9719_pm_ops),
341         },
342         .probe = dw9719_probe,
343         .remove = dw9719_remove,
344         .id_table = dw9719_id_table,
345 };
346 module_i2c_driver(dw9719_i2c_driver);
347
348 MODULE_AUTHOR("Daniel Scally <[email protected]>");
349 MODULE_DESCRIPTION("DW9719 VCM Driver");
350 MODULE_LICENSE("GPL");
This page took 0.047414 seconds and 4 git commands to generate.