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
41struct 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
55static 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
72static int dw9719_power_down(struct dw9719_device *dw9719)
73{
74	return regulator_disable(dw9719->regulator);
75}
76
77static 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
102static 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
107static 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
131static const struct v4l2_ctrl_ops dw9719_ctrl_ops = {
132	.s_ctrl = dw9719_set_ctrl,
133};
134
135static 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
154static 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
177err_power_down:
178	dw9719_power_down(dw9719);
179	return ret;
180}
181
182static int dw9719_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
183{
184	return pm_runtime_resume_and_get(sd->dev);
185}
186
187static 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
194static const struct v4l2_subdev_internal_ops dw9719_internal_ops = {
195	.open = dw9719_open,
196	.close = dw9719_close,
197};
198
199static 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
219err_free_handler:
220	v4l2_ctrl_handler_free(&dw9719->ctrls.handler);
221	return ret;
222}
223
224static const struct v4l2_subdev_ops dw9719_ops = { };
225
226static 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
299err_pm_runtime:
300	pm_runtime_disable(&client->dev);
301	pm_runtime_put_noidle(&client->dev);
302err_powerdown:
303	dw9719_power_down(dw9719);
304err_cleanup_media:
305	media_entity_cleanup(&dw9719->sd.entity);
306err_free_ctrl_handler:
307	v4l2_ctrl_handler_free(&dw9719->ctrls.handler);
308
309	return ret;
310}
311
312static 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
328static const struct i2c_device_id dw9719_id_table[] = {
329	{ "dw9719" },
330	{ }
331};
332MODULE_DEVICE_TABLE(i2c, dw9719_id_table);
333
334static DEFINE_RUNTIME_DEV_PM_OPS(dw9719_pm_ops, dw9719_suspend, dw9719_resume,
335				 NULL);
336
337static 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};
346module_i2c_driver(dw9719_i2c_driver);
347
348MODULE_AUTHOR("Daniel Scally <djrscally@gmail.com>");
349MODULE_DESCRIPTION("DW9719 VCM Driver");
350MODULE_LICENSE("GPL");
351