brintos

brintos / linux-shallow public Read only

0
0
Text · 8.7 KiB · f602710 Raw
381 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * drivers/media/i2c/ad5820.c4 *5 * AD5820 DAC driver for camera voice coil focus.6 *7 * Copyright (C) 2008 Nokia Corporation8 * Copyright (C) 2007 Texas Instruments9 * Copyright (C) 2016 Pavel Machek <pavel@ucw.cz>10 *11 * Contact: Tuukka Toivonen <tuukkat76@gmail.com>12 *	    Sakari Ailus <sakari.ailus@iki.fi>13 *14 * Based on af_d88.c by Texas Instruments.15 */16 17#include <linux/errno.h>18#include <linux/i2c.h>19#include <linux/kernel.h>20#include <linux/module.h>21#include <linux/regulator/consumer.h>22#include <linux/gpio/consumer.h>23 24#include <media/v4l2-ctrls.h>25#include <media/v4l2-device.h>26#include <media/v4l2-subdev.h>27 28/* Register definitions */29#define AD5820_POWER_DOWN		(1 << 15)30#define AD5820_DAC_SHIFT		431#define AD5820_RAMP_MODE_LINEAR		(0 << 3)32#define AD5820_RAMP_MODE_64_16		(1 << 3)33 34#define CODE_TO_RAMP_US(s)	((s) == 0 ? 0 : (1 << ((s) - 1)) * 50)35#define RAMP_US_TO_CODE(c)	fls(((c) + ((c)>>1)) / 50)36 37#define to_ad5820_device(sd)	container_of(sd, struct ad5820_device, subdev)38 39struct ad5820_device {40	struct v4l2_subdev subdev;41	struct ad5820_platform_data *platform_data;42	struct regulator *vana;43 44	struct v4l2_ctrl_handler ctrls;45	u32 focus_absolute;46	u32 focus_ramp_time;47	u32 focus_ramp_mode;48 49	struct gpio_desc *enable_gpio;50 51	struct mutex power_lock;52	int power_count;53 54	bool standby;55};56 57static int ad5820_write(struct ad5820_device *coil, u16 data)58{59	struct i2c_client *client = v4l2_get_subdevdata(&coil->subdev);60	struct i2c_msg msg;61	__be16 be_data;62	int r;63 64	if (!client->adapter)65		return -ENODEV;66 67	be_data = cpu_to_be16(data);68	msg.addr  = client->addr;69	msg.flags = 0;70	msg.len   = 2;71	msg.buf   = (u8 *)&be_data;72 73	r = i2c_transfer(client->adapter, &msg, 1);74	if (r < 0) {75		dev_err(&client->dev, "write failed, error %d\n", r);76		return r;77	}78 79	return 0;80}81 82/*83 * Calculate status word and write it to the device based on current84 * values of V4L2 controls. It is assumed that the stored V4L2 control85 * values are properly limited and rounded.86 */87static int ad5820_update_hw(struct ad5820_device *coil)88{89	u16 status;90 91	status = RAMP_US_TO_CODE(coil->focus_ramp_time);92	status |= coil->focus_ramp_mode93		? AD5820_RAMP_MODE_64_16 : AD5820_RAMP_MODE_LINEAR;94	status |= coil->focus_absolute << AD5820_DAC_SHIFT;95 96	if (coil->standby)97		status |= AD5820_POWER_DOWN;98 99	return ad5820_write(coil, status);100}101 102/*103 * Power handling104 */105static int ad5820_power_off(struct ad5820_device *coil, bool standby)106{107	int ret = 0, ret2;108 109	/*110	 * Go to standby first as real power off my be denied by the hardware111	 * (single power line control for both coil and sensor).112	 */113	if (standby) {114		coil->standby = true;115		ret = ad5820_update_hw(coil);116	}117 118	gpiod_set_value_cansleep(coil->enable_gpio, 0);119 120	ret2 = regulator_disable(coil->vana);121	if (ret)122		return ret;123	return ret2;124}125 126static int ad5820_power_on(struct ad5820_device *coil, bool restore)127{128	int ret;129 130	ret = regulator_enable(coil->vana);131	if (ret < 0)132		return ret;133 134	gpiod_set_value_cansleep(coil->enable_gpio, 1);135 136	if (restore) {137		/* Restore the hardware settings. */138		coil->standby = false;139		ret = ad5820_update_hw(coil);140		if (ret)141			goto fail;142	}143	return 0;144 145fail:146	gpiod_set_value_cansleep(coil->enable_gpio, 0);147	coil->standby = true;148	regulator_disable(coil->vana);149 150	return ret;151}152 153/*154 * V4L2 controls155 */156static int ad5820_set_ctrl(struct v4l2_ctrl *ctrl)157{158	struct ad5820_device *coil =159		container_of(ctrl->handler, struct ad5820_device, ctrls);160 161	switch (ctrl->id) {162	case V4L2_CID_FOCUS_ABSOLUTE:163		coil->focus_absolute = ctrl->val;164		return ad5820_update_hw(coil);165	}166 167	return 0;168}169 170static const struct v4l2_ctrl_ops ad5820_ctrl_ops = {171	.s_ctrl = ad5820_set_ctrl,172};173 174 175static int ad5820_init_controls(struct ad5820_device *coil)176{177	v4l2_ctrl_handler_init(&coil->ctrls, 1);178 179	/*180	 * V4L2_CID_FOCUS_ABSOLUTE181	 *182	 * Minimum current is 0 mA, maximum is 100 mA. Thus, 1 code is183	 * equivalent to 100/1023 = 0.0978 mA. Nevertheless, we do not use [mA]184	 * for focus position, because it is meaningless for user. Meaningful185	 * would be to use focus distance or even its inverse, but since the186	 * driver doesn't have sufficiently knowledge to do the conversion, we187	 * will just use abstract codes here. In any case, smaller value = focus188	 * position farther from camera. The default zero value means focus at189	 * infinity, and also least current consumption.190	 */191	v4l2_ctrl_new_std(&coil->ctrls, &ad5820_ctrl_ops,192			  V4L2_CID_FOCUS_ABSOLUTE, 0, 1023, 1, 0);193 194	if (coil->ctrls.error)195		return coil->ctrls.error;196 197	coil->focus_absolute = 0;198	coil->focus_ramp_time = 0;199	coil->focus_ramp_mode = 0;200 201	coil->subdev.ctrl_handler = &coil->ctrls;202 203	return 0;204}205 206/*207 * V4L2 subdev operations208 */209static int ad5820_registered(struct v4l2_subdev *subdev)210{211	struct ad5820_device *coil = to_ad5820_device(subdev);212 213	return ad5820_init_controls(coil);214}215 216static int217ad5820_set_power(struct v4l2_subdev *subdev, int on)218{219	struct ad5820_device *coil = to_ad5820_device(subdev);220	int ret = 0;221 222	mutex_lock(&coil->power_lock);223 224	/*225	 * If the power count is modified from 0 to != 0 or from != 0 to 0,226	 * update the power state.227	 */228	if (coil->power_count == !on) {229		ret = on ? ad5820_power_on(coil, true) :230			ad5820_power_off(coil, true);231		if (ret < 0)232			goto done;233	}234 235	/* Update the power count. */236	coil->power_count += on ? 1 : -1;237	WARN_ON(coil->power_count < 0);238 239done:240	mutex_unlock(&coil->power_lock);241	return ret;242}243 244static int ad5820_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)245{246	return ad5820_set_power(sd, 1);247}248 249static int ad5820_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)250{251	return ad5820_set_power(sd, 0);252}253 254static const struct v4l2_subdev_core_ops ad5820_core_ops = {255	.s_power = ad5820_set_power,256};257 258static const struct v4l2_subdev_ops ad5820_ops = {259	.core = &ad5820_core_ops,260};261 262static const struct v4l2_subdev_internal_ops ad5820_internal_ops = {263	.registered = ad5820_registered,264	.open = ad5820_open,265	.close = ad5820_close,266};267 268/*269 * I2C driver270 */271static int __maybe_unused ad5820_suspend(struct device *dev)272{273	struct v4l2_subdev *subdev = dev_get_drvdata(dev);274	struct ad5820_device *coil = to_ad5820_device(subdev);275 276	if (!coil->power_count)277		return 0;278 279	return ad5820_power_off(coil, false);280}281 282static int __maybe_unused ad5820_resume(struct device *dev)283{284	struct v4l2_subdev *subdev = dev_get_drvdata(dev);285	struct ad5820_device *coil = to_ad5820_device(subdev);286 287	if (!coil->power_count)288		return 0;289 290	return ad5820_power_on(coil, true);291}292 293static int ad5820_probe(struct i2c_client *client)294{295	struct ad5820_device *coil;296	int ret;297 298	coil = devm_kzalloc(&client->dev, sizeof(*coil), GFP_KERNEL);299	if (!coil)300		return -ENOMEM;301 302	coil->vana = devm_regulator_get(&client->dev, "VANA");303	if (IS_ERR(coil->vana))304		return dev_err_probe(&client->dev, PTR_ERR(coil->vana),305				     "could not get regulator for vana\n");306 307	coil->enable_gpio = devm_gpiod_get_optional(&client->dev, "enable",308						    GPIOD_OUT_LOW);309	if (IS_ERR(coil->enable_gpio))310		return dev_err_probe(&client->dev, PTR_ERR(coil->enable_gpio),311				     "could not get enable gpio\n");312 313	mutex_init(&coil->power_lock);314 315	v4l2_i2c_subdev_init(&coil->subdev, client, &ad5820_ops);316	coil->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;317	coil->subdev.internal_ops = &ad5820_internal_ops;318	coil->subdev.entity.function = MEDIA_ENT_F_LENS;319	strscpy(coil->subdev.name, "ad5820 focus", sizeof(coil->subdev.name));320 321	ret = media_entity_pads_init(&coil->subdev.entity, 0, NULL);322	if (ret < 0)323		goto clean_mutex;324 325	ret = v4l2_async_register_subdev(&coil->subdev);326	if (ret < 0)327		goto clean_entity;328 329	return ret;330 331clean_entity:332	media_entity_cleanup(&coil->subdev.entity);333clean_mutex:334	mutex_destroy(&coil->power_lock);335	return ret;336}337 338static void ad5820_remove(struct i2c_client *client)339{340	struct v4l2_subdev *subdev = i2c_get_clientdata(client);341	struct ad5820_device *coil = to_ad5820_device(subdev);342 343	v4l2_async_unregister_subdev(&coil->subdev);344	v4l2_ctrl_handler_free(&coil->ctrls);345	media_entity_cleanup(&coil->subdev.entity);346	mutex_destroy(&coil->power_lock);347}348 349static const struct i2c_device_id ad5820_id_table[] = {350	{ "ad5820" },351	{ "ad5821" },352	{ }353};354MODULE_DEVICE_TABLE(i2c, ad5820_id_table);355 356static const struct of_device_id ad5820_of_table[] = {357	{ .compatible = "adi,ad5820" },358	{ .compatible = "adi,ad5821" },359	{ }360};361MODULE_DEVICE_TABLE(of, ad5820_of_table);362 363static SIMPLE_DEV_PM_OPS(ad5820_pm, ad5820_suspend, ad5820_resume);364 365static struct i2c_driver ad5820_i2c_driver = {366	.driver		= {367		.name	= "ad5820",368		.pm	= &ad5820_pm,369		.of_match_table = ad5820_of_table,370	},371	.probe		= ad5820_probe,372	.remove		= ad5820_remove,373	.id_table	= ad5820_id_table,374};375 376module_i2c_driver(ad5820_i2c_driver);377 378MODULE_AUTHOR("Tuukka Toivonen");379MODULE_DESCRIPTION("AD5820 camera lens driver");380MODULE_LICENSE("GPL");381