File Index
Files and directories
Sign in
FILE

dw9719.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/i2c/dw9719.c 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 from:
  6 * https://github.com/ZenfoneArea/android_kernel_asus_zenfone5 and
  7 * latte-l-oss/drivers/external_drivers/camera/drivers/media/i2c/micam/dw9761.c
  8 * from: https://github.com/MiCode/Xiaomi_Kernel_OpenSource/
  9 */
 10
 11#include <linux/delay.h>
 12#include <linux/i2c.h>
 13#include <linux/pm_runtime.h>
 14#include <linux/regulator/consumer.h>
 15#include <linux/types.h>
 16
 17#include <media/v4l2-cci.h>
 18#include <media/v4l2-common.h>
 19#include <media/v4l2-ctrls.h>
 20#include <media/v4l2-subdev.h>
 21
 22#define DW9719_MAX_FOCUS_POS	1023
 23#define DW9719_CTRL_STEPS	16
 24#define DW9719_CTRL_DELAY_US	1000
 25
 26#define DW9718S_PD			CCI_REG8(0)
 27
 28#define DW9718S_CONTROL			CCI_REG8(1)
 29#define DW9718S_CONTROL_SW_LINEAR	BIT(0)
 30#define DW9718S_CONTROL_SAC_SHIFT	1
 31#define DW9718S_CONTROL_SAC_MASK	0x7
 32#define DW9718S_CONTROL_OCP_DISABLE	BIT(4)
 33#define DW9718S_CONTROL_UVLO_DISABLE	BIT(5)
 34#define DW9718S_DEFAULT_SAC		4
 35
 36#define DW9718S_VCM_CURRENT		CCI_REG16(2)
 37
 38#define DW9718S_SW			CCI_REG8(4)
 39#define DW9718S_SW_VCM_FREQ_MASK	0xF
 40#define DW9718S_DEFAULT_VCM_FREQ	0
 41
 42#define DW9718S_SACT			CCI_REG8(5)
 43#define DW9718S_SACT_PERIOD_8_8MS	0x19
 44
 45#define DW9719_INFO			CCI_REG8(0)
 46#define DW9719_ID			0xF1
 47#define DW9761_ID			0xF4
 48
 49#define DW9719_CONTROL			CCI_REG8(2)
 50#define DW9719_STANDBY			0x00
 51#define DW9719_SHUTDOWN			0x01
 52#define DW9719_ENABLE_RINGING		0x02
 53
 54#define DW9719_VCM_CURRENT		CCI_REG16(3)
 55
 56#define DW9719_STATUS			CCI_REG16(5)
 57#define DW9719_STATUS_BUSY		BIT(0)
 58
 59#define DW9719_MODE			CCI_REG8(6)
 60#define DW9719_MODE_SAC_SHIFT		4
 61#define DW9719_DEFAULT_SAC		4
 62#define DW9761_DEFAULT_SAC		6
 63
 64#define DW9719_VCM_FREQ			CCI_REG8(7)
 65#define DW9719_DEFAULT_VCM_FREQ		0x60
 66#define DW9761_DEFAULT_VCM_FREQ		0x3E
 67
 68#define DW9761_VCM_PRELOAD		CCI_REG8(8)
 69#define DW9761_DEFAULT_VCM_PRELOAD	0x73
 70
 71#define DW9800K_DEFAULT_SAC		1
 72#define DW9800K_MODE_SAC_SHIFT		6
 73#define DW9800K_DEFAULT_VCM_FREQ		0x10
 74
 75#define to_dw9719_device(x) container_of(x, struct dw9719_device, sd)
 76
 77enum dw9719_model {
 78	DW9718S,
 79	DW9719,
 80	DW9761,
 81	DW9800K,
 82};
 83
 84struct dw9719_device {
 85	struct v4l2_subdev sd;
 86	struct device *dev;
 87	struct regmap *regmap;
 88	struct regulator *regulator;
 89	enum dw9719_model model;
 90	u32 mode_low_bits;
 91	u32 sac_mode;
 92	u32 vcm_freq;
 93
 94	struct dw9719_v4l2_ctrls {
 95		struct v4l2_ctrl_handler handler;
 96		struct v4l2_ctrl *focus;
 97	} ctrls;
 98};
 99
100static int dw9719_power_down(struct dw9719_device *dw9719)
101{
102	u32 reg_pwr = dw9719->model == DW9718S ? DW9718S_PD : DW9719_CONTROL;
103
104	/*
105	 * Worth engaging the internal SHUTDOWN mode especially due to the
106	 * regulator being potentially shared with other devices.
107	 */
108	if (cci_write(dw9719->regmap, reg_pwr, DW9719_SHUTDOWN, NULL))
109		dev_err(dw9719->dev, "Error writing to power register\n");
110	return regulator_disable(dw9719->regulator);
111}
112
113static int dw9719_power_up(struct dw9719_device *dw9719, bool detect)
114{
115	u32 reg_pwr = dw9719->model == DW9718S ? DW9718S_PD : DW9719_CONTROL;
116	u64 val;
117	int ret;
118	int err;
119
120	ret = regulator_enable(dw9719->regulator);
121	if (ret)
122		return ret;
123
124	/*
125	 * Need 100us to transition from SHUTDOWN to STANDBY.
126	 * Jiggle the SCL pin to wake up the device (even when the regulator is
127	 * shared) and wait double the time to be sure, as 100us is not enough
128	 * at least on the DW9718S as found on the motorola-nora smartphone,
129	 * then retry the write.
130	 */
131	cci_write(dw9719->regmap, reg_pwr, DW9719_STANDBY, NULL);
132	/* the jiggle is expected to fail, don't even log that as error */
133	fsleep(200);
134	cci_write(dw9719->regmap, reg_pwr, DW9719_STANDBY, &ret);
135
136	if (detect) {
137		/* These models do not have an INFO register */
138		switch (dw9719->model) {
139		case DW9718S:
140			dw9719->sac_mode = DW9718S_DEFAULT_SAC;
141			dw9719->vcm_freq = DW9718S_DEFAULT_VCM_FREQ;
142			goto props;
143		case DW9800K:
144			dw9719->sac_mode = DW9800K_DEFAULT_SAC;
145			dw9719->vcm_freq = DW9800K_DEFAULT_VCM_FREQ;
146			goto props;
147		default:
148			break;
149		}
150
151		ret = cci_read(dw9719->regmap, DW9719_INFO, &val, NULL);
152		if (ret < 0)
153			return ret;
154
155		switch (val) {
156		case DW9719_ID:
157			dw9719->model = DW9719;
158			dw9719->mode_low_bits = 0x00;
159			dw9719->sac_mode = DW9719_DEFAULT_SAC;
160			dw9719->vcm_freq = DW9719_DEFAULT_VCM_FREQ;
161			break;
162		case DW9761_ID:
163			dw9719->model = DW9761;
164			dw9719->mode_low_bits = 0x01;
165			dw9719->sac_mode = DW9761_DEFAULT_SAC;
166			dw9719->vcm_freq = DW9761_DEFAULT_VCM_FREQ;
167			break;
168		default:
169			dev_err(dw9719->dev,
170				"Error unknown device id 0x%02llx\n", val);
171			return -ENXIO;
172		}
173
174props:
175		/* Optional indication of SAC mode select */
176		device_property_read_u32(dw9719->dev, "dongwoon,sac-mode",
177					 &dw9719->sac_mode);
178
179		/* Optional indication of VCM frequency */
180		err = device_property_read_u32(dw9719->dev, "dongwoon,vcm-freq",
181					       &dw9719->vcm_freq);
182		if (err == 0)
183			dev_warn(dw9719->dev, "dongwoon,vcm-freq property is deprecated, please use dongwoon,vcm-prescale\n");
184
185		/* Optional indication of VCM prescale */
186		device_property_read_u32(dw9719->dev, "dongwoon,vcm-prescale",
187					 &dw9719->vcm_freq);
188	}
189
190	switch (dw9719->model) {
191	case DW9800K:
192		cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_ENABLE_RINGING, &ret);
193		cci_write(dw9719->regmap, DW9719_MODE,
194			  dw9719->sac_mode << DW9800K_MODE_SAC_SHIFT, &ret);
195		cci_write(dw9719->regmap, DW9719_VCM_FREQ, dw9719->vcm_freq, &ret);
196		break;
197	case DW9718S:
198		/* Datasheet says [OCP/UVLO] should be disabled below 2.5V */
199		dw9719->sac_mode &= DW9718S_CONTROL_SAC_MASK;
200		cci_write(dw9719->regmap, DW9718S_CONTROL,
201			  DW9718S_CONTROL_SW_LINEAR |
202			  (dw9719->sac_mode << DW9718S_CONTROL_SAC_SHIFT) |
203			  DW9718S_CONTROL_OCP_DISABLE |
204			  DW9718S_CONTROL_UVLO_DISABLE, &ret);
205		cci_write(dw9719->regmap, DW9718S_SACT,
206			  DW9718S_SACT_PERIOD_8_8MS, &ret);
207		cci_write(dw9719->regmap, DW9718S_SW,
208			  dw9719->vcm_freq & DW9718S_SW_VCM_FREQ_MASK, &ret);
209		break;
210	case DW9761:
211		cci_write(dw9719->regmap, DW9761_VCM_PRELOAD,
212			  DW9761_DEFAULT_VCM_PRELOAD, &ret);
213		fallthrough;
214	case DW9719:
215		cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_ENABLE_RINGING, &ret);
216		cci_write(dw9719->regmap, DW9719_MODE, dw9719->mode_low_bits |
217				  (dw9719->sac_mode << DW9719_MODE_SAC_SHIFT), &ret);
218		cci_write(dw9719->regmap, DW9719_VCM_FREQ, dw9719->vcm_freq, &ret);
219	}
220
221	if (ret)
222		dw9719_power_down(dw9719);
223
224	return ret;
225}
226
227static int dw9719_t_focus_abs(struct dw9719_device *dw9719, s32 value)
228{
229	u32 reg = dw9719->model == DW9718S ? DW9718S_VCM_CURRENT
230					   : DW9719_VCM_CURRENT;
231	return cci_write(dw9719->regmap, reg, value, NULL);
232}
233
234static int dw9719_set_ctrl(struct v4l2_ctrl *ctrl)
235{
236	struct dw9719_device *dw9719 = container_of(ctrl->handler,
237						    struct dw9719_device,
238						    ctrls.handler);
239	int ret;
240
241	/* Only apply changes to the controls if the device is powered up */
242	if (!pm_runtime_get_if_in_use(dw9719->dev))
243		return 0;
244
245	switch (ctrl->id) {
246	case V4L2_CID_FOCUS_ABSOLUTE:
247		ret = dw9719_t_focus_abs(dw9719, ctrl->val);
248		break;
249	default:
250		ret = -EINVAL;
251	}
252
253	pm_runtime_put(dw9719->dev);
254
255	return ret;
256}
257
258static const struct v4l2_ctrl_ops dw9719_ctrl_ops = {
259	.s_ctrl = dw9719_set_ctrl,
260};
261
262static int dw9719_suspend(struct device *dev)
263{
264	struct v4l2_subdev *sd = dev_get_drvdata(dev);
265	struct dw9719_device *dw9719 = to_dw9719_device(sd);
266	int ret;
267	int val;
268
269	for (val = dw9719->ctrls.focus->val; val >= 0;
270	     val -= DW9719_CTRL_STEPS) {
271		ret = dw9719_t_focus_abs(dw9719, val);
272		if (ret)
273			return ret;
274
275		usleep_range(DW9719_CTRL_DELAY_US, DW9719_CTRL_DELAY_US + 10);
276	}
277
278	return dw9719_power_down(dw9719);
279}
280
281static int dw9719_resume(struct device *dev)
282{
283	struct v4l2_subdev *sd = dev_get_drvdata(dev);
284	struct dw9719_device *dw9719 = to_dw9719_device(sd);
285	int current_focus = dw9719->ctrls.focus->val;
286	int ret;
287	int val;
288
289	ret = dw9719_power_up(dw9719, false);
290	if (ret)
291		return ret;
292
293	for (val = current_focus % DW9719_CTRL_STEPS; val < current_focus;
294	     val += DW9719_CTRL_STEPS) {
295		ret = dw9719_t_focus_abs(dw9719, val);
296		if (ret)
297			goto err_power_down;
298
299		usleep_range(DW9719_CTRL_DELAY_US, DW9719_CTRL_DELAY_US + 10);
300	}
301
302	return 0;
303
304err_power_down:
305	dw9719_power_down(dw9719);
306	return ret;
307}
308
309static int dw9719_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
310{
311	return pm_runtime_resume_and_get(sd->dev);
312}
313
314static int dw9719_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
315{
316	pm_runtime_put_autosuspend(sd->dev);
317
318	return 0;
319}
320
321static const struct v4l2_subdev_internal_ops dw9719_internal_ops = {
322	.open = dw9719_open,
323	.close = dw9719_close,
324};
325
326static int dw9719_init_controls(struct dw9719_device *dw9719)
327{
328	const struct v4l2_ctrl_ops *ops = &dw9719_ctrl_ops;
329	int ret;
330
331	v4l2_ctrl_handler_init(&dw9719->ctrls.handler, 1);
332
333	dw9719->ctrls.focus = v4l2_ctrl_new_std(&dw9719->ctrls.handler, ops,
334						V4L2_CID_FOCUS_ABSOLUTE, 0,
335						DW9719_MAX_FOCUS_POS, 1, 0);
336
337	if (dw9719->ctrls.handler.error) {
338		dev_err(dw9719->dev, "Error initialising v4l2 ctrls\n");
339		ret = dw9719->ctrls.handler.error;
340		goto err_free_handler;
341	}
342
343	dw9719->sd.ctrl_handler = &dw9719->ctrls.handler;
344	return 0;
345
346err_free_handler:
347	v4l2_ctrl_handler_free(&dw9719->ctrls.handler);
348	return ret;
349}
350
351static const struct v4l2_subdev_ops dw9719_ops = { };
352
353static int dw9719_probe(struct i2c_client *client)
354{
355	struct dw9719_device *dw9719;
356	int ret;
357
358	dw9719 = devm_kzalloc(&client->dev, sizeof(*dw9719), GFP_KERNEL);
359	if (!dw9719)
360		return -ENOMEM;
361
362	dw9719->model = (enum dw9719_model)(uintptr_t)i2c_get_match_data(client);
363
364	dw9719->regmap = devm_cci_regmap_init_i2c(client, 8);
365	if (IS_ERR(dw9719->regmap))
366		return PTR_ERR(dw9719->regmap);
367
368	dw9719->dev = &client->dev;
369
370	dw9719->regulator = devm_regulator_get(&client->dev, "vdd");
371	if (IS_ERR(dw9719->regulator))
372		return dev_err_probe(&client->dev, PTR_ERR(dw9719->regulator),
373				     "getting regulator\n");
374
375	v4l2_i2c_subdev_init(&dw9719->sd, client, &dw9719_ops);
376	dw9719->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
377	dw9719->sd.internal_ops = &dw9719_internal_ops;
378
379	ret = dw9719_init_controls(dw9719);
380	if (ret)
381		return ret;
382
383	ret = media_entity_pads_init(&dw9719->sd.entity, 0, NULL);
384	if (ret < 0)
385		goto err_free_ctrl_handler;
386
387	dw9719->sd.entity.function = MEDIA_ENT_F_LENS;
388
389	/*
390	 * We need the driver to work in the event that pm runtime is disable in
391	 * the kernel, so power up and verify the chip now. In the event that
392	 * runtime pm is disabled this will leave the chip on, so that the lens
393	 * will work.
394	 */
395
396	ret = dw9719_power_up(dw9719, true);
397	if (ret)
398		goto err_cleanup_media;
399
400	pm_runtime_set_active(&client->dev);
401	pm_runtime_get_noresume(&client->dev);
402	pm_runtime_enable(&client->dev);
403
404	ret = v4l2_async_register_subdev(&dw9719->sd);
405	if (ret < 0)
406		goto err_pm_runtime;
407
408	pm_runtime_set_autosuspend_delay(&client->dev, 1000);
409	pm_runtime_use_autosuspend(&client->dev);
410	pm_runtime_put_autosuspend(&client->dev);
411
412	return ret;
413
414err_pm_runtime:
415	pm_runtime_disable(&client->dev);
416	pm_runtime_put_noidle(&client->dev);
417	dw9719_power_down(dw9719);
418err_cleanup_media:
419	media_entity_cleanup(&dw9719->sd.entity);
420err_free_ctrl_handler:
421	v4l2_ctrl_handler_free(&dw9719->ctrls.handler);
422
423	return ret;
424}
425
426static void dw9719_remove(struct i2c_client *client)
427{
428	struct v4l2_subdev *sd = i2c_get_clientdata(client);
429	struct dw9719_device *dw9719 =
430		container_of(sd, struct dw9719_device, sd);
431
432	v4l2_async_unregister_subdev(sd);
433	v4l2_ctrl_handler_free(&dw9719->ctrls.handler);
434	media_entity_cleanup(&dw9719->sd.entity);
435
436	pm_runtime_disable(&client->dev);
437	if (!pm_runtime_status_suspended(&client->dev))
438		dw9719_power_down(dw9719);
439	pm_runtime_set_suspended(&client->dev);
440}
441
442static const struct of_device_id dw9719_of_table[] = {
443	{ .compatible = "dongwoon,dw9718s", .data = (const void *)DW9718S },
444	{ .compatible = "dongwoon,dw9719", .data = (const void *)DW9719 },
445	{ .compatible = "dongwoon,dw9761", .data = (const void *)DW9761 },
446	{ .compatible = "dongwoon,dw9800k", .data = (const void *)DW9800K },
447	{ }
448};
449MODULE_DEVICE_TABLE(of, dw9719_of_table);
450
451static DEFINE_RUNTIME_DEV_PM_OPS(dw9719_pm_ops, dw9719_suspend, dw9719_resume,
452				 NULL);
453
454static struct i2c_driver dw9719_i2c_driver = {
455	.driver = {
456		.name = "dw9719",
457		.pm = pm_sleep_ptr(&dw9719_pm_ops),
458		.of_match_table = dw9719_of_table,
459	},
460	.probe = dw9719_probe,
461	.remove = dw9719_remove,
462};
463module_i2c_driver(dw9719_i2c_driver);
464
465MODULE_AUTHOR("Daniel Scally <djrscally@gmail.com>");
466MODULE_DESCRIPTION("DW9719 VCM Driver");
467MODULE_LICENSE("GPL");
~/ Read-only file preview.