Re: [PATCH v10 1/1] [media] i2c: add support for OV13858 sensor

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Hyungwoo,

On Mon, Jun 12, 2017 at 05:56:00PM -0700, Hyungwoo Yang wrote:
> This patch adds driver for Omnivision's ov13858
> sensor, the driver supports following features:
> 
> - manual exposure/gain(analog and digital) control support
> - two link frequencies
> - VBLANK/HBLANK support
> - test pattern support
> - media controller support
> - runtime pm support
> - supported resolutions
>   + 4224x3136 at 30FPS
>   + 2112x1568 at 30FPS(default) and 60FPS
>   + 2112x1188 at 30FPS(default) and 60FPS
>   + 1056x784 at 30FPS(default) and 60FPS
> 
> Signed-off-by: Hyungwoo Yang <hyungwoo.yang@xxxxxxxxx>
> ---
>  drivers/media/i2c/Kconfig   |    8 +
>  drivers/media/i2c/Makefile  |    1 +
>  drivers/media/i2c/ov13858.c | 1920 +++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1929 insertions(+)
>  create mode 100644 drivers/media/i2c/ov13858.c
> 
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index c380e24..26a9a3c 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -600,6 +600,14 @@ config VIDEO_OV9650
>  	  This is a V4L2 sensor-level driver for the Omnivision
>  	  OV9650 and OV9652 camera sensors.
>  
> +config VIDEO_OV13858
> +	tristate "OmniVision OV13858 sensor support"
> +	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> +	depends on MEDIA_CAMERA_SUPPORT

select V4L2_FWNODE

> +	---help---
> +	  This is a Video4Linux2 sensor-level driver for the OmniVision
> +	  OV13858 camera.
> +
>  config VIDEO_VS6624
>  	tristate "ST VS6624 sensor support"
>  	depends on VIDEO_V4L2 && I2C
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index 62323ec..3f4dc02 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -63,6 +63,7 @@ obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
>  obj-$(CONFIG_VIDEO_OV7640) += ov7640.o
>  obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
>  obj-$(CONFIG_VIDEO_OV9650) += ov9650.o
> +obj-$(CONFIG_VIDEO_OV13858) += ov13858.o
>  obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
>  obj-$(CONFIG_VIDEO_MT9M111) += mt9m111.o
>  obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
> diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c
> new file mode 100644
> index 0000000..0334cee
> --- /dev/null
> +++ b/drivers/media/i2c/ov13858.c

...

> +static int ov13858_read_platform_data(struct device *dev,
> +				      struct ov13858 *ov13858)
> +{
> +	struct fwnode_handle *child, *fwn_freq;
> +	struct ov13858_link_freq_config *freq_cfg;
> +	struct ov13858_reg *pll_regs;
> +	int i, freq_id = 0, ret, num_of_values, num_of_pairs;
> +	u32 val, *val_array;
> +
> +	/* For now, platform data is only available from fwnode */
> +	if (!dev_fwnode(dev)) {
> +		dev_err(dev, "no platform data\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = device_property_read_u32(dev, "skip-frames", &val);

This is a property of the sensor (or its configuration), not the hardware
platform. Please use a constant if the value does not depend on
configuration.

> +	if (ret)
> +		return ret;
> +	ov13858->num_of_skip_frames = val;
> +
> +	device_for_each_child_node(dev, child) {
> +		if (!fwnode_property_present(child, "link"))
> +			continue;

You shouldn't need these here.

> +
> +		/* Limited number of link frequencies are allowed */
> +		if (freq_id == OV13858_NUM_OF_LINK_FREQS) {
> +			dev_err(dev, "no more than two freqs\n");
> +			ret = -EINVAL;
> +			goto error;
> +		}
> +
> +		fwn_freq = fwnode_get_next_child_node(child, NULL);
> +		if (!fwn_freq) {
> +			ret = -EINVAL;
> +			goto error;
> +		}
> +
> +		/* Get link freq menu item for LINK_FREQ control */
> +		ret = fwnode_property_read_u32(fwn_freq, "link-rate", &val);

If you need to know the valid link frequencies (among other things), please
use v4l2_fwnode_endpoint_alloc_parse() instead. It parses the firmware
tables for you.

> +		if (ret) {
> +			dev_err(dev, "link-rate error : %d\n",  ret);
> +			goto error;
> +		}
> +		link_freq_menu_items[freq_id] = val;
> +
> +		freq_cfg = &link_freq_configs[freq_id];
> +		ret = fwnode_property_read_u32(fwn_freq, "pixel-rate", &val);

This is something a sensor driver needs to know. It may not be present in
device firmware.

> +		if (ret) {
> +			dev_err(dev, "pixel-rate error : %d\n",  ret);
> +			goto error;
> +		}
> +		freq_cfg->pixel_rate = val;
> +
> +		num_of_values = fwnode_property_read_u32_array(fwn_freq,
> +							       "pll-regs",

This is something that could go to device firmware but I don't really see
a reason for that at the moment. Can this continue to be a part of the
driver for now?

Could you also check that the external clock frequency matches with what
your register lists assume? The property should be called "clock-frequency"
and it's a u32.

> +							       NULL, 0);
> +		if (num_of_values <= 0 || num_of_values & 0x01) {
> +			dev_err(dev, "invalid pll-regs\n");
> +			ret = -EINVAL;
> +			goto error;
> +		}
> +
> +		/* Get num of addr-value pairs */
> +		num_of_pairs = num_of_values / 2;
> +		val_array = devm_kzalloc(dev,
> +					 sizeof(u32) * num_of_values,
> +					 GFP_KERNEL);
> +		if (!val_array) {
> +			ret = -ENOMEM;
> +			goto error;
> +		}
> +
> +		pll_regs = devm_kzalloc(dev,
> +					sizeof(*pll_regs) * num_of_pairs,
> +					GFP_KERNEL);
> +		if (!pll_regs) {
> +			ret = -ENOMEM;
> +			goto error;
> +		}
> +
> +		fwnode_property_read_u32_array(fwn_freq, "pll-regs",
> +					       val_array, num_of_values);
> +		for (i = 0; i < num_of_pairs; i++) {
> +			pll_regs[i].address = val_array[i * 2];
> +			pll_regs[i].val = val_array[i * 2 + 1];
> +		}
> +
> +		devm_kfree(dev, val_array);
> +
> +		freq_cfg->reg_list.num_of_regs = num_of_pairs;
> +		freq_cfg->reg_list.regs = pll_regs;
> +
> +		freq_id++;
> +	}
> +
> +	if (freq_id != OV13858_NUM_OF_LINK_FREQS)
> +		return -EINVAL;
> +
> +	return 0;
> +
> +error:
> +	fwnode_handle_put(child);
> +	return ret;
> +}
> +
> +static int ov13858_probe(struct i2c_client *client,
> +			 const struct i2c_device_id *devid)
> +{
> +	struct ov13858 *ov13858;
> +	int ret;
> +
> +	ov13858 = devm_kzalloc(&client->dev, sizeof(*ov13858), GFP_KERNEL);
> +	if (!ov13858)
> +		return -ENOMEM;
> +
> +	ret = ov13858_read_platform_data(&client->dev, ov13858);
> +	if (ret)
> +		return ret;
> +
> +	/* Initialize subdev */
> +	v4l2_i2c_subdev_init(&ov13858->sd, client, &ov13858_subdev_ops);
> +
> +	/* Check module identity */
> +	ret = ov13858_identify_module(ov13858);
> +	if (ret) {
> +		dev_err(&client->dev, "failed to find sensor: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* Set default mode to max resolution */
> +	ov13858->cur_mode = &supported_modes[0];
> +
> +	ret = ov13858_init_controls(ov13858);
> +	if (ret)
> +		return ret;
> +
> +	/* Initialize subdev */
> +	ov13858->sd.internal_ops = &ov13858_internal_ops;
> +	ov13858->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	ov13858->sd.entity.ops = &ov13858_subdev_entity_ops;
> +	ov13858->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +
> +	/* Initialize source pad */
> +	ov13858->pad.flags = MEDIA_PAD_FL_SOURCE;
> +	ret = media_entity_pads_init(&ov13858->sd.entity, 1, &ov13858->pad);
> +	if (ret) {
> +		dev_err(&client->dev, "%s failed:%d\n", __func__, ret);
> +		goto error_handler_free;
> +	}
> +
> +	ret = v4l2_async_register_subdev(&ov13858->sd);
> +	if (ret < 0)
> +		goto error_media_entity;
> +
> +	/*
> +	 * Device is already turned on by i2c-core with ACPI domain PM.
> +	 * Enable runtime PM and turn off the device.
> +	 */
> +	pm_runtime_get_noresume(&client->dev);
> +	pm_runtime_set_active(&client->dev);
> +	pm_runtime_enable(&client->dev);
> +	pm_runtime_put(&client->dev);
> +
> +	return 0;
> +
> +error_media_entity:
> +	media_entity_cleanup(&ov13858->sd.entity);
> +
> +error_handler_free:
> +	ov13858_free_controls(ov13858);
> +	dev_err(&client->dev, "%s failed:%d\n", __func__, ret);
> +
> +	return ret;
> +}
> +
> +static int ov13858_remove(struct i2c_client *client)
> +{
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct ov13858 *ov13858 = to_ov13858(sd);
> +
> +	v4l2_async_unregister_subdev(sd);
> +	media_entity_cleanup(&sd->entity);
> +	ov13858_free_controls(ov13858);
> +
> +	/*
> +	 * Disable runtime PM but keep the device turned on.
> +	 * i2c-core with ACPI domain PM will turn off the device.
> +	 */
> +	pm_runtime_get_sync(&client->dev);
> +	pm_runtime_disable(&client->dev);
> +	pm_runtime_set_suspended(&client->dev);
> +	pm_runtime_put_noidle(&client->dev);
> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id ov13858_id_table[] = {
> +	{"ov13858", 0},
> +	{},
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, ov13858_id_table);
> +
> +static const struct dev_pm_ops ov13858_pm_ops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(ov13858_suspend, ov13858_resume)
> +};
> +
> +#ifdef CONFIG_ACPI
> +static const struct acpi_device_id ov13858_acpi_ids[] = {
> +	{"OVTID858"},
> +	{ /* sentinel */ }
> +};
> +
> +MODULE_DEVICE_TABLE(acpi, ov13858_acpi_ids);
> +#endif
> +
> +static struct i2c_driver ov13858_i2c_driver = {
> +	.driver = {
> +		.name = "ov13858",
> +		.owner = THIS_MODULE,
> +		.pm = &ov13858_pm_ops,
> +		.acpi_match_table = ACPI_PTR(ov13858_acpi_ids),
> +	},
> +	.probe = ov13858_probe,
> +	.remove = ov13858_remove,
> +	.id_table = ov13858_id_table,
> +};
> +
> +module_i2c_driver(ov13858_i2c_driver);
> +
> +MODULE_AUTHOR("Kan, Chris <chris.kan@xxxxxxxxx>");
> +MODULE_AUTHOR("Rapolu, Chiranjeevi <chiranjeevi.rapolu@xxxxxxxxx>");
> +MODULE_AUTHOR("Yang, Hyungwoo <hyungwoo.yang@xxxxxxxxx>");
> +MODULE_DESCRIPTION("Omnivision ov13858 sensor driver");
> +MODULE_LICENSE("GPL v2");

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus@xxxxxx	XMPP: sailus@xxxxxxxxxxxxxx



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux