18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * drivers/media/i2c/ad5820.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * AD5820 DAC driver for camera voice coil focus.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation
88c2ecf20Sopenharmony_ci * Copyright (C) 2007 Texas Instruments
98c2ecf20Sopenharmony_ci * Copyright (C) 2016 Pavel Machek <pavel@ucw.cz>
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Contact: Tuukka Toivonen <tuukkat76@gmail.com>
128c2ecf20Sopenharmony_ci *	    Sakari Ailus <sakari.ailus@iki.fi>
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * Based on af_d88.c by Texas Instruments.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/errno.h>
188c2ecf20Sopenharmony_ci#include <linux/i2c.h>
198c2ecf20Sopenharmony_ci#include <linux/kernel.h>
208c2ecf20Sopenharmony_ci#include <linux/module.h>
218c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
228c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h>
258c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
268c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* Register definitions */
298c2ecf20Sopenharmony_ci#define AD5820_POWER_DOWN		(1 << 15)
308c2ecf20Sopenharmony_ci#define AD5820_DAC_SHIFT		4
318c2ecf20Sopenharmony_ci#define AD5820_RAMP_MODE_LINEAR		(0 << 3)
328c2ecf20Sopenharmony_ci#define AD5820_RAMP_MODE_64_16		(1 << 3)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define CODE_TO_RAMP_US(s)	((s) == 0 ? 0 : (1 << ((s) - 1)) * 50)
358c2ecf20Sopenharmony_ci#define RAMP_US_TO_CODE(c)	fls(((c) + ((c)>>1)) / 50)
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define to_ad5820_device(sd)	container_of(sd, struct ad5820_device, subdev)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistruct ad5820_device {
408c2ecf20Sopenharmony_ci	struct v4l2_subdev subdev;
418c2ecf20Sopenharmony_ci	struct ad5820_platform_data *platform_data;
428c2ecf20Sopenharmony_ci	struct regulator *vana;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler ctrls;
458c2ecf20Sopenharmony_ci	u32 focus_absolute;
468c2ecf20Sopenharmony_ci	u32 focus_ramp_time;
478c2ecf20Sopenharmony_ci	u32 focus_ramp_mode;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	struct gpio_desc *enable_gpio;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	struct mutex power_lock;
528c2ecf20Sopenharmony_ci	int power_count;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	bool standby;
558c2ecf20Sopenharmony_ci};
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic int ad5820_write(struct ad5820_device *coil, u16 data)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(&coil->subdev);
608c2ecf20Sopenharmony_ci	struct i2c_msg msg;
618c2ecf20Sopenharmony_ci	__be16 be_data;
628c2ecf20Sopenharmony_ci	int r;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	if (!client->adapter)
658c2ecf20Sopenharmony_ci		return -ENODEV;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	be_data = cpu_to_be16(data);
688c2ecf20Sopenharmony_ci	msg.addr  = client->addr;
698c2ecf20Sopenharmony_ci	msg.flags = 0;
708c2ecf20Sopenharmony_ci	msg.len   = 2;
718c2ecf20Sopenharmony_ci	msg.buf   = (u8 *)&be_data;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	r = i2c_transfer(client->adapter, &msg, 1);
748c2ecf20Sopenharmony_ci	if (r < 0) {
758c2ecf20Sopenharmony_ci		dev_err(&client->dev, "write failed, error %d\n", r);
768c2ecf20Sopenharmony_ci		return r;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	return 0;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/*
838c2ecf20Sopenharmony_ci * Calculate status word and write it to the device based on current
848c2ecf20Sopenharmony_ci * values of V4L2 controls. It is assumed that the stored V4L2 control
858c2ecf20Sopenharmony_ci * values are properly limited and rounded.
868c2ecf20Sopenharmony_ci */
878c2ecf20Sopenharmony_cistatic int ad5820_update_hw(struct ad5820_device *coil)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	u16 status;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	status = RAMP_US_TO_CODE(coil->focus_ramp_time);
928c2ecf20Sopenharmony_ci	status |= coil->focus_ramp_mode
938c2ecf20Sopenharmony_ci		? AD5820_RAMP_MODE_64_16 : AD5820_RAMP_MODE_LINEAR;
948c2ecf20Sopenharmony_ci	status |= coil->focus_absolute << AD5820_DAC_SHIFT;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (coil->standby)
978c2ecf20Sopenharmony_ci		status |= AD5820_POWER_DOWN;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	return ad5820_write(coil, status);
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/*
1038c2ecf20Sopenharmony_ci * Power handling
1048c2ecf20Sopenharmony_ci */
1058c2ecf20Sopenharmony_cistatic int ad5820_power_off(struct ad5820_device *coil, bool standby)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	int ret = 0, ret2;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	/*
1108c2ecf20Sopenharmony_ci	 * Go to standby first as real power off my be denied by the hardware
1118c2ecf20Sopenharmony_ci	 * (single power line control for both coil and sensor).
1128c2ecf20Sopenharmony_ci	 */
1138c2ecf20Sopenharmony_ci	if (standby) {
1148c2ecf20Sopenharmony_ci		coil->standby = true;
1158c2ecf20Sopenharmony_ci		ret = ad5820_update_hw(coil);
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(coil->enable_gpio, 0);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	ret2 = regulator_disable(coil->vana);
1218c2ecf20Sopenharmony_ci	if (ret)
1228c2ecf20Sopenharmony_ci		return ret;
1238c2ecf20Sopenharmony_ci	return ret2;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic int ad5820_power_on(struct ad5820_device *coil, bool restore)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	int ret;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	ret = regulator_enable(coil->vana);
1318c2ecf20Sopenharmony_ci	if (ret < 0)
1328c2ecf20Sopenharmony_ci		return ret;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(coil->enable_gpio, 1);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (restore) {
1378c2ecf20Sopenharmony_ci		/* Restore the hardware settings. */
1388c2ecf20Sopenharmony_ci		coil->standby = false;
1398c2ecf20Sopenharmony_ci		ret = ad5820_update_hw(coil);
1408c2ecf20Sopenharmony_ci		if (ret)
1418c2ecf20Sopenharmony_ci			goto fail;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci	return 0;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cifail:
1468c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(coil->enable_gpio, 0);
1478c2ecf20Sopenharmony_ci	coil->standby = true;
1488c2ecf20Sopenharmony_ci	regulator_disable(coil->vana);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	return ret;
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci/*
1548c2ecf20Sopenharmony_ci * V4L2 controls
1558c2ecf20Sopenharmony_ci */
1568c2ecf20Sopenharmony_cistatic int ad5820_set_ctrl(struct v4l2_ctrl *ctrl)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	struct ad5820_device *coil =
1598c2ecf20Sopenharmony_ci		container_of(ctrl->handler, struct ad5820_device, ctrls);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	switch (ctrl->id) {
1628c2ecf20Sopenharmony_ci	case V4L2_CID_FOCUS_ABSOLUTE:
1638c2ecf20Sopenharmony_ci		coil->focus_absolute = ctrl->val;
1648c2ecf20Sopenharmony_ci		return ad5820_update_hw(coil);
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	return 0;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops ad5820_ctrl_ops = {
1718c2ecf20Sopenharmony_ci	.s_ctrl = ad5820_set_ctrl,
1728c2ecf20Sopenharmony_ci};
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic int ad5820_init_controls(struct ad5820_device *coil)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(&coil->ctrls, 1);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/*
1808c2ecf20Sopenharmony_ci	 * V4L2_CID_FOCUS_ABSOLUTE
1818c2ecf20Sopenharmony_ci	 *
1828c2ecf20Sopenharmony_ci	 * Minimum current is 0 mA, maximum is 100 mA. Thus, 1 code is
1838c2ecf20Sopenharmony_ci	 * equivalent to 100/1023 = 0.0978 mA. Nevertheless, we do not use [mA]
1848c2ecf20Sopenharmony_ci	 * for focus position, because it is meaningless for user. Meaningful
1858c2ecf20Sopenharmony_ci	 * would be to use focus distance or even its inverse, but since the
1868c2ecf20Sopenharmony_ci	 * driver doesn't have sufficiently knowledge to do the conversion, we
1878c2ecf20Sopenharmony_ci	 * will just use abstract codes here. In any case, smaller value = focus
1888c2ecf20Sopenharmony_ci	 * position farther from camera. The default zero value means focus at
1898c2ecf20Sopenharmony_ci	 * infinity, and also least current consumption.
1908c2ecf20Sopenharmony_ci	 */
1918c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&coil->ctrls, &ad5820_ctrl_ops,
1928c2ecf20Sopenharmony_ci			  V4L2_CID_FOCUS_ABSOLUTE, 0, 1023, 1, 0);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	if (coil->ctrls.error)
1958c2ecf20Sopenharmony_ci		return coil->ctrls.error;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	coil->focus_absolute = 0;
1988c2ecf20Sopenharmony_ci	coil->focus_ramp_time = 0;
1998c2ecf20Sopenharmony_ci	coil->focus_ramp_mode = 0;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	coil->subdev.ctrl_handler = &coil->ctrls;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	return 0;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci/*
2078c2ecf20Sopenharmony_ci * V4L2 subdev operations
2088c2ecf20Sopenharmony_ci */
2098c2ecf20Sopenharmony_cistatic int ad5820_registered(struct v4l2_subdev *subdev)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct ad5820_device *coil = to_ad5820_device(subdev);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	return ad5820_init_controls(coil);
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic int
2178c2ecf20Sopenharmony_ciad5820_set_power(struct v4l2_subdev *subdev, int on)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct ad5820_device *coil = to_ad5820_device(subdev);
2208c2ecf20Sopenharmony_ci	int ret = 0;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	mutex_lock(&coil->power_lock);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/*
2258c2ecf20Sopenharmony_ci	 * If the power count is modified from 0 to != 0 or from != 0 to 0,
2268c2ecf20Sopenharmony_ci	 * update the power state.
2278c2ecf20Sopenharmony_ci	 */
2288c2ecf20Sopenharmony_ci	if (coil->power_count == !on) {
2298c2ecf20Sopenharmony_ci		ret = on ? ad5820_power_on(coil, true) :
2308c2ecf20Sopenharmony_ci			ad5820_power_off(coil, true);
2318c2ecf20Sopenharmony_ci		if (ret < 0)
2328c2ecf20Sopenharmony_ci			goto done;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	/* Update the power count. */
2368c2ecf20Sopenharmony_ci	coil->power_count += on ? 1 : -1;
2378c2ecf20Sopenharmony_ci	WARN_ON(coil->power_count < 0);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cidone:
2408c2ecf20Sopenharmony_ci	mutex_unlock(&coil->power_lock);
2418c2ecf20Sopenharmony_ci	return ret;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic int ad5820_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	return ad5820_set_power(sd, 1);
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic int ad5820_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	return ad5820_set_power(sd, 0);
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops ad5820_core_ops = {
2558c2ecf20Sopenharmony_ci	.s_power = ad5820_set_power,
2568c2ecf20Sopenharmony_ci};
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops ad5820_ops = {
2598c2ecf20Sopenharmony_ci	.core = &ad5820_core_ops,
2608c2ecf20Sopenharmony_ci};
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_internal_ops ad5820_internal_ops = {
2638c2ecf20Sopenharmony_ci	.registered = ad5820_registered,
2648c2ecf20Sopenharmony_ci	.open = ad5820_open,
2658c2ecf20Sopenharmony_ci	.close = ad5820_close,
2668c2ecf20Sopenharmony_ci};
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci/*
2698c2ecf20Sopenharmony_ci * I2C driver
2708c2ecf20Sopenharmony_ci */
2718c2ecf20Sopenharmony_cistatic int __maybe_unused ad5820_suspend(struct device *dev)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct i2c_client *client = container_of(dev, struct i2c_client, dev);
2748c2ecf20Sopenharmony_ci	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
2758c2ecf20Sopenharmony_ci	struct ad5820_device *coil = to_ad5820_device(subdev);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (!coil->power_count)
2788c2ecf20Sopenharmony_ci		return 0;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	return ad5820_power_off(coil, false);
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic int __maybe_unused ad5820_resume(struct device *dev)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	struct i2c_client *client = container_of(dev, struct i2c_client, dev);
2868c2ecf20Sopenharmony_ci	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
2878c2ecf20Sopenharmony_ci	struct ad5820_device *coil = to_ad5820_device(subdev);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (!coil->power_count)
2908c2ecf20Sopenharmony_ci		return 0;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	return ad5820_power_on(coil, true);
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic int ad5820_probe(struct i2c_client *client,
2968c2ecf20Sopenharmony_ci			const struct i2c_device_id *devid)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct ad5820_device *coil;
2998c2ecf20Sopenharmony_ci	int ret;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	coil = devm_kzalloc(&client->dev, sizeof(*coil), GFP_KERNEL);
3028c2ecf20Sopenharmony_ci	if (!coil)
3038c2ecf20Sopenharmony_ci		return -ENOMEM;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	coil->vana = devm_regulator_get(&client->dev, "VANA");
3068c2ecf20Sopenharmony_ci	if (IS_ERR(coil->vana)) {
3078c2ecf20Sopenharmony_ci		ret = PTR_ERR(coil->vana);
3088c2ecf20Sopenharmony_ci		if (ret != -EPROBE_DEFER)
3098c2ecf20Sopenharmony_ci			dev_err(&client->dev, "could not get regulator for vana\n");
3108c2ecf20Sopenharmony_ci		return ret;
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	coil->enable_gpio = devm_gpiod_get_optional(&client->dev, "enable",
3148c2ecf20Sopenharmony_ci						    GPIOD_OUT_LOW);
3158c2ecf20Sopenharmony_ci	if (IS_ERR(coil->enable_gpio)) {
3168c2ecf20Sopenharmony_ci		ret = PTR_ERR(coil->enable_gpio);
3178c2ecf20Sopenharmony_ci		if (ret != -EPROBE_DEFER)
3188c2ecf20Sopenharmony_ci			dev_err(&client->dev, "could not get enable gpio\n");
3198c2ecf20Sopenharmony_ci		return ret;
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	mutex_init(&coil->power_lock);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	v4l2_i2c_subdev_init(&coil->subdev, client, &ad5820_ops);
3258c2ecf20Sopenharmony_ci	coil->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
3268c2ecf20Sopenharmony_ci	coil->subdev.internal_ops = &ad5820_internal_ops;
3278c2ecf20Sopenharmony_ci	coil->subdev.entity.function = MEDIA_ENT_F_LENS;
3288c2ecf20Sopenharmony_ci	strscpy(coil->subdev.name, "ad5820 focus", sizeof(coil->subdev.name));
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	ret = media_entity_pads_init(&coil->subdev.entity, 0, NULL);
3318c2ecf20Sopenharmony_ci	if (ret < 0)
3328c2ecf20Sopenharmony_ci		goto clean_mutex;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	ret = v4l2_async_register_subdev(&coil->subdev);
3358c2ecf20Sopenharmony_ci	if (ret < 0)
3368c2ecf20Sopenharmony_ci		goto clean_entity;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	return ret;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ciclean_entity:
3418c2ecf20Sopenharmony_ci	media_entity_cleanup(&coil->subdev.entity);
3428c2ecf20Sopenharmony_ciclean_mutex:
3438c2ecf20Sopenharmony_ci	mutex_destroy(&coil->power_lock);
3448c2ecf20Sopenharmony_ci	return ret;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic int ad5820_remove(struct i2c_client *client)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
3508c2ecf20Sopenharmony_ci	struct ad5820_device *coil = to_ad5820_device(subdev);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	v4l2_async_unregister_subdev(&coil->subdev);
3538c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&coil->ctrls);
3548c2ecf20Sopenharmony_ci	media_entity_cleanup(&coil->subdev.entity);
3558c2ecf20Sopenharmony_ci	mutex_destroy(&coil->power_lock);
3568c2ecf20Sopenharmony_ci	return 0;
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic const struct i2c_device_id ad5820_id_table[] = {
3608c2ecf20Sopenharmony_ci	{ "ad5820", 0 },
3618c2ecf20Sopenharmony_ci	{ "ad5821", 0 },
3628c2ecf20Sopenharmony_ci	{ }
3638c2ecf20Sopenharmony_ci};
3648c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ad5820_id_table);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic const struct of_device_id ad5820_of_table[] = {
3678c2ecf20Sopenharmony_ci	{ .compatible = "adi,ad5820" },
3688c2ecf20Sopenharmony_ci	{ .compatible = "adi,ad5821" },
3698c2ecf20Sopenharmony_ci	{ }
3708c2ecf20Sopenharmony_ci};
3718c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ad5820_of_table);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(ad5820_pm, ad5820_suspend, ad5820_resume);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic struct i2c_driver ad5820_i2c_driver = {
3768c2ecf20Sopenharmony_ci	.driver		= {
3778c2ecf20Sopenharmony_ci		.name	= "ad5820",
3788c2ecf20Sopenharmony_ci		.pm	= &ad5820_pm,
3798c2ecf20Sopenharmony_ci		.of_match_table = ad5820_of_table,
3808c2ecf20Sopenharmony_ci	},
3818c2ecf20Sopenharmony_ci	.probe		= ad5820_probe,
3828c2ecf20Sopenharmony_ci	.remove		= ad5820_remove,
3838c2ecf20Sopenharmony_ci	.id_table	= ad5820_id_table,
3848c2ecf20Sopenharmony_ci};
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cimodule_i2c_driver(ad5820_i2c_driver);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tuukka Toivonen");
3898c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AD5820 camera lens driver");
3908c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
391