lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <1506330692.16112.68.camel@linux.intel.com>
Date:   Mon, 25 Sep 2017 12:11:32 +0300
From:   Andy Shevchenko <andriy.shevchenko@...ux.intel.com>
To:     Devid Antonio Floni <d.filoni@...ntu.com>
Cc:     sakari.ailus@...ux.intel.com,
        Mauro Carvalho Chehab <mchehab@...nel.org>,
        Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
        Alan Cox <alan@...ux.intel.com>,
        Hans Verkuil <hverkuil@...all.nl>,
        Jérémy Lefaure <jeremy.lefaure@....epita.fr>,
        linux-kernel@...r.kernel.org, linux-media@...r.kernel.org,
        devel@...verdev.osuosl.org
Subject: Re: [PATCH v2] staging: atomisp: add a driver for ov5648 camera
 sensor

On Sun, 2017-09-24 at 16:59 +0200, Devid Antonio Floni wrote:
> The ov5648 5-megapixel camera sensor from OmniVision supports up to
> 2592x1944
> resolution and MIPI CSI-2 interface. Output format is raw sRGB/Bayer
> with
> 10 bits per colour (SGRBG10_1X10).
> 
> This patch is a port of ov5648 driver after applying following
> 01org/ProductionKernelQuilts patches:
>  - 0004-ov2680-ov5648-Fork-lift-source-from-CTS.patch
>  - 0005-ov2680-ov5648-gminification.patch
>  - 0006-ov5648-Focus-support.patch
>  - 0007-Fix-resolution-issues-on-rear-camera.patch
>  - 0008-ov2680-ov5648-Enabled-the-set_exposure-functions.patch
>  - 0010-IRDA-cameras-mode-list-cleanup-unification.patch
>  - 0012-ov5648-Add-1296x972-binned-mode.patch
>  - 0014-ov5648-Adapt-to-Atomisp2-Gmin-VCM-framework.patch
>  - 0015-dw9714-Gmin-Atomisp-specific-VCM-driver.patch
>  - 0017-ov5648-Fix-deadlock-on-I2C-error.patch
>  - 0018-gc2155-Fix-deadlock-on-error.patch
>  - 0019-ov5648-Add-1280x960-binned-mode.patch
>  - 0020-ov5648-Make-1280x960-as-default-video-resolution.patch
>  - 0021-MALATA-Fix-testCameraToSurfaceTextureMetadata-CTS.patch
>  - 0023-OV5648-Added-5MP-video-resolution.patch

> 
> New changes introduced during the port:
>  - Rename entity types to entity functions
>  - Replace v4l2_subdev_fh by v4l2_subdev_pad_config
>  - Make use of media_bus_format enum
>  - Rename media_entity_init function to media_entity_pads_init
>  - Replace try_mbus_fmt by set_fmt
>  - Replace s_mbus_fmt by set_fmt
>  - Replace g_mbus_fmt by get_fmt
>  - Use s_ctrl/g_volatile_ctrl instead of ctrl core ops
>  - Update gmin platform API path
>  - Constify acpi_device_id
>  - Add "INT5648" value to acpi_device_id
>  - Fix some checkpatch errors and warnings
>  - Remove FSF's mailing address from the sample GPL notice
> 

> Changes in v2:
>  - Fix indentation
>  - Add atomisp prefix to Kconfig option

The above paragraph usually goes after --- delimiter line.


While v2 is better, it needs much more work. See my few comments (tip of
an iceberg) below.

> 
> "INT5648" ACPI device id can be found in following production
> hardware:
>     BIOS Information
>         Vendor: LENOVO
>         Version: 1HCN40WW
>         Release Date: 11/04/2016
>         ...
>         BIOS Revision: 0.40
>         Firmware Revision: 0.0
>     ...
>     System Information
>         Manufacturer: LENOVO
>         Product Name: 80SG
>         Version: MIIX 310-10ICR
>         ...
>         SKU Number: LENOVO_MT_80SG_BU_idea_FM_MIIX 310-10ICR
>         Family: IDEAPAD
>     ...
> 
> Device DSDT excerpt:
>     Device (CA00)
>     {
>         Name (_ADR, Zero)  // _ADR: Address
>         Name (_HID, "INT5648")  // _HID: Hardware ID
>         Name (_CID, "INT5648")  // _CID: Compatible ID
>         Name (_SUB, "INTL0000")  // _SUB: Subsystem ID
>         Name (_DDN, "ov5648")  // _DDN: DOS Device Name
>     ...

Thanks for the above description!

> 
> I was not able to properly test this patch on my Lenovo Miix 310 due
> to other
> issues with atomisp, the output is the same as ov2680 driver
> (OVTI2680)
> which is very similar.


> +#include <linux/module.h>

(1)

> +#include <linux/types.h>
> +#include <linux/kernel.h>
> +#include <linux/mm.h>
> +#include <linux/string.h>
> +#include <linux/errno.h>

> +#include <linux/init.h>

(2)

> +#include <linux/kmod.h>
> +#include <linux/device.h>
> +#include <linux/delay.h>
> +#include <linux/slab.h>
> +#include <linux/i2c.h>
> +#include <linux/gpio.h>
> +#include <linux/moduleparam.h>
> +#include <media/v4l2-device.h>
> +#include <linux/io.h>
> +#include "../include/linux/atomisp_gmin_platform.h"
> +
> +#include "ov5648.h"

One of (1) and (2) is redundant.

Can we also keep them in alphabetical order? Rationale is to fast search
among and avoiding duplication.

> +static int ov5648_read_reg(struct i2c_client *client,
> +			   u16 data_length, u16 reg, u16 *val)
> +{
> +	int err;
> +	struct i2c_msg msg[2];
> +	unsigned char data[6];
> +

> +	if (!client->adapter) {
> +		dev_err(&client->dev, "%s error, no client-
> >adapter\n",
> +			__func__);
> +		return -ENODEV;
> +	}

Hmm... When it's possible?

> +
> +	if (data_length != OV5648_8BIT && data_length != OV5648_16BIT
> +					&& data_length !=
> OV5648_32BIT) {
> +		dev_err(&client->dev, "%s error, invalid data
> length\n",
> +			__func__);
> +		return -EINVAL;
> +	}
> +
> 

> +
> +	err = i2c_transfer(client->adapter, msg, 2);
> +	if (err != 2) {

> +		if (err >= 0)
> +			err = -EIO;

This will hide an error below (in case it's returned -EIO vs. err ==
1)...

> +		dev_err(&client->dev,
> +			"read from offset 0x%x error %d", reg, err);

...thus would be better to

dev_err();
return err < 0 ? err : - EIO;


> +		return err;
> +	}
> +
> +	*val = 0;
> +	/* high byte comes first */
> +	if (data_length == OV5648_8BIT)
> +		*val = (u8)data[0];
> +	else if (data_length == OV5648_16BIT)
> +		*val = be16_to_cpu(*(u16 *)&data[0]);
> +	else
> +		*val = be32_to_cpu(*(u32 *)&data[0]);

If you can use i2c_smbus_*() functions they will convert endianess at
the same time.

> +
> +	return 0;
> +}
> 

> +static int ov5648_write_reg(struct i2c_client *client, u16
> data_length,
> +			    u16 reg, u16 val)
> +{
> +	int ret;
> +	unsigned char data[4] = {0};
> +	u16 *wreg = (u16 *)data;
> +	const u16 len = data_length + sizeof(u16); /* 16-bit address
> + data */
> +
> +	if (data_length != OV5648_8BIT && data_length !=
> OV5648_16BIT) {

Where 32 bit data width gone?

> +		dev_err(&client->dev,
> +			"%s error, invalid data_length\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	/* high byte goes out first */
> +	*wreg = cpu_to_be16(reg);
> +
> +	if (data_length == OV5648_8BIT) {
> +		data[2] = (u8)(val);
> +	} else {
> +		/* OV5648_16BIT */
> +		u16 *wdata = (u16 *)&data[2];
> +		*wdata = cpu_to_be16(val);
> +	}
> +
> +	ret = ov5648_i2c_write(client, len, data);
> +	if (ret)
> +		dev_err(&client->dev,
> +			"write error: wrote 0x%x to offset 0x%x error
> %d",
> +			val, reg, ret);
> +
> +	return ret;
> +}


> +static int ov5648_write_reg_array(struct i2c_client *client,
> +				  const struct ov5648_reg *reglist)
> +{
> +	const struct ov5648_reg *next = reglist;
> +	struct ov5648_write_ctrl ctrl;
> +	int err;
> +
> +	ctrl.index = 0;
> +	for (; next->type != OV5648_TOK_TERM; next++) {

Better to 
for (next = reglist; ...) {

> +		switch (next->type & OV5648_TOK_MASK) {
> +		case OV5648_TOK_DELAY:
> +			err = __ov5648_flush_reg_array(client,
> &ctrl);
> +			if (err)
> +				return err;

> +			msleep(next->val);

Oy vey, how can you guarantee that this will not be long enough to make
user experience awful?

> +			break;
> +		default:
> +			/*
> +			 * If next address is not consecutive, data
> needs to be
> +			 * flushed before proceed.
> +			 */
> +			if
> (!__ov5648_write_reg_is_consecutive(client, &ctrl,
> +								next)
> ) {
> +				err =
> __ov5648_flush_reg_array(client, &ctrl);
> +			if (err)
> +				return err;
> +			}
> +			err = __ov5648_buf_reg_array(client, &ctrl,
> next);
> +			if (err) {
> +				dev_err(&client->dev, "%s: write
> error, aborted\n",
> +					 __func__);
> +				return err;
> +			}
> +			break;
> +		}
> +	}
> +
> +	return __ov5648_flush_reg_array(client, &ctrl);
> +}

+ empty line.

> +static int ov5648_g_focal(struct v4l2_subdev *sd, s32 *val)
> +{
> +	*val = (OV5648_FOCAL_LENGTH_NUM << 16) |
> OV5648_FOCAL_LENGTH_DEM;
> +	return 0;
> +}
> +
> +static int ov5648_g_fnumber(struct v4l2_subdev *sd, s32 *val)
> +{

> +	/*const f number for imx*/

Bad style.

> +	*val = (OV5648_F_NUMBER_DEFAULT_NUM << 16) |
> OV5648_F_NUMBER_DEM;
> +	return 0;
> +}

> +static int ov5648_get_intg_factor(struct i2c_client *client,
> +				struct camera_mipi_info *info,
> +				const struct ov5648_resolution *res)
> +{
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct ov5648_device *dev = to_ov5648_sensor(sd);
> +	struct atomisp_sensor_mode_data *buf = &info->data;
> +	unsigned int pix_clk_freq_hz;
> +	u16 reg_val;
> +	int ret;
> +
> +	if (!info)
> +		return -EINVAL;
> +

> +	/* pixel clock */

Isn't it obvious from the code?

> +	pix_clk_freq_hz = res->pix_clk_freq * 1000000;
> +
> +	dev->vt_pix_clk_freq_mhz = pix_clk_freq_hz;
> +	buf->vt_pix_clk_freq_mhz = pix_clk_freq_hz;
> +
> +	/* get integration time */

Ditto.

> +	buf->coarse_integration_time_min =
> OV5648_COARSE_INTG_TIME_MIN;
> +	buf->coarse_integration_time_max_margin =
> +		OV5648_COARSE_INTG_TIME_MAX_MARGIN;
> +
> +	buf->fine_integration_time_min = OV5648_FINE_INTG_TIME_MIN;
> +	buf->fine_integration_time_max_margin =
> +		OV5648_FINE_INTG_TIME_MAX_MARGIN;
> +
> +	buf->fine_integration_time_def = OV5648_FINE_INTG_TIME_MIN;
> +	buf->frame_length_lines = res->lines_per_frame;
> +	buf->line_length_pck = res->pixels_per_line;
> +	buf->read_mode = res->bin_mode;
> +
> +	/* get the cropping and output resolution to ISP for this
> mode. */
> 

Style of comments.

Make them consistent over the code and be following the code-style
rules.

> +}

> +	// EXPOSURE CONTROL DISABLED FOR INITIAL CHECKIN, TUNING
> DOESN'T WORK

Huh?

This style of comments should not be in upstream code.

> +	return ov5648_set_exposure(sd, exp, gain, digitgain);
> +}

> +err:

> +	return ret;

In above like cases err: label is redundant.

> +}

> +static int ov5648_h_flip(struct v4l2_subdev *sd, s32 value)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	int ret;
> +	u16 val;

+ empty line.

> +	dev_dbg(&client->dev, "@%s: value:%d\n", __func__, value);

Please, remove __func__ from all dev_dbg() and pr_debug() calls.
Dynamic debug can do it for you.

Moreover, if you wish to do better debug, switch to trace points.

> +static int ov5648_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct ov5648_device *dev = container_of(ctrl->handler,
> +		struct ov5648_device, ctrl_handler);

> +	int ret = 0;

Redundant assignment. Check the rest of the code for it and remove where
it's not needed. Otherwise it might hide a potential error.

> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_EXPOSURE_ABSOLUTE:
> +		ret = ov5648_q_exposure(&dev->sd, &ctrl->val);
> +		break;
> +	case V4L2_CID_FOCAL_ABSOLUTE:
> +		ret = ov5648_g_focal(&dev->sd, &ctrl->val);
> +		break;
> +	case V4L2_CID_FNUMBER_ABSOLUTE:
> +		ret = ov5648_g_fnumber(&dev->sd, &ctrl->val);
> +		break;
> +	case V4L2_CID_FNUMBER_RANGE:
> +		ret = ov5648_g_fnumber_range(&dev->sd, &ctrl->val);
> +		break;
> +	case V4L2_CID_BIN_FACTOR_HORZ:
> +		ret = ov5648_g_bin_factor_x(&dev->sd, &ctrl->val);
> +		break;
> +	case V4L2_CID_BIN_FACTOR_VERT:
> +		ret = ov5648_g_bin_factor_y(&dev->sd, &ctrl->val);
> +		break;
> +	default:
> +		ret = -EINVAL;
> +	}
> +
> +	return ret;
> +}

> +#if 1

WTF?

> +/*
> + *Camera driver need to load AWB calibration data
> + *stored in OTP and write to gain registers after
> + *initialization of register settings.
> + * index: index of otp group. (1, 2, 3)
> + * return: 0, group index is empty
> + *		1, group index has invalid data
> + *		2, group index has valid data
> + */
> +static int check_otp(struct i2c_client *client, int index)
> +{
> +	int i;
> +	u16 flag = 0, rg = 0, bg = 0;
> +	if (index == 1) {
> +		/* read otp --Bank 0 */
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d84, 0xc0);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d85, 0x00);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d86, 0x0f);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d81, 0x01);
> +		mdelay(5);
> +		ov5648_read_reg(client, OV5648_8BIT, 0x3d05, &flag);
> +		ov5648_read_reg(client, OV5648_8BIT, 0x3d07, &rg);
> +		ov5648_read_reg(client, OV5648_8BIT, 0x3d08, &bg);
> +	} else if (index == 2) {
> +		/* read otp --Bank 0 */
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d84, 0xc0);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d85, 0x00);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d86, 0x0f);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d81, 0x01);
> +		mdelay(5);
> +		ov5648_read_reg(client, OV5648_8BIT, 0x3d0e, &flag);
> +
> +		/* read otp --Bank 1 */
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d84, 0xc0);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d85, 0x10);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d86, 0x1f);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d81, 0x01);
> +		mdelay(5);
> +		ov5648_read_reg(client, OV5648_8BIT, 0x3d00, &rg);
> +		ov5648_read_reg(client, OV5648_8BIT, 0x3d01, &bg);
> +	} else if (index == 3) {
> +		/* read otp --Bank 1 */
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d84, 0xc0);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d85, 0x10);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d86, 0x1f);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d81, 0x01);
> +		mdelay(5);
> +		ov5648_read_reg(client, OV5648_8BIT, 0x3d07, &flag);
> +		ov5648_read_reg(client, OV5648_8BIT, 0x3d09, &rg);
> +		ov5648_read_reg(client, OV5648_8BIT, 0x3d0a, &bg);
> +	}
> +
> +	flag = flag & 0x80;
> +
> +	/* clear otp buffer */
> +	for (i = 0; i < 16; i++)
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d00 + i,
> 0x00);
> +
> +	if (flag)
> +		return 1;
> +	else {
> +		if (rg == 0 && bg == 0)
> +			return 0;
> +		else
> +			return 2;
> +	}
> +
> +}
> +
> +/* index: index of otp group. (1, 2, 3)
> + * return: 0,
> + */
> +static int read_otp(struct i2c_client *client,
> +	    int index, struct otp_struct *otp_ptr)
> +{
> +	int i;
> +	u16 temp;
> +	/* read otp into buffer */
> +	if (index == 1) {
> +		/* read otp --Bank 0 */
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d84, 0xc0);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d85, 0x00);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d86, 0x0f);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d81, 0x01);
> +		mdelay(5);
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d05, &((*otp_ptr).module_integrator_id));
> +		(*otp_ptr).module_integrator_id =
> +			(*otp_ptr).module_integrator_id & 0x7f;
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d06, &((*otp_ptr).lens_id));
> +		ov5648_read_reg(client, OV5648_8BIT, 0x3d0b, &temp);
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d07, &((*otp_ptr).rg_ratio));
> +		(*otp_ptr).rg_ratio =
> +			((*otp_ptr).rg_ratio<<2) + ((temp>>6) &
> 0x03);
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d08, &((*otp_ptr).bg_ratio));
> +		(*otp_ptr).bg_ratio =
> +			((*otp_ptr).bg_ratio<<2) + ((temp>>4) &
> 0x03);
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d0c, &((*otp_ptr).light_rg));
> +		(*otp_ptr).light_rg =
> +			((*otp_ptr).light_rg<<2) + ((temp>>2) &
> 0x03);
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d0d, &((*otp_ptr).light_bg));
> +		(*otp_ptr).light_bg =
> +			((*otp_ptr).light_bg<<2) + (temp & 0x03);
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d09, &((*otp_ptr).user_data[0]));
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d0a, &((*otp_ptr).user_data[1]));
> +	} else if (index == 2) {
> +		/* read otp --Bank 0 */
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d84, 0xc0);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d85, 0x00);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d86, 0x0f);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d81, 0x01);
> +		mdelay(5);
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d0e, &((*otp_ptr).module_integrator_id));
> +		(*otp_ptr).module_integrator_id =
> +			(*otp_ptr).module_integrator_id & 0x7f;
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d0f, &((*otp_ptr).lens_id));
> +		/* read otp --Bank 1 */
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d84, 0xc0);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d85, 0x10);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d86, 0x1f);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d81, 0x01);
> +		mdelay(5);
> +		ov5648_read_reg(client, OV5648_8BIT, 0x3d04, &temp);
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d00, &((*otp_ptr).rg_ratio));
> +		(*otp_ptr).rg_ratio =
> +			((*otp_ptr).rg_ratio<<2) + ((temp>>6) &
> 0x03);
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d01, &((*otp_ptr).bg_ratio));
> +		(*otp_ptr).bg_ratio =
> +			((*otp_ptr).bg_ratio<<2) + ((temp>>4) &
> 0x03);
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d05, &((*otp_ptr).light_rg));
> +		(*otp_ptr).light_rg =
> +			((*otp_ptr).light_rg<<2) + ((temp>>2) &
> 0x03);
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d06, &((*otp_ptr).light_bg));
> +		(*otp_ptr).light_bg =
> +			((*otp_ptr).light_bg<<2) + (temp & 0x03);
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d02, &((*otp_ptr).user_data[0]));
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d03, &((*otp_ptr).user_data[1]));
> +	} else if (index == 3) {
> +		/* read otp --Bank 1 */
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d84, 0xc0);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d85, 0x10);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d86, 0x1f);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d81, 0x01);
> +		mdelay(5);
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d07, &((*otp_ptr).module_integrator_id));
> +		(*otp_ptr).module_integrator_id =
> +			(*otp_ptr).module_integrator_id & 0x7f;
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d08, &((*otp_ptr).lens_id));
> +		ov5648_read_reg(client, OV5648_8BIT, 0x3d0d, &temp);
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d09, &((*otp_ptr).rg_ratio));
> +		(*otp_ptr).rg_ratio =
> +			((*otp_ptr).rg_ratio<<2) + ((temp>>6) &
> 0x03);
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d0a, &((*otp_ptr).bg_ratio));
> +		(*otp_ptr).bg_ratio =
> +			((*otp_ptr).bg_ratio<<2) + ((temp>>4) &
> 0x03);
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d0e, &((*otp_ptr).light_rg));
> +		(*otp_ptr).light_rg =
> +			((*otp_ptr).light_rg<<2) + ((temp>>2) &
> 0x03);
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d0f, &((*otp_ptr).light_bg));
> +		(*otp_ptr).light_bg =
> +			((*otp_ptr).light_bg<<2) + (temp & 0x03);
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d0b, &((*otp_ptr).user_data[0]));
> +		ov5648_read_reg(client, OV5648_8BIT,
> +			0x3d0c, &((*otp_ptr).user_data[1]));
> +	}
> +	/* clear otp buffer */
> +	for (i = 0; i < 16; i++)
> +		ov5648_write_reg(client, OV5648_8BIT, 0x3d00 + i,
> 0x00);
> +
> +	return 0;
> +}
> +/* R_gain, sensor red gain of AWB, 0x400 =1
> + * G_gain, sensor green gain of AWB, 0x400 =1
> + * B_gain, sensor blue gain of AWB, 0x400 =1
> + * return 0;
> + */
> +static int update_awb_gain(struct v4l2_subdev *sd)
> +{
> +
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	struct ov5648_device *dev = to_ov5648_sensor(sd);
> +	int R_gain = dev->current_otp.R_gain;
> +	int G_gain = dev->current_otp.G_gain;
> +	int B_gain = dev->current_otp.B_gain;
> +	if (R_gain > 0x400) {
> +		ov5648_write_reg(client, OV5648_8BIT, 0x5186,
> R_gain>>8);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x5187, R_gain
> & 0x00ff);
> +	}
> +	if (G_gain > 0x400) {
> +		ov5648_write_reg(client, OV5648_8BIT, 0x5188,
> G_gain>>8);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x5189, G_gain
> & 0x00ff);
> +	}
> +	if (B_gain > 0x400) {
> +		ov5648_write_reg(client, OV5648_8BIT, 0x518a,
> B_gain>>8);
> +		ov5648_write_reg(client, OV5648_8BIT, 0x518b, B_gain
> & 0x00ff);
> +	}
> +	#ifdef OV5648_DEBUG_EN
> +	dev_dbg(&client->dev, "_ov5648_: %s : rgain:%x ggain:%x
> bgain:%x\n", __func__, R_gain, G_gain, B_gain);
> +	#endif
> +	return 0;
> +}
> +
> +/* call this function after OV5648 initialization
> + * return: 0 update success
> + *		1, no OTP
> + */
> +static int update_otp(struct v4l2_subdev *sd)
> +{
> +	struct otp_struct current_otp;
> +	struct ov5648_device *dev = to_ov5648_sensor(sd);
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	int i, ret;
> +	int otp_index;
> +	u16 temp;
> +	int R_gain, G_gain, B_gain, G_gain_R, G_gain_B;
> +	u16 rg = 1, bg = 1;
> +
> +	//otp valid after mipi on and sw stream on
> +	ov5648_write_reg(client, OV5648_8BIT, OV5648_SW_STREAM,
> OV5648_START_STREAMING);
> +
> +	/* R/G and B/G of current camera module is read out from
> sensor OTP
> +	 * check first OTP with valid data
> +	 */
> +	for (i = 1; i <= 3; i++) {
> +		temp = check_otp(client, i);
> +		if (temp == 2) {
> +			otp_index = i;
> +			break;
> +		}
> +	}
> +	if (i > 3) {
> +		printk(KERN_INFO"@%s: no valid wb otp data\n",
> __func__);
> +		/* no valid wb OTP data */
> +		return 1;
> +	}
> +	read_otp(client, otp_index, &current_otp);
> +	if (current_otp.light_rg == 0) {
> +		/* no light source information in OTP */
> +		rg = current_otp.rg_ratio;
> +	} else {
> +		/* light source information found in OTP */
> +		rg = current_otp.rg_ratio * (current_otp.light_rg +
> 512) / 1024;
> +	}
> +	if (current_otp.light_bg == 0) {
> +		/* no light source information in OTP */
> +		bg = current_otp.bg_ratio;
> +	} else {
> +		/* light source information found in OTP */
> +		bg = current_otp.bg_ratio * (current_otp.light_bg +
> 512) / 1024;
> +	}
> +	#ifdef OV5648_DEBUG_EN
> +	dev_dbg(&client->dev, "_ov5648_: %s : rg:%x bg:%x\n",
> __func__, rg, bg);
> +	#endif
> +	if (rg == 0)
> +		rg = 1;
> +	if (bg == 0)
> +		bg = 1;
> +	/*calculate G gain
> +	 *0x400 = 1x gain
> +	 */
> +	if (bg < BG_Ratio_Typical) {
> +		if (rg < RG_Ratio_Typical) {
> +			/* current_otp.bg_ratio < BG_Ratio_typical &&
> +			 * current_otp.rg_ratio < RG_Ratio_typical
> +			 */
> +			G_gain = 0x400;
> +			B_gain = 0x400 * BG_Ratio_Typical / bg;
> +			R_gain = 0x400 * RG_Ratio_Typical / rg;
> +		} else {
> +			/* current_otp.bg_ratio < BG_Ratio_typical &&
> +			 * current_otp.rg_ratio >= RG_Ratio_typical
> +			 */
> +			R_gain = 0x400;
> +			G_gain = 0x400 * rg / RG_Ratio_Typical;
> +			B_gain = G_gain * BG_Ratio_Typical / bg;
> +		}
> +	} else {
> +		if (rg < RG_Ratio_Typical) {
> +			/* current_otp.bg_ratio >= BG_Ratio_typical
> &&
> +			 * current_otp.rg_ratio < RG_Ratio_typical
> +			 */
> +			B_gain = 0x400;
> +			G_gain = 0x400 * bg / BG_Ratio_Typical;
> +			R_gain = G_gain * RG_Ratio_Typical / rg;
> +		} else {
> +			/* current_otp.bg_ratio >= BG_Ratio_typical
> &&
> +			 * current_otp.rg_ratio >= RG_Ratio_typical
> +			 */
> +			G_gain_B = 0x400 * bg / BG_Ratio_Typical;
> +			G_gain_R = 0x400 * rg / RG_Ratio_Typical;
> +			if (G_gain_B > G_gain_R) {
> +				B_gain = 0x400;
> +				G_gain = G_gain_B;
> +				R_gain = G_gain * RG_Ratio_Typical /
> rg;
> +			} else {
> +				R_gain = 0x400;
> +				G_gain = G_gain_R;
> +				B_gain = G_gain * BG_Ratio_Typical /
> bg;
> +			}
> +		}
> +	}
> +
> +	dev->current_otp.R_gain = R_gain;
> +	dev->current_otp.G_gain = G_gain;
> +	dev->current_otp.B_gain = B_gain;
> +
> +	ret = ov5648_write_reg(client, OV5648_8BIT,
> +		OV5648_SW_STREAM, OV5648_STOP_STREAMING);
> +	return ret ;
> +}
> +
> +#endif
> +
> +static int power_ctrl(struct v4l2_subdev *sd, bool flag)
> +{
> +	int ret = 0;
> +	struct ov5648_device *dev = to_ov5648_sensor(sd);
> +	if (!dev || !dev->platform_data)
> +		return -ENODEV;
> +
> +	/* Non-gmin platforms use the legacy callback */
> +	if (dev->platform_data->power_ctrl)
> +		return dev->platform_data->power_ctrl(sd, flag);
> +
> +	if (flag) {
> +		ret |= dev->platform_data->v1p8_ctrl(sd, 1);
> +		ret |= dev->platform_data->v2p8_ctrl(sd, 1);
> +		usleep_range(10000, 15000);
> +	}
> +
> +	if (!flag || ret) {
> +		ret |= dev->platform_data->v1p8_ctrl(sd, 0);
> +		ret |= dev->platform_data->v2p8_ctrl(sd, 0);
> +	}
> +	return ret;
> +}
> +
> +static int gpio_ctrl(struct v4l2_subdev *sd, bool flag)
> +{

> +	int ret;
> +	struct ov5648_device *dev = to_ov5648_sensor(sd);

Better to use reversed xmas tree order in such blocks.

> +
> +	if (!dev || !dev->platform_data)
> +		return -ENODEV;
> +

When it's the case?

> +	/* Non-gmin platforms use the legacy callback */
> +	if (dev->platform_data->gpio_ctrl)
> +		return dev->platform_data->gpio_ctrl(sd, flag);

What the platforms? Are they sold in the open market?

> +
> +	/* GPIO0 == "RESETB", GPIO1 == "PWDNB", named in opposite
> +	 * senses but with the same behavior: both must be high for
> +	 * the device to opperate */
> +	if (flag) {
> +		ret = dev->platform_data->gpio0_ctrl(sd, 1);
> +		usleep_range(10000, 15000);
> +		ret |= dev->platform_data->gpio1_ctrl(sd, 1);
> +		usleep_range(10000, 15000);
> +	} else {
> +		ret = dev->platform_data->gpio1_ctrl(sd, 0);
> +		ret |= dev->platform_data->gpio0_ctrl(sd, 0);
> +	}
> +	return ret;
> +}
> +
> +static int power_up(struct v4l2_subdev *sd)
> +{
> +	struct ov5648_device *dev = to_ov5648_sensor(sd);
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	int ret;
> +
> +	dev_dbg(&client->dev, "@%s:\n", __func__);
> +	if (!dev->platform_data) {
> +		dev_err(&client->dev,
> +			"no camera_sensor_platform_data");
> +		return -ENODEV;
> +	}
> +
> +	/* power control */
> +	ret = power_ctrl(sd, 1);
> +	if (ret)
> +		goto fail_power;
> +
> +	/* according to DS, at least 5ms is needed between DOVDD and
> PWDN */
> +	usleep_range(5000, 6000);
> +
> +	/* gpio ctrl */
> +	ret = gpio_ctrl(sd, 1);
> +	if (ret) {
> +		ret = gpio_ctrl(sd, 1);
> +		if (ret)
> +			goto fail_power;
> +	}
> +
> +	/* flis clock control */
> +	ret = dev->platform_data->flisclk_ctrl(sd, 1);
> +	if (ret)
> +		goto fail_clk;
> +
> +	/* according to DS, 20ms is needed between PWDN and i2c
> access */
> +	msleep(20);
> +
> +	return 0;
> +
> +fail_clk:
> +	gpio_ctrl(sd, 0);
> +fail_power:
> +	power_ctrl(sd, 0);
> +	dev_err(&client->dev, "sensor power-up failed\n");
> +
> +	return ret;
> +}

> +
> +static int power_down(struct v4l2_subdev *sd)
> +{
> +	struct ov5648_device *dev = to_ov5648_sensor(sd);
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	int ret = 0;
> +
> +	h_flag = H_FLIP_DEFAULT;
> +	v_flag = V_FLIP_DEFAULT;
> +	dev_dbg(&client->dev, "@%s:\n", __func__);
> +	if (!dev->platform_data) {
> +		dev_err(&client->dev,
> +			"no camera_sensor_platform_data");
> +		return -ENODEV;
> +	}
> +
> +	ret = dev->platform_data->flisclk_ctrl(sd, 0);
> +	if (ret)
> +		dev_err(&client->dev, "flisclk failed\n");
> +
> +	/* gpio ctrl */
> +	ret = gpio_ctrl(sd, 0);
> +	if (ret) {
> +		ret = gpio_ctrl(sd, 0);
> +		if (ret)
> +			dev_err(&client->dev, "gpio failed 2\n");
> +	}
> +
> +	/* power control */
> +	ret = power_ctrl(sd, 0);
> +	if (ret)
> +		dev_err(&client->dev, "vprog failed.\n");
> +
> +	return ret;
> +}

For all above play around GPIO you need to register a voltage regulator
and use regulator framework.

> +#define LARGEST_ALLOWED_RATIO_MISMATCH 900
> +static int distance(struct ov5648_resolution *res, u32 w, u32 h)
> +{
> +	unsigned int w_ratio = ((res->width << 13) / w);

Redundant parens.

> +	unsigned int h_ratio;
> +	int match;
> +
> +	if (h == 0)
> +		return -1;
> +	h_ratio = ((res->height << 13) / h);

Ditto.

> +	if (h_ratio == 0)
> +		return -1;
> +	match   = abs(((w_ratio << 13) / h_ratio) - ((int)8192));

Ditto.

> +
> +	if ((w_ratio < (int)8192) || (h_ratio < (int)8192)  ||
> +		(match > LARGEST_ALLOWED_RATIO_MISMATCH))
> +		return -1;

Why (int) casting here and above?!

> +
> +	return w_ratio + h_ratio;
> +}

> +
> +/* Return the nearest higher resolution index */
> +static int nearest_resolution_index(int w, int h)
> +{
> +	int i;
> +	int idx = -1;
> +	int dist;
> +	int min_dist = INT_MAX;
> +	struct ov5648_resolution *tmp_res = NULL;

Reversed xmas tree order.

> +
> +	for (i = 0; i < N_RES; i++) {
> +		tmp_res = &ov5648_res[i];
> +		dist = distance(tmp_res, w, h);
> +		if (dist == -1)
> +			continue;
> +		if (dist < min_dist) {
> +			min_dist = dist;
> +			idx = i;
> +		}
> +	}
> +
> +	return idx;
> +}
> 

> +	id = ((((u16) high) << 8) | (u16) low);

Redundant parens. Please, check all lines to avoid such.

> +
> 

> +	revision = (u8) high & 0x0f;
> +
> +	dev_dbg(&client->dev, "sensor_revision = 0x%x\n", revision);
> +	dev_dbg(&client->dev, "detect ov5648 success\n");

It would be one line, moreover, instead of above, you may use

...("...%c...",  hex_asc_lo[high]);


> +	return 0;
> +}

> +
> +static int ov5648_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct ov5648_device *dev = to_ov5648_sensor(sd);
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	int ret;

+ empty line.
Please check all functions.

> +	dev_dbg(&client->dev, "@%s:\n", __func__);

Noise. Remove.
Do this for all similar cases.

> +	mutex_lock(&dev->input_lock);
> +
> +	ret = ov5648_write_reg(client, OV5648_8BIT, OV5648_SW_STREAM,
> +				enable ? OV5648_START_STREAMING :
> +				OV5648_STOP_STREAMING);
> +
> +	mutex_unlock(&dev->input_lock);
> +
> +	return ret;
> +}

> +static int ov5648_remove(struct i2c_client *client)
> +{
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct ov5648_device *dev = to_ov5648_sensor(sd);

> +	dev_dbg(&client->dev, "ov5648_remove...\n");

Noise, remove.

> +
> +	dev->platform_data->csi_cfg(sd, 0);
> +
> +	v4l2_device_unregister_subdev(sd);
> +	media_entity_cleanup(&dev->sd.entity);
> +	v4l2_ctrl_handler_free(&dev->ctrl_handler);
> +	kfree(dev);
> +
> +	return 0;
> +}

> +
> +static int ov5648_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)
> +{
> +	struct ov5648_device *dev;
> +	size_t len = CAMERA_MODULE_ID_LEN * sizeof(char);
> +	int ret;
> +	void *pdata;
> +	unsigned int i;
> +
> +	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
> +	if (!dev) {
> +		dev_err(&client->dev, "out of memory\n");
> +		return -ENOMEM;
> +	}
> +
> +	dev->camera_module = kzalloc(len, GFP_KERNEL);
> +	if (!dev->camera_module) {
> +		kfree(dev);
> +		dev_err(&client->dev, "out of memory\n");
> +		return -ENOMEM;
> +	}
> +
> +	mutex_init(&dev->input_lock);
> +
> +	dev->fmt_idx = 0;

> +	//otp functions

Wrong style, noisy comment. Remove.

> +	dev->current_otp.otp_en = 1;// enable otp functions

Ditto.

> +	v4l2_i2c_subdev_init(&(dev->sd), client, &ov5648_ops);
> +
> +	if (gmin_get_config_var(&client->dev, "CameraModule",
> +				dev->camera_module, &len)) {
> +		kfree(dev->camera_module);
> +		dev->camera_module = NULL;
> +		dev_info(&client->dev, "Camera module id is
> missing\n");
> +	}
> +
> +	if (ACPI_COMPANION(&client->dev))
> +		pdata = gmin_camera_platform_data(&dev->sd,
> +						  ATOMISP_INPUT_FORMA
> T_RAW_10,
> +						  atomisp_bayer_order
> _bggr);
> +	else

> +		pdata = client->dev.platform_data;

What kind of platforms will use platform_data?

> +out_free:
> +	v4l2_device_unregister_subdev(&dev->sd);

Doesn't v4l2 have devm_*() helpers?

> +	kfree(dev->camera_module);
> +	kfree(dev);

Shouldn't those be devm_kzalloc() ?

> +	return ret;
> +}

> +
> +static const struct acpi_device_id ov5648_acpi_match[] = {

> +	{"XXOV5648"},

WTF is that?

> +	{"INT5648"},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(acpi, ov5648_acpi_match);
> +

> +MODULE_DEVICE_TABLE(i2c, ov5648_id);

Where is the table?

> +static struct i2c_driver ov5648_driver = {
> +	.driver = {

> +		.owner = THIS_MODULE,

Redundant.

> +		.name = OV5648_NAME,
> +		.acpi_match_table = ACPI_PTR(ov5648_acpi_match),
> +	},
> +	.probe = ov5648_probe,
> +	.remove = ov5648_remove,
> +	.id_table = ov5648_id,
> +};
> +

> +static int init_ov5648(void)
> +{
> +	return i2c_add_driver(&ov5648_driver);
> +}
> +
> +static void exit_ov5648(void)
> +{
> +
> +	i2c_del_driver(&ov5648_driver);
> +}
> +
> +module_init(init_ov5648);
> +module_exit(exit_ov5648);

module_i2c_driver();

> +#ifndef __OV5648_H__
> +#define __OV5648_H__

+ empty line.

> +#include <linux/kernel.h>
> +#include <linux/types.h>
> +#include <linux/i2c.h>
> +#include <linux/delay.h>
> +#include <linux/videodev2.h>
> +#include <linux/spinlock.h>
> +#include <media/v4l2-subdev.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <linux/v4l2-mediabus.h>
> +#include <media/media-entity.h>
> +#include <linux/acpi.h>
> +#include  "../include/linux/atomisp_platform.h"

Why? Are they all needed for definitions below?
I'm pretty sure you may leave only couple out of them.

> +
> +#define OV5648_NAME		"ov5648"

Why it's here?

> +static const struct i2c_device_id ov5648_id[] = {
> +	{OV5648_NAME, 0},
> +	{}
> +};

WTF?! It shouldn't be in the header!

-- 
Andy Shevchenko <andriy.shevchenko@...ux.intel.com>
Intel Finland Oy

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ