Merge tag 'trace-printf-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[drm/drm-misc.git] / drivers / media / i2c / dw9719.c
blobc626ed845928cc842794cad7da72080d1ade32a9
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2012 Intel Corporation
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 */
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>
15 #include <media/v4l2-cci.h>
16 #include <media/v4l2-common.h>
17 #include <media/v4l2-ctrls.h>
18 #include <media/v4l2-subdev.h>
20 #define DW9719_MAX_FOCUS_POS 1023
21 #define DW9719_CTRL_STEPS 16
22 #define DW9719_CTRL_DELAY_US 1000
24 #define DW9719_INFO CCI_REG8(0)
25 #define DW9719_ID 0xF1
27 #define DW9719_CONTROL CCI_REG8(2)
28 #define DW9719_ENABLE_RINGING 0x02
30 #define DW9719_VCM_CURRENT CCI_REG16(3)
32 #define DW9719_MODE CCI_REG8(6)
33 #define DW9719_MODE_SAC_SHIFT 4
34 #define DW9719_MODE_SAC3 4
36 #define DW9719_VCM_FREQ CCI_REG8(7)
37 #define DW9719_DEFAULT_VCM_FREQ 0x60
39 #define to_dw9719_device(x) container_of(x, struct dw9719_device, sd)
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;
49 struct dw9719_v4l2_ctrls {
50 struct v4l2_ctrl_handler handler;
51 struct v4l2_ctrl *focus;
52 } ctrls;
55 static int dw9719_detect(struct dw9719_device *dw9719)
57 int ret;
58 u64 val;
60 ret = cci_read(dw9719->regmap, DW9719_INFO, &val, NULL);
61 if (ret < 0)
62 return ret;
64 if (val != DW9719_ID) {
65 dev_err(dw9719->dev, "Failed to detect correct id\n");
66 return -ENXIO;
69 return 0;
72 static int dw9719_power_down(struct dw9719_device *dw9719)
74 return regulator_disable(dw9719->regulator);
77 static int dw9719_power_up(struct dw9719_device *dw9719)
79 int ret;
81 ret = regulator_enable(dw9719->regulator);
82 if (ret)
83 return ret;
85 /* Jiggle SCL pin to wake up device */
86 cci_write(dw9719->regmap, DW9719_CONTROL, 1, &ret);
88 /* Need 100us to transit from SHUTDOWN to STANDBY */
89 fsleep(100);
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);
96 if (ret)
97 dw9719_power_down(dw9719);
99 return ret;
102 static int dw9719_t_focus_abs(struct dw9719_device *dw9719, s32 value)
104 return cci_write(dw9719->regmap, DW9719_VCM_CURRENT, value, NULL);
107 static int dw9719_set_ctrl(struct v4l2_ctrl *ctrl)
109 struct dw9719_device *dw9719 = container_of(ctrl->handler,
110 struct dw9719_device,
111 ctrls.handler);
112 int ret;
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;
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;
126 pm_runtime_put(dw9719->dev);
128 return ret;
131 static const struct v4l2_ctrl_ops dw9719_ctrl_ops = {
132 .s_ctrl = dw9719_set_ctrl,
135 static int dw9719_suspend(struct device *dev)
137 struct v4l2_subdev *sd = dev_get_drvdata(dev);
138 struct dw9719_device *dw9719 = to_dw9719_device(sd);
139 int ret;
140 int val;
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;
148 usleep_range(DW9719_CTRL_DELAY_US, DW9719_CTRL_DELAY_US + 10);
151 return dw9719_power_down(dw9719);
154 static int dw9719_resume(struct device *dev)
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;
162 ret = dw9719_power_up(dw9719);
163 if (ret)
164 return ret;
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;
172 usleep_range(DW9719_CTRL_DELAY_US, DW9719_CTRL_DELAY_US + 10);
175 return 0;
177 err_power_down:
178 dw9719_power_down(dw9719);
179 return ret;
182 static int dw9719_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
184 return pm_runtime_resume_and_get(sd->dev);
187 static int dw9719_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
189 pm_runtime_put(sd->dev);
191 return 0;
194 static const struct v4l2_subdev_internal_ops dw9719_internal_ops = {
195 .open = dw9719_open,
196 .close = dw9719_close,
199 static int dw9719_init_controls(struct dw9719_device *dw9719)
201 const struct v4l2_ctrl_ops *ops = &dw9719_ctrl_ops;
202 int ret;
204 v4l2_ctrl_handler_init(&dw9719->ctrls.handler, 1);
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);
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;
216 dw9719->sd.ctrl_handler = &dw9719->ctrls.handler;
217 return 0;
219 err_free_handler:
220 v4l2_ctrl_handler_free(&dw9719->ctrls.handler);
221 return ret;
224 static const struct v4l2_subdev_ops dw9719_ops = { };
226 static int dw9719_probe(struct i2c_client *client)
228 struct dw9719_device *dw9719;
229 int ret;
231 dw9719 = devm_kzalloc(&client->dev, sizeof(*dw9719), GFP_KERNEL);
232 if (!dw9719)
233 return -ENOMEM;
235 dw9719->regmap = devm_cci_regmap_init_i2c(client, 8);
236 if (IS_ERR(dw9719->regmap))
237 return PTR_ERR(dw9719->regmap);
239 dw9719->dev = &client->dev;
240 dw9719->sac_mode = DW9719_MODE_SAC3;
241 dw9719->vcm_freq = DW9719_DEFAULT_VCM_FREQ;
243 /* Optional indication of SAC mode select */
244 device_property_read_u32(&client->dev, "dongwoon,sac-mode",
245 &dw9719->sac_mode);
247 /* Optional indication of VCM frequency */
248 device_property_read_u32(&client->dev, "dongwoon,vcm-freq",
249 &dw9719->vcm_freq);
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");
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;
260 ret = dw9719_init_controls(dw9719);
261 if (ret)
262 return ret;
264 ret = media_entity_pads_init(&dw9719->sd.entity, 0, NULL);
265 if (ret < 0)
266 goto err_free_ctrl_handler;
268 dw9719->sd.entity.function = MEDIA_ENT_F_LENS;
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.
277 ret = dw9719_power_up(dw9719);
278 if (ret)
279 goto err_cleanup_media;
281 ret = dw9719_detect(dw9719);
282 if (ret)
283 goto err_powerdown;
285 pm_runtime_set_active(&client->dev);
286 pm_runtime_get_noresume(&client->dev);
287 pm_runtime_enable(&client->dev);
289 ret = v4l2_async_register_subdev(&dw9719->sd);
290 if (ret < 0)
291 goto err_pm_runtime;
293 pm_runtime_set_autosuspend_delay(&client->dev, 1000);
294 pm_runtime_use_autosuspend(&client->dev);
295 pm_runtime_put_autosuspend(&client->dev);
297 return ret;
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);
309 return ret;
312 static void dw9719_remove(struct i2c_client *client)
314 struct v4l2_subdev *sd = i2c_get_clientdata(client);
315 struct dw9719_device *dw9719 =
316 container_of(sd, struct dw9719_device, sd);
318 v4l2_async_unregister_subdev(sd);
319 v4l2_ctrl_handler_free(&dw9719->ctrls.handler);
320 media_entity_cleanup(&dw9719->sd.entity);
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);
328 static const struct i2c_device_id dw9719_id_table[] = {
329 { "dw9719" },
332 MODULE_DEVICE_TABLE(i2c, dw9719_id_table);
334 static DEFINE_RUNTIME_DEV_PM_OPS(dw9719_pm_ops, dw9719_suspend, dw9719_resume,
335 NULL);
337 static struct i2c_driver dw9719_i2c_driver = {
338 .driver = {
339 .name = "dw9719",
340 .pm = pm_sleep_ptr(&dw9719_pm_ops),
342 .probe = dw9719_probe,
343 .remove = dw9719_remove,
344 .id_table = dw9719_id_table,
346 module_i2c_driver(dw9719_i2c_driver);
348 MODULE_AUTHOR("Daniel Scally <djrscally@gmail.com>");
349 MODULE_DESCRIPTION("DW9719 VCM Driver");
350 MODULE_LICENSE("GPL");