xref: /openbmc/linux/drivers/media/i2c/dw9719.c (revision fed60fc5)
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 
dw9719_detect(struct dw9719_device * dw9719)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 
dw9719_power_down(struct dw9719_device * dw9719)72 static int dw9719_power_down(struct dw9719_device *dw9719)
73 {
74 	return regulator_disable(dw9719->regulator);
75 }
76 
dw9719_power_up(struct dw9719_device * dw9719)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 
dw9719_t_focus_abs(struct dw9719_device * dw9719,s32 value)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 
dw9719_set_ctrl(struct v4l2_ctrl * ctrl)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 
dw9719_suspend(struct device * dev)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 
dw9719_resume(struct device * dev)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 
dw9719_open(struct v4l2_subdev * sd,struct v4l2_subdev_fh * fh)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 
dw9719_close(struct v4l2_subdev * sd,struct v4l2_subdev_fh * fh)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 
dw9719_init_controls(struct dw9719_device * dw9719)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 
dw9719_probe(struct i2c_client * client)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 
dw9719_remove(struct i2c_client * client)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 <djrscally@gmail.com>");
349 MODULE_DESCRIPTION("DW9719 VCM Driver");
350 MODULE_LICENSE("GPL");
351