Re: [PATCH 2/2] drivers: media: video: Add support for Aptina ar0130 sensor

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

 



Hi Prashanth,

Thanks for the patch.

On Fri, Sep 28, 2012 at 07:50:49PM +0530, Prashanth Subramanya wrote:
> This driver adds basic support for Aptina ar0130 1.2M sensor.
> 
> Changes for v2:
> 1: Include new test pattern control as pointed by Hans and Lad.
> 2: Remove soc_camera.h as suggested by Guennadi.
> 3: Change auto exposure control as pointed by Dan Rittersdorf.
> 4: Change incorrect return value as pointed by Nicolas.
> 5: Change crop and binning settings.
> 
> Signed-off-by: Prashanth Subramanya <sprashanth@xxxxxxxxxx>
> ---
>  drivers/media/video/Kconfig       |    7 +
>  drivers/media/video/Makefile      |    1 +
>  drivers/media/video/ar0130.c      | 1088 +++++++++++++++++++++++++++++++++++++
>  drivers/media/video/ar0130_regs.h |  107 ++++
>  include/media/ar0130.h            |   52 ++
>  include/media/v4l2-chip-ident.h   |    1 +
>  6 files changed, 1256 insertions(+)
>  create mode 100644 drivers/media/video/ar0130.c
>  create mode 100644 drivers/media/video/ar0130_regs.h
>  create mode 100644 include/media/ar0130.h
> 
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index c128fac..821c021 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -505,6 +505,13 @@ config VIDEO_VS6624
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called vs6624.
>  
> +config VIDEO_AR0130
> +	tristate "Aptina AR0130 support"
> +	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> +	---help---
> +	  This is a Video4Linux2 sensor-level driver for the Aptina
> +	  ar0130 1.2 Mpixel camera.
> +
>  config VIDEO_MT9M032
>  	tristate "MT9M032 camera sensor support"
>  	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index b7da9fa..4f97e30 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -71,6 +71,7 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
>  obj-$(CONFIG_VIDEO_OV7670) 	+= ov7670.o
>  obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
>  obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
> +obj-$(CONFIG_VIDEO_AR0130) += ar0130.o
>  obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
>  obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
>  obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o
> diff --git a/drivers/media/video/ar0130.c b/drivers/media/video/ar0130.c
> new file mode 100644
> index 0000000..1fdbafa
> --- /dev/null
> +++ b/drivers/media/video/ar0130.c
> @@ -0,0 +1,1088 @@
> +/*
> + * drivers/media/video/ar0130.c
> + *
> + * Aptina AR0130 sensor driver
> + *
> + * Copyright (C) 2012 Aptina Imaging
> + *
> + * Contributor Prashanth Subramanya <sprashanth@xxxxxxxxxx>
> + *
> + * Based on MT9P031 driver
> + *
> + * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + *
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/i2c.h>
> +#include <linux/log2.h>
> +#include <linux/pm.h>
> +#include <linux/slab.h>
> +#include <media/v4l2-subdev.h>
> +#include <linux/videodev2.h>
> +#include <linux/module.h>
> +
> +#include <media/ar0130.h>
> +#include <media/v4l2-chip-ident.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-subdev.h>
> +#include "ar0130_regs.h"
> +
> +#define	AR0130_ROW_START_MIN		0
> +#define	AR0130_ROW_START_MAX		1280
> +#define	AR0130_ROW_START_DEF		0
> +#define	AR0130_COLUMN_START_MIN		0
> +#define	AR0130_COLUMN_START_MAX		960
> +#define	AR0130_COLUMN_START_DEF		0
> +#define	AR0130_WINDOW_HEIGHT_MIN	360
> +#define	AR0130_WINDOW_HEIGHT_MAX	960
> +#define	AR0130_WINDOW_HEIGHT_DEF	960
> +#define	AR0130_WINDOW_WIDTH_MIN		640
> +#define	AR0130_WINDOW_WIDTH_MAX		1280
> +#define	AR0130_WINDOW_WIDTH_DEF		1280
> +
> +#define AR0130_VGA_WIDTH		640
> +#define AR0130_VGA_HEIGHT		480
> +#define AR0130_ENABLE			1
> +#define AR0130_DISABLE			0
> +
> +#define AR0130_CHIP_VERSION_REG		0x3000
> +#define AR0130_CHIP_ID			0x2402
> +#define AR0130_RESET_REG		0x301A
> +#define AR0130_STREAM_ON		0x10DC
> +#define AR0130_STREAM_OFF		0x10D8
> +#define AR0130_SEQ_PORT			0x3086
> +#define AR0130_SEQ_PORT_CTRL		0x3088
> +#define AR0130_TEST_REG			0x3070
> +#define	AR0130_TEST_PATTERN_DISABLE	0x0000
> +
> +#define AR0130_DCDS_PROG_START_ADDR	0x309E
> +#define AR0130_ADC_BITS_6_7		0x30E4
> +#define AR0130_ADC_BITS_4_5		0x30E2
> +#define AR0130_ADC_BITS_2_3		0x30E0
> +#define AR0130_ADC_CONFIG1		0x30E6
> +#define AR0130_ADC_CONFIG2		0x30E8
> +
> +#define AR0130_VT_SYS_CLK_DIV		0x302C
> +#define AR0130_VT_PIX_CLK_DIV		0x302A
> +#define AR0130_PRE_PLL_CLK_DIV		0x302E
> +#define AR0130_PLL_MULTIPLIER		0x3030
> +#define AR0130_DIGITAL_TEST		0x30B0
> +
> +#define AR0130_OPERATION_MODE_CTRL	0x3082
> +#define AR0130_COLUMN_CORRECTION	0x30D4
> +#define AR0130_DARK_CONTROL		0x3044
> +#define AR0130_DAC_LD_14_15		0x3EDA
> +#define AR0130_DAC_LD_12_13		0x3ED8
> +#define AR0130_COARSE_INTEGRATION_TIME	0x3012
> +
> +#define AR0130_DIGITAL_BINNING		0x3032
> +#define AR0130_HOR_AND_VER_BIN		0x0002
> +#define AR0130_HOR_BIN			0x0001
> +#define AR0130_Y_ADDR_START		0x3002
> +#define AR0130_X_ADDR_START		0x3004
> +#define AR0130_Y_ADDR_END		0x3006
> +#define AR0130_X_ADDR_END		0x3008
> +#define AR0130_FRAME_LENGTH_LINES	0x300A
> +#define AR0130_LINE_LENGTH_PCK		0x300C
> +#define AR0130_DATAPATH_SELECT		0x306E

Many of the registers are called the same as in SMIA, but the address is
different. Are there more of such sensors, and are the limit / capability
registers defined in SMIA valid for them?

Is the sensor datasheet public?

> +#define AR0130_EMBEDDED_DATA_CTRL	0x3064
> +#define AR0130_AE_CTRL_REG		0x3100
> +#define AR0130_AE_DCG_EXP_HIGH_REG	0x3112
> +#define AR0130_AE_DCG_EXP_LOW_REG	0x3114
> +#define AR0130_AE_DCG_G_FACTOR		0x3116
> +#define AR0130_AE_DCG_G_FACTOR_INV	0x3118
> +#define AR0130_AE_LUMA_TARGET_REG	0x3102
> +#define AR0130_AE_HIST_TARGET_REG	0x3104
> +#define AR0130_AE_ALPHA_V1_REG		0x3126
> +#define AR0130_AE_MAX_EXPOSURE_REG	0x311C
> +#define AR0130_AE_MIN_EXPOSURE_REG	0x311E
> +#define AR0130_HDR_COMP			0x31D0
> +
> +#define	AR0130_READ_MODE		0x3040
> +#define AR0130_HFLIP_ENABLE		0x4000
> +#define AR0130_HFLIP_DISABLE		0xBFFF
> +#define AR0130_VFLIP_ENABLE		0x8000
> +#define AR0130_VFLIP_DISABLE		0x7FFF
> +
> +#define AR0130_GLOBAL_GAIN_MIN		0x00
> +#define AR0130_GLOBAL_GAIN_MAX		0x11
> +#define AR0130_GLOBAL_GAIN_DEF		0x01
> +
> +struct ar0130_pll_divs {
> +	u32 ext_freq;
> +	u32 target_freq;
> +	u8 m;
> +	u8 n;
> +	u8 p1;
> +	u8 p2;
> +};
> +
> +struct ar0130_frame_size {
> +	u16 width;
> +	u16 height;
> +};
> +
> +static const struct ar0130_frame_size ar0130_supported_framesizes[] = {
> +	{  640,  360 },
> +	{  640,  480 },
> +	{ 1280,  720 },
> +	{ 1280,  960 }

Unless I'm mistaken, the sensor would support much more.

Is the digital crop supported before binning btw.?

What I'm thinking is whether it'd make sense to use the smiapp driver for
this sensor. There's a lot in common, but also much is different, such as
the register addresses.

> +};
> +
> +enum ar0130_resolution {
> +	AR0130_640x360_BINNED,
> +	AR0130_640x480_BINNED,
> +	AR0130_720P_60FPS,
> +	AR0130_FULL_RES_45FPS
> +};
> +
> +struct ar0130_priv {
> +	struct v4l2_subdev subdev;
> +	struct media_pad pad;
> +	struct v4l2_rect crop;  /* Sensor window */
> +	struct v4l2_mbus_framefmt format;
> +	enum ar0130_resolution res_index;
> +	struct v4l2_ctrl_handler ctrls;
> +	struct ar0130_platform_data *pdata;
> +	struct mutex power_lock; /* lock to protect power_count */
> +	const struct ar0130_pll_divs *pll;
> +	int power_count;
> +};
> +
> +struct ar0130_reg {
> +	u16 addr;
> +	u16 data;
> +};
> +
> +/************************************************************************
> +			Helper Functions
> +************************************************************************/
> +/**
> + * to_ar0130 - A helper function which returns pointer to the
> + * private data structure
> + * @client: pointer to i2c client
> + *
> + */
> +static struct ar0130_priv *to_ar0130(const struct i2c_client *client)
> +{
> +	return container_of(i2c_get_clientdata(client),
> +			struct ar0130_priv, subdev);
> +}
> +
> +/**
> + * ar0130_read - reads the data from the given register
> + * @client: pointer to i2c client
> + * @command: address of the register which is to be read
> + *
> + */
> +static int ar0130_read(struct i2c_client *client, u16 command)
> +{
> +	struct i2c_msg msg[2];
> +	u8 buf[2];
> +	u16 ret;
> +
> +	/* 16 bit addressable register */
> +	command = swab16(command);
> +
> +	msg[0].addr  = client->addr;
> +	msg[0].flags = 0;
> +	msg[0].len   = 2;
> +	msg[0].buf   = (u8 *)&command;
> +
> +	msg[1].addr  = client->addr;
> +	msg[1].flags = I2C_M_RD; /* 1 */
> +	msg[1].len   = 2;
> +	msg[1].buf   = buf;
> +
> +	/*
> +	* if return value of this function is < 0,
> +	* it means error.
> +	* else, under 16bit is valid data.
> +	*/
> +	ret = i2c_transfer(client->adapter, msg, 2);
> +
> +	if (ret < 0) {
> +		v4l_err(client, "Read from offset 0x%x error %d",
> +					swab16(command), ret);
> +		return ret;
> +	}
> +
> +	memcpy(&ret, buf, 2);
> +	return swab16(ret);
> +}
> +
> +/**
> + * ar0130_write - writes the data into the given register
> + * @client: pointer to i2c client
> + * @command: address of the register in which to write
> + * @data: data to be written into the register
> + *
> + */
> +static int ar0130_write(struct i2c_client *client, u16 command,
> +				u16 data)
> +{
> +	struct i2c_msg msg;
> +	u8 buf[4];
> +	int ret;
> +
> +	/* 16-bit addressable register */
> +
> +	command = swab16(command);
> +	data = swab16(data);
> +
> +	memcpy(buf + 0, &command, 2);
> +	memcpy(buf + 2, &data, 2);
> +	msg.addr  = client->addr;
> +	msg.flags = 0;
> +	msg.len   = 4;
> +	msg.buf   = buf;
> +
> +	/* i2c_transfer returns message length, but function should return 0 */
> +	ret = i2c_transfer(client->adapter, &msg, 1);
> +	if (ret >= 0)
> +		return 0;
> +
> +	v4l_err(client, "Write failed at 0x%X error %d\n",
> +				swab16(command), ret);
> +	return ret;
> +}
> +
> +/**
> + * ar0130_calc_size - Find the best match for a requested image capture size
> + * @request_width: requested image width in pixels
> + * @request_height: requested image height in pixels
> + *
> + * Find the best match for a requested image capture size.  The best match
> + * is chosen as the nearest match that has the same number or fewer pixels
> + * as the requested size, or the smallest image size if the requested size
> + * has fewer pixels than the smallest image.
> + */
> +static int ar0130_calc_size(unsigned int request_width,
> +				unsigned int request_height)
> +{
> +	int i = 0;
> +	unsigned long requested_pixels = request_width * request_height;
> +
> +	for (i = 0; i < ARRAY_SIZE(ar0130_supported_framesizes); i++) {
> +		if (ar0130_supported_framesizes[i].height *
> +		ar0130_supported_framesizes[i].width >= requested_pixels)
> +			return i;
> +	}
> +
> +	/* couldn't find a match, return the max size as a default */
> +	return ARRAY_SIZE(ar0130_supported_framesizes) - 1;
> +}
> +
> +/**
> + * ar0130_v4l2_try_fmt_cap - Find the best match for a requested image size
> + * @requestedsize: pointer to the structure which contains requested image size
> + *
> + * Find the best match for a requested image capture size.  The best match
> + * is chosen as the nearest match that has the same number or fewer pixels
> + * as the requested size, or the smallest image size if the requested size
> + * has fewer pixels than the smallest image.
> + */
> +static int ar0130_v4l2_try_fmt_cap(struct ar0130_frame_size *requestedsize)
> +{
> +	int isize;
> +
> +	isize = ar0130_calc_size(requestedsize->width, requestedsize->height);
> +
> +	requestedsize->width = ar0130_supported_framesizes[isize].width;
> +	requestedsize->height = ar0130_supported_framesizes[isize].height;
> +
> +	return isize;
> +}
> +
> +/**
> + * ar0130_reset - Soft resets the sensor
> + * @client: pointer to the i2c client
> + *
> + */
> +static int ar0130_reset(struct i2c_client *client)
> +{
> +	int ret;
> +
> +	ret = ar0130_write(client, AR0130_RESET_REG, 0x0001);
> +	if (ret < 0)
> +		return ret;
> +
> +	mdelay(10);

msleep(), please. Or usleep_range(). mdelay() is a busy loop.

> +	ret = ar0130_write(client, AR0130_RESET_REG, AR0130_STREAM_OFF);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +/**
> + * PLL Dividers
> + *
> + * Calculated according to the following formula:
> + *
> + *    target_freq = (ext_freq x M) / (N x P1 x P2)
> + *    VCO_freq    = (ext_freq x M) / N
> + *
> + * And subject to the following limitations:
> + *
> + *    Limitations of PLL parameters
> + *    -----------------------------
> + *    32     ≤ M        ≤ 384
> + *    1      ≤ N        ≤ 64
> + *    1      ≤ P1       ≤ 16
> + *    4      ≤ P2       ≤ 16
> + *    384MHz ≤ VCO_freq ≤ 768MHz
> + *
> + * TODO: Use Aptina PLL Helper module to calculate dividers
> + */
> +
> +static const struct ar0130_pll_divs ar0130_divs[] = {
> +	/* ext_freq	target_freq	M	N	p1	p2 */
> +	{24000000,	48000000,	32,	2,	2,	4},
> +	{24000000,	66000000,	44,	2,	2,	4},
> +	{48000000,	48000000,	40,	5,	2,	4}
> +};

Do you think you could use the smiapp-pll PLL calculator, as your pll looks
similar to that? Here you're making a lot of assumptions you wouldn't have
to make.

> +
> +static int ar0130_pll_get_divs(struct ar0130_priv *ar0130)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&ar0130->subdev);
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(ar0130_divs); i++) {
> +		if (ar0130_divs[i].ext_freq == ar0130->pdata->ext_freq &&
> +		ar0130_divs[i].target_freq == ar0130->pdata->target_freq) {
> +			ar0130->pll = &ar0130_divs[i];
> +			return 0;
> +		}
> +	}
> +	dev_err(&client->dev, "Couldn't find PLL dividers for ext_freq = %d, target_freq = %d\n",
> +			ar0130->pdata->ext_freq, ar0130->pdata->target_freq);
> +
> +	return -EINVAL;
> +}
> +
> +/**
> + * ar0130_pll_enable - enable the sensor pll
> + * @client: pointer to the i2c client
> + *
> + */
> +static int ar0130_pll_enable(struct ar0130_priv *ar0130)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&ar0130->subdev);
> +	int ret;
> +
> +	ret = ar0130_pll_get_divs(ar0130);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret  = ar0130_write(client, AR0130_VT_SYS_CLK_DIV, ar0130->pll->p1);
> +	ret |= ar0130_write(client, AR0130_VT_PIX_CLK_DIV, ar0130->pll->p2);
> +	ret |= ar0130_write(client, AR0130_PRE_PLL_CLK_DIV, ar0130->pll->n);
> +	ret |= ar0130_write(client, AR0130_PLL_MULTIPLIER, ar0130->pll->m);
> +
> +	mdelay(100);

This must be msleep() instead.

> +	return ret;
> +}
> +
> +/**
> + * ar0130_power_on - power on the sensor
> + * @ar0130: pointer to private data structure
> + *
> + */
> +static int ar0130_power_on(struct ar0130_priv *ar0130)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(&ar0130->subdev);
> +	int ret;
> +
> +	/* Ensure RESET_BAR is low */
> +	if (ar0130->pdata->reset) {
> +		ar0130->pdata->reset(&ar0130->subdev, 1);
> +		usleep_range(1000, 2000);
> +	}
> +
> +	/* Enable clock */
> +	if (ar0130->pdata->set_xclk) {
> +		ar0130->pdata->set_xclk(&ar0130->subdev,
> +		ar0130->pdata->ext_freq);
> +		usleep_range(1000, 2000);
> +	}
> +
> +	/* Now RESET_BAR must be high */
> +	if (ar0130->pdata->reset) {
> +		ar0130->pdata->reset(&ar0130->subdev, 0);
> +		usleep_range(1000, 2000);
> +	}
> +
> +	ret = ar0130_reset(client);
> +
> +	if (ret < 0) {
> +		dev_err(&client->dev, "Failed to reset the camera\n");
> +		return ret;
> +	}
> +
> +	return v4l2_ctrl_handler_setup(&ar0130->ctrls);
> +}
> +
> +/**
> + * ar0130_power_off - power off the sensor
> + * @ar0130: pointer to private data structure
> + *
> + */
> +void ar0130_power_off(struct ar0130_priv *ar0130)
> +{
> +	if (ar0130->pdata->set_xclk)
> +		ar0130->pdata->set_xclk(&ar0130->subdev, 0);
> +}
> +
> +static struct v4l2_mbus_framefmt *
> +__ar0130_get_pad_format(struct ar0130_priv *ar0130, struct v4l2_subdev_fh *fh,
> +			unsigned int pad, u32 which)
> +{
> +	switch (which) {
> +	case V4L2_SUBDEV_FORMAT_TRY:
> +		return v4l2_subdev_get_try_format(fh, pad);
> +	case V4L2_SUBDEV_FORMAT_ACTIVE:
> +		return &ar0130->format;
> +	default:
> +		return NULL;
> +	}
> +}
> +
> +static int ar0130_linear_mode_setup(struct i2c_client *client)
> +{
> +	int i, ret;
> +
> +	ret = ar0130_write(client, AR0130_SEQ_PORT_CTRL, 0x8000);
> +	for (i = 0; i < 80; i++)
> +		ret |= ar0130_write(client, AR0130_SEQ_PORT,
> +					ar0130_linear_data[i]);
> +
> +	ret |= ar0130_write(client, AR0130_DCDS_PROG_START_ADDR, 0x0000);
> +	ret |= ar0130_write(client, AR0130_ADC_BITS_6_7, 0x6372);
> +	ret |= ar0130_write(client, AR0130_ADC_BITS_4_5, 0x7253);
> +	ret |= ar0130_write(client, AR0130_ADC_BITS_2_3, 0x5470);
> +	ret |= ar0130_write(client, AR0130_ADC_CONFIG1, 0xC4CC);
> +	ret |= ar0130_write(client, AR0130_ADC_CONFIG2, 0x8050);
> +
> +	mdelay(200);

msleep().

> +	ret |= ar0130_write(client, AR0130_OPERATION_MODE_CTRL, 0x0029);
> +	ret |= ar0130_write(client, AR0130_DIGITAL_TEST, 0x1300);
> +	ret |= ar0130_write(client, AR0130_COLUMN_CORRECTION, 0xE007);
> +	ret |= ar0130_write(client, AR0130_RESET_REG, 0x10DC);
> +	ret |= ar0130_write(client, AR0130_RESET_REG, 0x10D8);
> +	ret |= ar0130_write(client, AR0130_DARK_CONTROL, 0x0400);
> +	ret |= ar0130_write(client, AR0130_DAC_LD_14_15, 0x0F03);
> +	ret |= ar0130_write(client, AR0130_DAC_LD_12_13, 0x01EF);
> +	ret |= ar0130_write(client, AR0130_COARSE_INTEGRATION_TIME, 0x02A0);
> +

You should always return the error you've gotten if you do get one.

This is done elsewhere, too.

> +	return ret;
> +}
> +
> +static int ar0130_set_resolution(struct i2c_client *client)
> +{
> +	struct ar0130_priv *ar0130 = to_ar0130(client);
> +	int ret = 0;
> +	unsigned int hratio, vratio;
> +
> +	hratio = DIV_ROUND_CLOSEST(ar0130->crop.width, ar0130->format.width);
> +	vratio = DIV_ROUND_CLOSEST(ar0130->crop.height, ar0130->format.height);
> +	if (hratio == 2) {
> +		if (vratio == 2)
> +			ret = ar0130_write(client, AR0130_DIGITAL_BINNING,
> +					AR0130_HOR_AND_VER_BIN);
> +		else if (vratio < 2)
> +			ret = ar0130_write(client, AR0130_DIGITAL_BINNING,
> +					AR0130_HOR_BIN);
> +	}
> +
> +	ret |= ar0130_write(client, AR0130_X_ADDR_START, ar0130->crop.top);
> +	ret |= ar0130_write(client, AR0130_X_ADDR_END,
> +				ar0130->crop.top + ar0130->crop.width - 1);
> +	ret |= ar0130_write(client, AR0130_Y_ADDR_START, ar0130->crop.left);
> +	ret |= ar0130_write(client, AR0130_Y_ADDR_END,
> +				ar0130->crop.left + ar0130->crop.height - 1);
> +	ret |= ar0130_write(client, AR0130_LINE_LENGTH_PCK, 1650);
> +	ret |= ar0130_write(client, AR0130_FRAME_LENGTH_LINES,
> +		ar0130->crop.height + 29);
> +
> +	return ret;
> +}
> +
> +static int ar0130_set_autoexposure(struct i2c_client *client,
> +					enum v4l2_exposure_auto_type ae_mode)
> +{
> +	int ret = 0;
> +
> +	switch (ae_mode) {
> +	case V4L2_EXPOSURE_AUTO:
> +		ret  = ar0130_write(client, AR0130_EMBEDDED_DATA_CTRL, 0x1982);
> +		ret |= ar0130_write(client, AR0130_AE_CTRL_REG, 0x001B);
> +		ret |= ar0130_write(client, AR0130_AE_DCG_EXP_HIGH_REG, 0x029F);
> +		ret |= ar0130_write(client, AR0130_AE_DCG_EXP_LOW_REG, 0x008C);
> +		ret |= ar0130_write(client, AR0130_AE_DCG_G_FACTOR, 0x02C0);
> +		ret |= ar0130_write(client, AR0130_AE_DCG_G_FACTOR_INV, 0x005B);
> +		ret |= ar0130_write(client, AR0130_AE_LUMA_TARGET_REG, 0x0384);
> +		ret |= ar0130_write(client, AR0130_AE_HIST_TARGET_REG, 0x1000);
> +		ret |= ar0130_write(client, AR0130_AE_ALPHA_V1_REG, 0x0080);
> +		ret |= ar0130_write(client, AR0130_AE_MAX_EXPOSURE_REG, 0x03DD);
> +		ret |= ar0130_write(client, AR0130_AE_MIN_EXPOSURE_REG, 0x0002);
> +		return ret;
> +
> +	case V4L2_EXPOSURE_MANUAL:
> +		ret = ar0130_write(client, AR0130_RESET_REG, AR0130_STREAM_OFF);
> +		ret |= ar0130_write(client, AR0130_AE_CTRL_REG, 0x001A);
> +		ret |= ar0130_write(client, AR0130_RESET_REG, AR0130_STREAM_ON);
> +		return ret;
> +
> +	case V4L2_EXPOSURE_SHUTTER_PRIORITY:
> +	case V4L2_EXPOSURE_APERTURE_PRIORITY:
> +		dev_err(&client->dev, "Unsupported auto-exposure mode requested: %d\n",
> +			ae_mode);
> +		ret = -EINVAL;
> +		break;
> +
> +	default:
> +		dev_err(&client->dev, "Auto Exposure mode out of range: %d\n",
> +			ae_mode);
> +		ret = -ERANGE;
> +		break;
> +	}
> +	return ret;
> +}
> +
> +/************************************************************************
> +			v4l2_subdev_core_ops
> +************************************************************************/
> +
> +static int ar0130_g_chip_ident(struct v4l2_subdev *sd,
> +				struct v4l2_dbg_chip_ident *id)
> +{
> +	id->ident    = V4L2_IDENT_AR0130;
> +	id->revision = 1;
> +
> +	return 0;
> +}
> +
> +static int ar0130_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct ar0130_priv *ar0130 = container_of(ctrl->handler,
> +					struct ar0130_priv, ctrls);
> +	struct i2c_client *client = v4l2_get_subdevdata(&ar0130->subdev);
> +	int ret = 0;
> +	u16 reg16;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_EXPOSURE_AUTO:
> +		return ar0130_set_autoexposure(client,
> +				(enum v4l2_exposure_auto_type)ctrl->val);
> +
> +	case V4L2_CID_GAIN:
> +		reg16  = ar0130_read(client, AR0130_DIGITAL_TEST);
> +		reg16 |= (ctrl->val << 5);
> +		return ar0130_write(client, AR0130_DIGITAL_TEST, reg16);
> +
> +	case V4L2_CID_HFLIP:
> +		if (ctrl->val) {
> +			reg16 = ar0130_read(client, AR0130_READ_MODE);
> +			reg16 |= AR0130_HFLIP_ENABLE;
> +			return ar0130_write(client, AR0130_READ_MODE, reg16);
> +		}
> +		reg16 = ar0130_read(client, AR0130_READ_MODE);
> +		reg16 &= AR0130_HFLIP_DISABLE;
> +		return ar0130_write(client, AR0130_READ_MODE, reg16);
> +
> +	case V4L2_CID_VFLIP:
> +		if (ctrl->val) {
> +			reg16 = ar0130_read(client, AR0130_READ_MODE);
> +			reg16 |= AR0130_VFLIP_ENABLE;
> +			return ar0130_write(client, AR0130_READ_MODE, reg16);
> +		}
> +		reg16 = ar0130_read(client, AR0130_READ_MODE);
> +		reg16 &= AR0130_VFLIP_DISABLE;
> +		return ar0130_write(client, AR0130_READ_MODE, reg16);
> +
> +	case V4L2_CID_TEST_PATTERN:
> +		if (!ctrl->val)
> +			return ar0130_write(client, AR0130_TEST_REG,
> +						AR0130_TEST_PATTERN_DISABLE);
> +		else if (ctrl->val < 4)
> +			return ar0130_write(client, AR0130_TEST_REG, ctrl->val);
> +
> +		return ar0130_write(client, AR0130_TEST_REG, 256);
> +
> +	default:
> +		dev_err(&client->dev, "Control %d not supported\n", ctrl->id);
> +		ret = -ERANGE;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static struct v4l2_ctrl_ops ar0130_ctrl_ops = {
> +	.s_ctrl = ar0130_s_ctrl,
> +};
> +
> +/*
> +@AR0130_TEST_PATTERN
> +0 = Disabled. Normal operation. Generate output data from pixel array
> +1 = Solid color test pattern.
> +2 = Full color bar test pattern
> +3 = Fade to grey color bar test pattern
> +256 = Marching 1s test pattern (12 bit)
> +*/
> +static const char * const ar0130_test_pattern[] = {
> +	"Disabled",
> +	"Solid color",
> +	"Full color bar",
> +	"Fade to grey color bar",
> +	"Marching 1s",
> +	NULL
> +};
> +
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +static int ar0130_g_reg(struct v4l2_subdev *sd,
> +				struct v4l2_dbg_register *reg)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	u16 data;
> +
> +	reg->size = 2;
> +	data = ar0130_read(client, reg->reg);
> +
> +	reg->val = (__u64)data;
> +	return 0;
> +}
> +
> +static int ar0130_s_reg(struct v4l2_subdev *sd,
> +				struct v4l2_dbg_register *reg)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +
> +	return ar0130_write(client, reg->reg, reg->val);
> +}
> +#endif
> +
> +static int ar0130_s_power(struct v4l2_subdev *sd, int on)
> +{
> +	struct ar0130_priv *ar0130 = container_of(sd,
> +				struct ar0130_priv, subdev);
> +	int ret = 0;
> +
> +	mutex_lock(&ar0130->power_lock);
> +
> +	/*
> +	* If the power count is modified from 0 to != 0 or from != 0 to 0,
> +	* update the power state.
> +	*/
> +	if (ar0130->power_count == !on) {
> +		if (on) {
> +			ret = ar0130_power_on(ar0130);
> +			if (ret) {
> +				dev_err(ar0130->subdev.v4l2_dev->dev,
> +				"Failed to power on: %d\n", ret);
> +				goto out;
> +			}
> +		} else
> +			ar0130_power_off(ar0130);
> +	}
> +	/* Update the power count. */
> +	ar0130->power_count += on ? 1 : -1;
> +	WARN_ON(ar0130->power_count < 0);
> +out:
> +	mutex_unlock(&ar0130->power_lock);
> +	return ret;
> +}
> +
> +/***************************************************
> +		v4l2_subdev_video_ops
> +****************************************************/
> +static int ar0130_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	struct ar0130_priv *ar0130 = container_of(sd,
> +					struct ar0130_priv, subdev);
> +	int ret;
> +
> +	if (!enable) {
> +		ret = ar0130_write(client, AR0130_RESET_REG, AR0130_STREAM_OFF);
> +		return 0;
> +	}
> +
> +	ret = ar0130_linear_mode_setup(client);
> +	if (ret < 0) {
> +		dev_err(ar0130->subdev.v4l2_dev->dev,
> +			"Failed to setup linear mode: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = ar0130_set_resolution(client);
> +	if (ret < 0) {
> +		dev_err(ar0130->subdev.v4l2_dev->dev,
> +			"Failed to setup resolution: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret  = ar0130_write(client, AR0130_RESET_REG, AR0130_STREAM_OFF);
> +	ret |= ar0130_write(client, AR0130_HDR_COMP, 0x0001);
> +
> +	ret |= ar0130_pll_enable(ar0130);
> +	if (ret < 0) {
> +		dev_err(ar0130->subdev.v4l2_dev->dev,
> +			"Failed to enable pll: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret |= ar0130_write(client, AR0130_RESET_REG, AR0130_STREAM_ON);
> +
> +	return ret;
> +}
> +
> +/***************************************************
> +		v4l2_subdev_pad_ops
> +****************************************************/
> +static int ar0130_enum_mbus_code(struct v4l2_subdev *sd,
> +				struct v4l2_subdev_fh *fh,
> +				struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	struct ar0130_priv *ar0130 = container_of(sd,
> +					struct ar0130_priv, subdev);
> +
> +	if (code->pad || code->index)
> +		return -EINVAL;
> +
> +	code->code = ar0130->format.code;
> +	return 0;
> +}
> +
> +static int ar0130_enum_frame_size(struct v4l2_subdev *sd,
> +				struct v4l2_subdev_fh *fh,
> +				struct v4l2_subdev_frame_size_enum *fse)
> +{
> +	struct ar0130_priv *ar0130 = container_of(sd,
> +					struct ar0130_priv, subdev);
> +
> +	if (fse->index >= 8 || fse->code != ar0130->format.code)
> +		return -EINVAL;
> +
> +	fse->min_width = AR0130_WINDOW_WIDTH_DEF
> +				/ min_t(unsigned int, 7, fse->index + 1);
> +	fse->max_width = fse->min_width;
> +	fse->min_height = AR0130_WINDOW_HEIGHT_DEF / (fse->index + 1);
> +	fse->max_height = fse->min_height;
> +
> +	return 0;
> +}
> +
> +static int ar0130_get_format(struct v4l2_subdev *sd,
> +				struct v4l2_subdev_fh *fh,
> +				struct v4l2_subdev_format *fmt)
> +{
> +	struct ar0130_priv *ar0130 = container_of(sd,
> +					struct ar0130_priv, subdev);
> +
> +	fmt->format = *__ar0130_get_pad_format(ar0130, fh, fmt->pad,
> +						fmt->which);
> +
> +	return 0;
> +}
> +
> +static int ar0130_set_format(struct v4l2_subdev *sd,
> +				struct v4l2_subdev_fh *fh,
> +				struct v4l2_subdev_format *format)
> +{
> +	struct ar0130_priv *ar0130 = container_of(sd,
> +					struct ar0130_priv, subdev);
> +	struct ar0130_frame_size size;
> +
> +	size.height = format->format.height;
> +	size.width = format->format.width;
> +	ar0130->res_index		= ar0130_v4l2_try_fmt_cap(&size);
> +	ar0130->format.width		= size.width;
> +	ar0130->format.height		= size.height;
> +	ar0130->format.code		= V4L2_MBUS_FMT_SGRBG12_1X12;
> +
> +	format->format.width		= size.width;
> +	format->format.height		= size.height;
> +
> +	return 0;
> +}
> +
> +static struct v4l2_rect *
> +__ar0130_get_pad_crop(struct ar0130_priv *ar0130, struct v4l2_subdev_fh *fh,
> +	unsigned int pad, u32 which)
> +{
> +	switch (which) {
> +	case V4L2_SUBDEV_FORMAT_TRY:
> +		return v4l2_subdev_get_try_crop(fh, pad);
> +	case V4L2_SUBDEV_FORMAT_ACTIVE:
> +		return &ar0130->crop;
> +	default:
> +		return NULL;
> +	}
> +}
> +
> +static int ar0130_get_crop(struct v4l2_subdev *sd,
> +			struct v4l2_subdev_fh *fh,
> +			struct v4l2_subdev_crop *crop)
> +{
> +	struct ar0130_priv *ar0130 = container_of(sd,
> +					struct ar0130_priv, subdev);
> +
> +	crop->rect = *__ar0130_get_pad_crop(ar0130, fh, crop->pad, crop->which);
> +
> +	return 0;
> +}
> +
> +static int ar0130_set_crop(struct v4l2_subdev *sd,
> +			struct v4l2_subdev_fh *fh,
> +			struct v4l2_subdev_crop *crop)
> +{
> +	struct ar0130_priv *ar0130 = container_of(sd,
> +					struct ar0130_priv, subdev);
> +	struct ar0130_frame_size size;
> +
> +	size.width = crop->rect.width;
> +	size.height = crop->rect.height;
> +	ar0130_v4l2_try_fmt_cap(&size);
> +
> +	if (crop->rect.left < AR0130_COLUMN_START_MIN)
> +		crop->rect.left = AR0130_COLUMN_START_MIN;
> +	else if (crop->rect.left > AR0130_COLUMN_START_MAX)
> +		crop->rect.left = AR0130_COLUMN_START_MAX;
> +	if (crop->rect.top < AR0130_ROW_START_MIN)
> +		crop->rect.top = AR0130_ROW_START_MIN;
> +	else if (crop->rect.top > AR0130_ROW_START_MAX)
> +		crop->rect.top = AR0130_ROW_START_MAX;
> +
> +	ar0130->crop.left	= crop->rect.left;
> +	ar0130->crop.top	= crop->rect.top;
> +	ar0130->crop.width	= size.width;
> +	ar0130->crop.height	= size.height;
> +
> +	crop->rect.width	= size.width;
> +	crop->rect.height	= size.height;
> +
> +	return 0;
> +}
> +
> +/***********************************************************
> +	V4L2 subdev internal operations
> +************************************************************/
> +static int ar0130_registered(struct v4l2_subdev *sd)
> +{
> +	struct i2c_client *client = v4l2_get_subdevdata(sd);
> +	struct ar0130_priv *ar0130 = to_ar0130(client);
> +	s32 data;
> +	int ret;
> +
> +	ret = ar0130_power_on(ar0130);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "AR0130 power up failed\n");
> +		return ret;
> +	}
> +
> +	/* Read out the chip version register */
> +	data = ar0130_read(client, AR0130_CHIP_VERSION_REG);
> +	if (data != AR0130_CHIP_ID) {
> +		dev_err(&client->dev, "AR0130 not detected, chip ID read:0x%4.4X\n",
> +				data);
> +		return -ENODEV;
> +	}
> +
> +	dev_info(&client->dev, "AR0130 detected at address 0x%02X:chip ID = 0x%4.4X\n",
> +			client->addr, AR0130_CHIP_ID);
> +
> +	ar0130_power_off(ar0130);
> +
> +	return ret;
> +}
> +
> +static int ar0130_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	return ar0130_s_power(sd, 1);
> +}
> +
> +static int ar0130_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	return ar0130_s_power(sd, 0);
> +}
> +
> +/***************************************************
> +		v4l2_subdev_ops
> +****************************************************/
> +static struct v4l2_subdev_core_ops ar0130_subdev_core_ops = {
> +	.g_chip_ident	= ar0130_g_chip_ident,
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +	.g_register	= ar0130_g_reg,
> +	.s_register	= ar0130_s_reg,
> +#endif
> +	.s_power	= ar0130_s_power,
> +};
> +
> +static struct v4l2_subdev_video_ops ar0130_subdev_video_ops = {
> +	.s_stream	= ar0130_s_stream,
> +};
> +
> +static struct v4l2_subdev_pad_ops ar0130_subdev_pad_ops = {
> +	.enum_mbus_code	 = ar0130_enum_mbus_code,
> +	.enum_frame_size = ar0130_enum_frame_size,
> +	.get_fmt	 = ar0130_get_format,
> +	.set_fmt	 = ar0130_set_format,
> +	.get_crop	 = ar0130_get_crop,
> +	.set_crop	 = ar0130_set_crop,
> +};
> +
> +static struct v4l2_subdev_ops ar0130_subdev_ops = {
> +	.core	= &ar0130_subdev_core_ops,
> +	.video	= &ar0130_subdev_video_ops,
> +	.pad	= &ar0130_subdev_pad_ops,
> +};
> +
> +/*
> + * Internal ops. Never call this from drivers, only the v4l2 framework can call
> + * these ops.
> + */
> +static const struct v4l2_subdev_internal_ops ar0130_subdev_internal_ops = {
> +	.registered	= ar0130_registered,
> +	.open		= ar0130_open,
> +	.close		= ar0130_close,
> +};
> +
> +/***************************************************
> +		I2C driver
> +****************************************************/
> +static int ar0130_probe(struct i2c_client *client,
> +			const struct i2c_device_id *did)
> +{
> +	struct ar0130_platform_data *pdata = client->dev.platform_data;
> +	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
> +	struct ar0130_priv *ar0130;
> +	struct v4l2_ctrl *ar0130_test_ctrl;
> +	int ret;
> +
> +	if (pdata == NULL) {
> +		dev_err(&client->dev, "No platform data\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
> +		dev_warn(&client->dev, "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
> +		return -EIO;
> +	}
> +
> +	ar0130 = kzalloc(sizeof(struct ar0130_priv), GFP_KERNEL);
> +	if (ar0130 == NULL)
> +		return -ENOMEM;
> +
> +	ar0130->pdata = pdata;
> +
> +	v4l2_ctrl_handler_init(&ar0130->ctrls, 5);
> +
> +	v4l2_ctrl_new_std(&ar0130->ctrls, &ar0130_ctrl_ops,
> +			V4L2_CID_GAIN, AR0130_GLOBAL_GAIN_MIN,
> +			AR0130_GLOBAL_GAIN_MAX, 1, AR0130_GLOBAL_GAIN_DEF);
> +	v4l2_ctrl_new_std(&ar0130->ctrls, &ar0130_ctrl_ops,
> +				V4L2_CID_HFLIP, 0, 1, 1, 0);
> +	v4l2_ctrl_new_std(&ar0130->ctrls, &ar0130_ctrl_ops,
> +				V4L2_CID_VFLIP, 0, 1, 1, 0);
> +	v4l2_ctrl_new_std_menu(&ar0130->ctrls, &ar0130_ctrl_ops,
> +		V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0,
> +					V4L2_EXPOSURE_AUTO);
> +	ar0130_test_ctrl = v4l2_ctrl_new_std_menu(&ar0130->ctrls,
> +		&ar0130_ctrl_ops, V4L2_CID_TEST_PATTERN, 5, 0,
> +				V4L2_TEST_PATTERN_DISABLED);
> +
> +	if (ar0130->ctrls.error) {
> +		ret = ar0130->ctrls.error;
> +		dev_err(&client->dev, "Control initialization error: %d\n",
> +			ret);
> +		goto done;
> +	}
> +
> +	v4l2_ctrl_modify_menu(ar0130_test_ctrl, ar0130_test_pattern, 5, 0, 0);
> +
> +	mutex_init(&ar0130->power_lock);
> +	v4l2_i2c_subdev_init(&ar0130->subdev, client, &ar0130_subdev_ops);
> +	ar0130->subdev.internal_ops = &ar0130_subdev_internal_ops;
> +	ar0130->subdev.ctrl_handler = &ar0130->ctrls;
> +
> +	ar0130->pad.flags = MEDIA_PAD_FL_SOURCE;
> +	ret = media_entity_init(&ar0130->subdev.entity, 1, &ar0130->pad, 0);
> +	if (ret < 0)
> +		goto done;
> +
> +	ar0130->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> +	ar0130->crop.width	= AR0130_WINDOW_WIDTH_DEF;
> +	ar0130->crop.height	= AR0130_WINDOW_HEIGHT_DEF;
> +	ar0130->crop.left	= AR0130_COLUMN_START_DEF;
> +	ar0130->crop.top	= AR0130_ROW_START_DEF;
> +
> +	ar0130->format.code		= V4L2_MBUS_FMT_SGRBG12_1X12;
> +	ar0130->format.width		= AR0130_WINDOW_WIDTH_DEF;
> +	ar0130->format.height		= AR0130_WINDOW_HEIGHT_DEF;
> +	ar0130->format.field		= V4L2_FIELD_NONE;
> +	ar0130->format.colorspace	= V4L2_COLORSPACE_SRGB;
> +
> +done:
> +	if (ret < 0) {
> +		v4l2_ctrl_handler_free(&ar0130->ctrls);
> +		media_entity_cleanup(&ar0130->subdev.entity);
> +		kfree(ar0130);
> +		dev_err(&client->dev, "Probe failed\n");
> +	}
> +
> +	return ret;
> +}
> +
> +static int ar0130_remove(struct i2c_client *client)
> +{
> +	struct v4l2_subdev *subdev = i2c_get_clientdata(client);
> +	struct ar0130_priv *ar0130 = to_ar0130(client);
> +
> +	v4l2_ctrl_handler_free(&ar0130->ctrls);
> +	v4l2_device_unregister_subdev(subdev);
> +	media_entity_cleanup(&subdev->entity);
> +	kfree(ar0130);
> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id ar0130_id[] = {
> +	{ "ar0130", 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, ar0130_id);
> +
> +static struct i2c_driver ar0130_i2c_driver = {
> +	.driver = {
> +		 .name = "ar0130",
> +	},
> +	.probe    = ar0130_probe,
> +	.remove   = ar0130_remove,
> +	.id_table = ar0130_id,
> +};
> +
> +module_i2c_driver(ar0130_i2c_driver);
> +
> +MODULE_DESCRIPTION("Aptina AR0130 Camera driver");
> +MODULE_AUTHOR("Aptina Imaging <drivers@xxxxxxxxxx>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/media/video/ar0130_regs.h b/drivers/media/video/ar0130_regs.h
> new file mode 100644
> index 0000000..a7a6619
> --- /dev/null
> +++ b/drivers/media/video/ar0130_regs.h
> @@ -0,0 +1,107 @@
> +/*
> + * drivers/media/video/ar0130_regs.h
> + *
> + * Aptina AR0130 sensor related register values
> + *
> + * Copyright (C) 2012 Aptina Imaging
> + *
> + * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + *
> + */
> +
> +#ifndef __AR0130_H__
> +#define __AR0130_H__
> +#endif /* __AR0130_H__ */

Uh, why? Shouln't this line be at the end of the file?

> +
> +static unsigned int ar0130_linear_data[79] = {
> +0x0225,
> +0x5050,
> +0x2D26,
> +0x0828,
> +0x0D17,
> +0x0926,
> +0x0028,
> +0x0526,
> +0xA728,
> +0x0725,
> +0x8080,
> +0x2917,
> +0x0525,
> +0x0040,
> +0x2702,
> +0x1616,
> +0x2706,
> +0x1736,
> +0x26A6,
> +0x1703,
> +0x26A4,
> +0x171F,
> +0x2805,
> +0x2620,
> +0x2804,
> +0x2520,
> +0x2027,
> +0x0017,
> +0x1E25,
> +0x0020,
> +0x2117,
> +0x1028,
> +0x051B,
> +0x1703,
> +0x2706,
> +0x1703,
> +0x1741,
> +0x2660,
> +0x17AE,
> +0x2500,
> +0x9027,
> +0x0026,
> +0x1828,
> +0x002E,
> +0x2A28,
> +0x081E,
> +0x0831,
> +0x1440,
> +0x4014,
> +0x2020,
> +0x1410,
> +0x1034,
> +0x1400,
> +0x1014,
> +0x0020,
> +0x1400,
> +0x4013,
> +0x1802,
> +0x1470,
> +0x7004,
> +0x1470,
> +0x7003,
> +0x1470,
> +0x7017,
> +0x2002,
> +0x1400,
> +0x2002,
> +0x1400,
> +0x5004,
> +0x1400,
> +0x2004,
> +0x1400,
> +0x5022,
> +0x0314,
> +0x0020,
> +0x0314,
> +0x0050,
> +0x2C2C,
> +0x2C2C
> +};
> diff --git a/include/media/ar0130.h b/include/media/ar0130.h
> new file mode 100644
> index 0000000..4a95b64
> --- /dev/null
> +++ b/include/media/ar0130.h
> @@ -0,0 +1,52 @@
> +/*
> + * include/media/ar0130.h
> + *
> + * Aptina AR0130 sensor related register values
> + *
> + * Copyright (C) 2012 Aptina Imaging
> + *
> + * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + *
> + */
> +
> +#ifndef __AR0130_H__
> +#define __AR0130_H__
> +#define AR0130_I2C_ADDR		0x10 /* (0x20 >> 1) */
> +
> +struct v4l2_subdev;
> +
> +enum {
> +	AR0130_COLOR_VERSION,
> +	AR0130_MONOCHROME_VERSION,
> +};
> +
> +/*
> + * struct ar0130_platform_data - AR0130 platform data
> + * @set_xclk: Clock frequency set callback
> + * @reset: Chip reset GPIO (set to -1 if not used)
> + * @ext_freq: Input clock frequency
> + * @target_freq: Pixel clock frequency
> + * @version: color or monochrome
> + * @clk_pol: parallel pixclk polarity
> + */
> +struct ar0130_platform_data {
> +	int (*set_xclk)(struct v4l2_subdev *subdev, int hz);
> +	int (*reset)(struct v4l2_subdev *subdev, int active);

Wouldn't a gpio number suffice?

> +	int ext_freq;
> +	int target_freq;
> +	int version;
> +	unsigned int clk_pol:1;
> +};
> +
> +#endif
> diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
> index 58f914a..d20f19d 100644
> --- a/include/media/v4l2-chip-ident.h
> +++ b/include/media/v4l2-chip-ident.h
> @@ -305,6 +305,7 @@ enum {
>  	V4L2_IDENT_MT9T112		= 45022,
>  	V4L2_IDENT_MT9V111		= 45031,
>  	V4L2_IDENT_MT9V112		= 45032,
> +	V4L2_IDENT_AR0130		= 45040,
>  
>  	/* HV7131R CMOS sensor: just ident 46000 */
>  	V4L2_IDENT_HV7131R		= 46000,

Regards,

-- 
Sakari Ailus
e-mail: sakari.ailus@xxxxxx	XMPP: sailus@xxxxxxxxxxxxxx
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[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