linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
To: Pavel Machek <pavel@ucw.cz>
Cc: pali.rohar@gmail.com, sre@kernel.org,
	kernel list <linux-kernel@vger.kernel.org>,
	linux-arm-kernel <linux-arm-kernel@lists.infradead.org>,
	linux-omap@vger.kernel.org, tony@atomide.com, khilman@kernel.org,
	aaro.koskinen@iki.fi, patrikbachan@gmail.com, serge@hallyn.com,
	linux-media@vger.kernel.org, mchehab@osg.samsung.com,
	sakari.ailus@iki.fi
Subject: Re: [PATCHv2] support for AD5820 camera auto-focus coil
Date: Sat, 21 May 2016 09:25:21 +0300	[thread overview]
Message-ID: <573FFF51.1000004@gmail.com> (raw)
In-Reply-To: <20160521054336.GA27123@amd>

Hi,

On 21.05.2016 08:43, Pavel Machek wrote:
> This adds support for AD5820 autofocus coil, found for example in
> Nokia N900 smartphone.
>
> Signed-off-by: Pavel Machek <pavel@ucw.cz>
>
> ---
> v2: simple cleanups, fix error paths, simplify probe
>
> I think it is ready. Please apply :-).
>
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 993dc50..77313a1 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -279,6 +279,13 @@ config VIDEO_ML86V7667
>   	  To compile this driver as a module, choose M here: the
>   	  module will be called ml86v7667.
>
> +config VIDEO_AD5820
> +	tristate "AD5820 lens voice coil support"
> +	depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
> +	---help---
> +	  This is a driver for the AD5820 camera lens voice coil.
> +	  It is used for example in Nokia N900 (RX-51).
> +
>   config VIDEO_SAA7110
>   	tristate "Philips SAA7110 video decoder"
>   	depends on VIDEO_V4L2 && I2C
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index 94f2c99..34434ae 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -19,6 +20,7 @@ obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o
>   obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o
>   obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o
>   obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o
> +obj-$(CONFIG_VIDEO_AD5820)  += ad5820.o
>   obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o
>   obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
>   obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o
> diff --git a/drivers/media/i2c/ad5820.c b/drivers/media/i2c/ad5820.c
> new file mode 100644
> index 0000000..7725829
> --- /dev/null
> +++ b/drivers/media/i2c/ad5820.c
> @@ -0,0 +1,416 @@
> +/*
> + * drivers/media/i2c/ad5820.c
> + *
> + * AD5820 DAC driver for camera voice coil focus.
> + *
> + * Copyright (C) 2008 Nokia Corporation
> + * Copyright (C) 2007 Texas Instruments
> + * Copyright (C) 2016 Pavel Machek <pavel@ucw.cz>
> + *
> + * Contact: Tuukka Toivonen
> + *          Sakari Ailus
> + *
> + * Based on af_d88.c by Texas Instruments.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +

Remove FSF address from the above.

> +#include <linux/module.h>
> +#include <linux/errno.h>
> +#include <linux/i2c.h>
> +#include <linux/slab.h>
> +#include <linux/sched.h>
> +#include <linux/delay.h>
> +#include <linux/bitops.h>
> +#include <linux/kernel.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <media/ad5820.h>

drivers/media/i2c/ad5820.c:40:26: fatal error: media/ad5820.h: No such 
file or directory

> +#include <media/v4l2-device.h>
> +
> +#define CODE_TO_RAMP_US(s)	((s) == 0 ? 0 : (1 << ((s) - 1)) * 50)
> +#define RAMP_US_TO_CODE(c)	fls(((c) + ((c)>>1)) / 50)
> +
> +/**
> + * @brief I2C write using i2c_transfer().
> + * @param coil - the driver data structure
> + * @param data - register value to be written
> + * @returns nonnegative on success, negative if failed
> + */
> +static int ad5820_write(struct ad5820_device *coil, u16 data)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&coil->subdev);
> +	struct i2c_msg msg;
> +	int r;
> +
> +	if (!client->adapter)
> +		return -ENODEV;
> +
> +	data = cpu_to_be16(data);
> +	msg.addr  = client->addr;
> +	msg.flags = 0;
> +	msg.len   = 2;
> +	msg.buf   = (u8 *)&data;
> +
> +	r = i2c_transfer(client->adapter, &msg, 1);
> +	if (r < 0) {
> +		dev_err(&client->dev, "write failed, error %d\n", r);
> +		return r;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Calculate status word and write it to the device based on current
> + * values of V4L2 controls. It is assumed that the stored V4L2 control
> + * values are properly limited and rounded.
> + */
> +static int ad5820_update_hw(struct ad5820_device *coil)
> +{
> +	u16 status;
> +
> +	status = RAMP_US_TO_CODE(coil->focus_ramp_time);
> +	status |= coil->focus_ramp_mode
> +		? AD5820_RAMP_MODE_64_16 : AD5820_RAMP_MODE_LINEAR;
> +	status |= coil->focus_absolute << AD5820_DAC_SHIFT;
> +
> +	if (coil->standby)
> +		status |= AD5820_POWER_DOWN;
> +
> +	return ad5820_write(coil, status);
> +}
> +
> +/*
> + * Power handling
> + */
> +static int ad5820_power_off(struct ad5820_device *coil, int standby)
> +{
> +	int ret = 0;
> +
> +	/*
> +	 * Go to standby first as real power off my be denied by the hardware
> +	 * (single power line control for both coil and sensor).
> +	 */
> +	if (standby) {
> +		coil->standby = 1;
> +		ret = ad5820_update_hw(coil);
> +	}
> +
> +	ret |= regulator_disable(coil->vana);
> +
> +	return ret;
> +}
> +
> +static int ad5820_power_on(struct ad5820_device *coil, int restore)
> +{
> +	int ret;
> +
> +	printk("ad5820_power_on: 1\n");

Some remnants from testing?

> +	ret = regulator_enable(coil->vana);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (restore) {
> +		/* Restore the hardware settings. */
> +		coil->standby = 0;
> +		ret = ad5820_update_hw(coil);
> +		if (ret)
> +			goto fail;
> +	}
> +	return 0;
> +
> +fail:
> +	coil->standby = 1;
> +	regulator_disable(coil->vana);
> +
> +	return ret;
> +}
> +
> +/*
> + * V4L2 controls
> + */
> +static int ad5820_set_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct ad5820_device *coil =
> +		container_of(ctrl->handler, struct ad5820_device, ctrls);
> +	u32 code;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_FOCUS_ABSOLUTE:
> +		coil->focus_absolute = ctrl->val;
> +		return ad5820_update_hw(coil);
> +
> +	case V4L2_CID_FOCUS_AD5820_RAMP_TIME:
> +		code = RAMP_US_TO_CODE(ctrl->val);
> +		ctrl->val = CODE_TO_RAMP_US(code);
> +		coil->focus_ramp_time = ctrl->val;
> +		break;
> +
> +	case V4L2_CID_FOCUS_AD5820_RAMP_MODE:
> +		coil->focus_ramp_mode = ctrl->val;
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_ctrl_ops ad5820_ctrl_ops = {
> +	.s_ctrl = ad5820_set_ctrl,
> +};
> +
> +static const char *ad5820_focus_menu[] = {

static const char * const ?

> +	"Linear ramp",
> +	"64/16 ramp",
> +};
> +
> +static const struct v4l2_ctrl_config ad5820_ctrls[] = {
> +	{
> +		.ops		= &ad5820_ctrl_ops,
> +		.id		= V4L2_CID_FOCUS_AD5820_RAMP_TIME,
> +		.type		= V4L2_CTRL_TYPE_INTEGER,
> +		.name		= "Focus ramping time [us]",
> +		.min		= 0,
> +		.max		= 3200,
> +		.step		= 50,
> +		.def		= 0,
> +		.flags		= 0,
> +	},
> +	{
> +		.ops		= &ad5820_ctrl_ops,
> +		.id		= V4L2_CID_FOCUS_AD5820_RAMP_MODE,
> +		.type		= V4L2_CTRL_TYPE_MENU,
> +		.name		= "Focus ramping mode",
> +		.min		= 0,
> +		.max		= ARRAY_SIZE(ad5820_focus_menu),
> +		.step		= 0,
> +		.def		= 0,
> +		.flags		= 0,
> +		.qmenu		= ad5820_focus_menu,
> +	},
> +};
> +
> +
> +static int ad5820_init_controls(struct ad5820_device *coil)
> +{
> +	unsigned int i;
> +
> +	v4l2_ctrl_handler_init(&coil->ctrls, ARRAY_SIZE(ad5820_ctrls) + 1);
> +
> +	/*
> +	 * V4L2_CID_FOCUS_ABSOLUTE
> +	 *
> +	 * Minimum current is 0 mA, maximum is 100 mA. Thus, 1 code is
> +	 * equivalent to 100/1023 = 0.0978 mA. Nevertheless, we do not use [mA]
> +	 * for focus position, because it is meaningless for user. Meaningful
> +	 * would be to use focus distance or even its inverse, but since the
> +	 * driver doesn't have sufficiently knowledge to do the conversion, we
> +	 * will just use abstract codes here. In any case, smaller value = focus
> +	 * position farther from camera. The default zero value means focus at
> +	 * infinity, and also least current consumption.
> +	 */
> +	v4l2_ctrl_new_std(&coil->ctrls, &ad5820_ctrl_ops,
> +			  V4L2_CID_FOCUS_ABSOLUTE, 0, 1023, 1, 0);
> +
> +	/* V4L2_CID_TEST_PATTERN and V4L2_CID_MODE_* */
> +	for (i = 0; i < ARRAY_SIZE(ad5820_ctrls); ++i)
> +		v4l2_ctrl_new_custom(&coil->ctrls, &ad5820_ctrls[i], NULL);
> +
> +	if (coil->ctrls.error)
> +		return coil->ctrls.error;
> +
> +	coil->focus_absolute = 0;
> +	coil->focus_ramp_time = 0;
> +	coil->focus_ramp_mode = 0;
> +
> +	coil->subdev.ctrl_handler = &coil->ctrls;
> +	return 0;
> +}

please add a new line before return.

> +
> +/*
> + * V4L2 subdev operations
> + */
> +static int ad5820_registered(struct v4l2_subdev *subdev)
> +{
> +	struct ad5820_device *coil = to_ad5820_device(subdev);
> +	struct i2c_client *client = v4l2_get_subdevdata(subdev);
> +
> +	coil->vana = regulator_get(&client->dev, "VANA");
> +	if (IS_ERR(coil->vana)) {
> +		dev_err(&client->dev, "could not get regulator for vana\n");
> +		return -ENODEV;
> +	}
> +
> +	return ad5820_init_controls(coil);
> +}
> +
> +static int
> +ad5820_set_power(struct v4l2_subdev *subdev, int on)
> +{
> +	struct ad5820_device *coil = to_ad5820_device(subdev);
> +	int ret = 0;
> +
> +	mutex_lock(&coil->power_lock);
> +
> +	/*
> +	 * If the power count is modified from 0 to != 0 or from != 0 to 0,
> +	 * update the power state.
> +	 */
> +	if (coil->power_count == !on) {
> +		ret = on ? ad5820_power_on(coil, 1) : ad5820_power_off(coil, 1);
> +		if (ret < 0)
> +			goto done;
> +	}
> +
> +	/* Update the power count. */
> +	coil->power_count += on ? 1 : -1;
> +	WARN_ON(coil->power_count < 0);
> +
> +done:
> +	mutex_unlock(&coil->power_lock);
> +	return ret;
> +}
> +
> +static int ad5820_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	return ad5820_set_power(sd, 1);
> +}
> +
> +static int ad5820_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	return ad5820_set_power(sd, 0);
> +}
> +
> +static const struct v4l2_subdev_core_ops ad5820_core_ops = {
> +	.s_power = ad5820_set_power,
> +};
> +
> +static const struct v4l2_subdev_ops ad5820_ops = {
> +	.core = &ad5820_core_ops,
> +};
> +
> +static const struct v4l2_subdev_internal_ops ad5820_internal_ops = {
> +	.registered = ad5820_registered,
> +	.open = ad5820_open,
> +	.close = ad5820_close,
> +};
> +
> +/*
> + * I2C driver
> + */
> +#ifdef CONFIG_PM
> +
> +static int ad5820_suspend(struct device *dev)
> +{
> +	struct i2c_client *client = container_of(dev, struct i2c_client, dev);
> +	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
> +	struct ad5820_device *coil = to_ad5820_device(subdev);
> +
> +	if (!coil->power_count)
> +		return 0;
> +
> +	return ad5820_power_off(coil, 0);
> +}
> +
> +static int ad5820_resume(struct device *dev)
> +{
> +	struct i2c_client *client = container_of(dev, struct i2c_client, dev);
> +	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
> +	struct ad5820_device *coil = to_ad5820_device(subdev);
> +
> +	if (!coil->power_count)
> +		return 0;
> +
> +	return ad5820_power_on(coil, 1);
> +}
> +
> +#else
> +
> +#define ad5820_suspend	NULL
> +#define ad5820_resume	NULL
> +
> +#endif /* CONFIG_PM */
> +
> +static int ad5820_probe(struct i2c_client *client,
> +			const struct i2c_device_id *devid)
> +{
> +	struct ad5820_device *coil;
> +	int ret = 0;
> +
> +	coil = kzalloc(sizeof(*coil), GFP_KERNEL);
> +	if (coil == NULL)

if (!coil) ?

> +		return -ENOMEM;
> +
> +	mutex_init(&coil->power_lock);
> +
> +	v4l2_i2c_subdev_init(&coil->subdev, client, &ad5820_ops);
> +	coil->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	coil->subdev.internal_ops = &ad5820_internal_ops;
> +	strcpy(coil->subdev.name, "ad5820 focus");
> +
> +	ret = media_entity_pads_init(&coil->subdev.entity, 0, NULL);
> +	if (ret < 0)
> +		goto free;
> +
> +	ret = v4l2_async_register_subdev(&coil->subdev);
> +	if (ret < 0)
> +		goto cleanup;
> +
> +	return ret;
> +cleanup:
> +	media_entity_cleanup(&coil->subdev.entity);
> +free:
> +	kfree(coil);
> +	return ret;
> +}

new line before return, also I would put new lines before labels :)

> +
> +static int __exit ad5820_remove(struct i2c_client *client)
> +{
> +	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
> +	struct ad5820_device *coil = to_ad5820_device(subdev);
> +
> +	v4l2_device_unregister_subdev(&coil->subdev);
> +	v4l2_ctrl_handler_free(&coil->ctrls);
> +	media_entity_cleanup(&coil->subdev.entity);
> +	if (coil->vana)
> +		regulator_put(coil->vana);
> +
> +	kfree(coil);

new line here

> +	return 0;
> +}
> +
> +static const struct i2c_device_id ad5820_id_table[] = {
> +	{ AD5820_NAME, 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, ad5820_id_table);
> +
> +static SIMPLE_DEV_PM_OPS(ad5820_pm, ad5820_suspend, ad5820_resume);
> +
> +static struct i2c_driver ad5820_i2c_driver = {
> +	.driver		= {
> +		.name	= AD5820_NAME,
> +		.pm	= &ad5820_pm,
> +	},
> +	.probe		= ad5820_probe,
> +	.remove		= __exit_p(ad5820_remove),
> +	.id_table	= ad5820_id_table,
> +};
> +
> +module_i2c_driver(ad5820_i2c_driver);
> +
> +MODULE_AUTHOR("Tuukka Toivonen");
> +MODULE_DESCRIPTION("AD5820 camera lens driver");
> +MODULE_LICENSE("GPL");
>

Thanks,
Ivo

  reply	other threads:[~2016-05-21  6:25 UTC|newest]

Thread overview: 73+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-05-17 18:19 [PATCH] support for AD5820 camera auto-focus coil Pavel Machek
2016-05-17 18:33 ` Marcus Folkesson
2016-05-18  8:30   ` Pavel Machek
2016-05-21  5:43 ` [PATCHv2] " Pavel Machek
2016-05-21  6:25   ` Ivaylo Dimitrov [this message]
2016-05-21 10:56     ` [PATCHv3] " Pavel Machek
2016-05-21 11:43       ` Ivaylo Dimitrov
2016-05-23  7:41         ` Pali Rohár
2016-05-24  9:04         ` Pavel Machek
2016-05-24  9:16           ` Ivaylo Dimitrov
2016-05-24 20:20             ` Pavel Machek
2016-05-26  3:46               ` Ivaylo Dimitrov
2016-05-24  9:17           ` [PATCHv4] " Pavel Machek
2016-05-25 21:26             ` Sakari Ailus
2016-05-27 20:33               ` Pavel Machek
2016-05-27 20:51               ` [PATCHv5] " Pavel Machek
2016-05-31 21:22                 ` Sakari Ailus
2016-05-31 21:34                   ` Pavel Machek
2016-06-01 15:24                     ` Sakari Ailus
2016-06-01 22:08                       ` Pavel Machek
2016-06-02  7:45                         ` Sakari Ailus
2016-06-02 19:27                           ` Pavel Machek
2016-06-02 21:23                             ` Sakari Ailus
2016-06-02 19:30                           ` [PATCH] device tree description " Pavel Machek
2016-06-02 21:27                             ` Sakari Ailus
2016-06-03  6:19                               ` Pavel Machek
2016-06-05 19:07                               ` [PATCH] userspace API definitions for " Pavel Machek
2016-06-06  6:06                                 ` Ivaylo Dimitrov
2016-06-06  7:21                                   ` Pavel Machek
2016-06-11 22:06                                   ` Sakari Ailus
2016-06-12  7:54                                     ` Pavel Machek
2016-06-17 21:28                                       ` Sakari Ailus
2016-06-12  8:48                                     ` Pavel Machek
2016-06-12 11:22                                       ` Sakari Ailus
2016-06-13 19:17                                         ` Pavel Machek
2016-06-17 21:35                                           ` Sakari Ailus
2016-06-18 15:37                                             ` [PATCHv4] support for AD5820 camera " Pavel Machek
2016-06-18 15:38                                             ` [PATCH] userspace API definitions for " Pavel Machek
2016-07-12 23:32                                               ` Mauro Carvalho Chehab
2016-07-13  6:57                                                 ` Pavel Machek
2016-07-13  7:26                                                 ` Pavel Machek
2016-06-06 13:29                             ` [PATCH] device tree description for AD5820 camera " Rob Herring
2016-06-07  7:10                             ` [PATCHv2] " Pavel Machek
2016-06-09 22:37                               ` Sakari Ailus
2016-06-04 23:27                 ` [PATCHv5] support " kbuild test robot
2016-06-05  0:46                 ` kbuild test robot
2016-06-12 20:41                 ` [PATCH 1/1] v4l: Add camera voice coil lens control class, current control Sakari Ailus
2016-06-12 21:48                   ` Ivaylo Dimitrov
2016-06-17 22:11                     ` Pavel Machek
2016-06-17 22:39                       ` Laurent Pinchart
2016-06-18 11:28                         ` Pavel Machek
2016-06-17 22:06                   ` Pavel Machek
2016-08-05 10:26                 ` [PATCHv6] support for AD5820 camera auto-focus coil Pavel Machek
2016-08-05 10:30                   ` Pali Rohár
2016-08-08  8:09                   ` Sakari Ailus
2016-08-08 21:41                     ` Pavel Machek
2016-08-10 12:01                       ` Sakari Ailus
2016-08-08 23:23                         ` Pavel Machek
2016-08-11 11:16                           ` Sakari Ailus
2016-08-18 10:45                             ` Pavel Machek
2016-08-18 20:26                               ` Sakari Ailus
2016-08-18 21:28                                 ` Pavel Machek
2016-11-03 10:27                                 ` Pavel Machek
2016-11-03 21:49                                   ` Sakari Ailus
2016-11-04  7:45                                     ` Pavel Machek
2016-11-04 14:49                                       ` Tony Lindgren
2016-12-14 13:38                       ` Pali Rohár
2016-12-14 15:08                         ` Tony Lindgren
2016-12-15  6:50                           ` Sakari Ailus
2016-12-19 22:23                             ` Pavel Machek
2016-08-10  7:34                     ` Pali Rohár
2016-08-08 21:40                       ` Pavel Machek
2016-08-10 12:00                       ` Sakari Ailus

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=573FFF51.1000004@gmail.com \
    --to=ivo.g.dimitrov.75@gmail.com \
    --cc=aaro.koskinen@iki.fi \
    --cc=khilman@kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=linux-omap@vger.kernel.org \
    --cc=mchehab@osg.samsung.com \
    --cc=pali.rohar@gmail.com \
    --cc=patrikbachan@gmail.com \
    --cc=pavel@ucw.cz \
    --cc=sakari.ailus@iki.fi \
    --cc=serge@hallyn.com \
    --cc=sre@kernel.org \
    --cc=tony@atomide.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).