1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2012 Intel Corporation
5 * Based on linux/modules/camera/drivers/media/i2c/imx/dw9719.c in this repo:
6 * https://github.com/ZenfoneArea/android_kernel_asus_zenfone5
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
;
44 struct regmap
*regmap
;
45 struct regulator
*regulator
;
49 struct dw9719_v4l2_ctrls
{
50 struct v4l2_ctrl_handler handler
;
51 struct v4l2_ctrl
*focus
;
55 static int dw9719_detect(struct dw9719_device
*dw9719
)
60 ret
= cci_read(dw9719
->regmap
, DW9719_INFO
, &val
, NULL
);
64 if (val
!= DW9719_ID
) {
65 dev_err(dw9719
->dev
, "Failed to detect correct id\n");
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
)
81 ret
= regulator_enable(dw9719
->regulator
);
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 */
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
);
97 dw9719_power_down(dw9719
);
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
,
114 /* Only apply changes to the controls if the device is powered up */
115 if (!pm_runtime_get_if_in_use(dw9719
->dev
))
119 case V4L2_CID_FOCUS_ABSOLUTE
:
120 ret
= dw9719_t_focus_abs(dw9719
, ctrl
->val
);
126 pm_runtime_put(dw9719
->dev
);
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
);
142 for (val
= dw9719
->ctrls
.focus
->val
; val
>= 0;
143 val
-= DW9719_CTRL_STEPS
) {
144 ret
= dw9719_t_focus_abs(dw9719
, val
);
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
;
162 ret
= dw9719_power_up(dw9719
);
166 for (val
= current_focus
% DW9719_CTRL_STEPS
; val
< current_focus
;
167 val
+= DW9719_CTRL_STEPS
) {
168 ret
= dw9719_t_focus_abs(dw9719
, val
);
172 usleep_range(DW9719_CTRL_DELAY_US
, DW9719_CTRL_DELAY_US
+ 10);
178 dw9719_power_down(dw9719
);
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
);
194 static const struct v4l2_subdev_internal_ops dw9719_internal_ops
= {
196 .close
= dw9719_close
,
199 static int dw9719_init_controls(struct dw9719_device
*dw9719
)
201 const struct v4l2_ctrl_ops
*ops
= &dw9719_ctrl_ops
;
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
;
220 v4l2_ctrl_handler_free(&dw9719
->ctrls
.handler
);
224 static const struct v4l2_subdev_ops dw9719_ops
= { };
226 static int dw9719_probe(struct i2c_client
*client
)
228 struct dw9719_device
*dw9719
;
231 dw9719
= devm_kzalloc(&client
->dev
, sizeof(*dw9719
), GFP_KERNEL
);
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",
247 /* Optional indication of VCM frequency */
248 device_property_read_u32(&client
->dev
, "dongwoon,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
);
264 ret
= media_entity_pads_init(&dw9719
->sd
.entity
, 0, NULL
);
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
277 ret
= dw9719_power_up(dw9719
);
279 goto err_cleanup_media
;
281 ret
= dw9719_detect(dw9719
);
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
);
293 pm_runtime_set_autosuspend_delay(&client
->dev
, 1000);
294 pm_runtime_use_autosuspend(&client
->dev
);
295 pm_runtime_put_autosuspend(&client
->dev
);
300 pm_runtime_disable(&client
->dev
);
301 pm_runtime_put_noidle(&client
->dev
);
303 dw9719_power_down(dw9719
);
305 media_entity_cleanup(&dw9719
->sd
.entity
);
306 err_free_ctrl_handler
:
307 v4l2_ctrl_handler_free(&dw9719
->ctrls
.handler
);
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
[] = {
332 MODULE_DEVICE_TABLE(i2c
, dw9719_id_table
);
334 static DEFINE_RUNTIME_DEV_PM_OPS(dw9719_pm_ops
, dw9719_suspend
, dw9719_resume
,
337 static struct i2c_driver dw9719_i2c_driver
= {
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");