diff mbox

[1/5] MT9P012: Add driver

Message ID A24693684029E5489D1D202277BE89442E1D9220@dlee02.ent.ti.com (mailing list archive)
State Changes Requested, archived
Headers show

Commit Message

Aguirre Rodriguez, Sergio Alberto March 3, 2009, 8:44 p.m. UTC
This driver has been currently being tested with
OMAP3430SDP platform, working in Parallel mode.

Signed-off-by: Sergio Aguirre <saaguirre@ti.com>
---
 drivers/media/video/Kconfig        |    8 +
 drivers/media/video/Makefile       |    1 +
 drivers/media/video/mt9p012.c      | 1890 ++++++++++++++++++++++++++++++++++++
 drivers/media/video/mt9p012_regs.h |   74 ++
 include/media/mt9p012.h            |   37 +
 5 files changed, 2010 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/mt9p012.c
 create mode 100644 drivers/media/video/mt9p012_regs.h
 create mode 100644 include/media/mt9p012.h

--
1.5.6.5

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Felipe Balbi March 4, 2009, 11:30 a.m. UTC | #1
Hi,

not looking at v4l2 part since it's not my area...


On Tue, Mar 03, 2009 at 09:44:14PM +0100, ext Aguirre Rodriguez, Sergio Alberto wrote:
> +#define SENSOR_DETECTED                1
> +#define SENSOR_NOT_DETECTED    0

these two should be unneeded...

> +
> +/**
> + * struct mt9p012_reg - mt9p012 register format
> + * @length: length of the register
> + * @reg: 16-bit offset to register
> + * @val: 8/16/32-bit register value
> + *
> + * Define a structure for MT9P012 register initialization values
> + */
> +struct mt9p012_reg {
> +       u16     length;
> +       u16     reg;
> +       u32     val;
> +};
> +
> +enum image_size {
> +       BIN4XSCALE,
> +       BIN4X,
> +       BIN2X,
> +       THREE_MP,
> +       FIVE_MP

you probably wanna prefix these with MT9P012_ to avoid namespace
conflicts.

> +};
> +
> +enum pixel_format {
> +       RAWBAYER10
> +};
> +
> +#define NUM_IMAGE_SIZES                5
> +#define NUM_PIXEL_FORMATS      1
> +#define NUM_FPS                        2       /* 2 ranges */
> +#define FPS_LOW_RANGE          0
> +#define FPS_HIGH_RANGE         1
> +
> +/**
> + * struct capture_size - image capture size information
> + * @width: image width in pixels
> + * @height: image height in pixels
> + */
> +struct capture_size {
> +       unsigned long width;
> +       unsigned long height;
> +};
> +
> +/**
> + * struct mt9p012_pll_settings - struct for storage of sensor pll values
> + * @vt_pix_clk_div: vertical pixel clock divider
> + * @vt_sys_clk_div: veritcal system clock divider
> + * @pre_pll_div: pre pll divider
> + * @fine_int_tm: fine resolution interval time
> + * @frame_lines: number of lines in frame
> + * @line_len: number of pixels in line
> + * @min_pll: minimum pll multiplier
> + * @max_pll: maximum pll multiplier
> + */
> +struct mt9p012_pll_settings {
> +       u16     vt_pix_clk_div;
> +       u16     vt_sys_clk_div;
> +       u16     pre_pll_div;
> +
> +       u16     fine_int_tm;
> +       u16     frame_lines;
> +       u16     line_len;
> +
> +       u16     min_pll;
> +       u16     max_pll;
> +};
> +
> +/*
> + * Array of image sizes supported by MT9P012.  These must be ordered from
> + * smallest image size to largest.
> + */
> +const static struct capture_size mt9p012_sizes[] = {
> +       {  216, 162 },  /* 4X BINNING+SCALING */
> +       {  648, 486 },  /* 4X BINNING */
> +       { 1296, 972 },  /* 2X BINNING */
> +       { 2048, 1536},  /* 3 MP */
> +       { 2592, 1944},  /* 5 MP */
> +};
> +
> +/* PLL settings for MT9P012 */
> +enum mt9p012_pll_type {
> +  PLL_5MP = 0,
> +  PLL_3MP,
> +  PLL_1296_15FPS,
> +  PLL_1296_30FPS,
> +  PLL_648_15FPS,
> +  PLL_648_30FPS,
> +  PLL_216_15FPS,
> +  PLL_216_30FPS
> +};

missing tabs, fix identation.

> +
> +/* Debug functions */
> +static int debug;
> +module_param(debug, bool, 0644);
> +MODULE_PARM_DESC(debug, "Debug level (0-1)");

if it's a bool it's not debug level, it's debug on/off switch :-p

> +static struct mt9p012_sensor mt9p012;
> +static struct i2c_driver mt9p012sensor_i2c_driver;

unneeded.

> +static unsigned long xclk_current = MT9P012_XCLK_NOM_1;

why ??

> +static int
> +find_vctrl(int id)

I guess it fits in one line...

> +static int
> +mt9p012_read_reg(struct i2c_client *client, u16 data_length, u16 reg, u32 *val)
> +{
> +       int err;
> +       struct i2c_msg msg[1];
> +       unsigned char data[4];
> +
> +       if (!client->adapter)
> +               return -ENODEV;
> +       if (data_length != MT9P012_8BIT && data_length != MT9P012_16BIT
> +                                       && data_length != MT9P012_32BIT)
> +               return -EINVAL;
> +
> +       msg->addr = client->addr;
> +       msg->flags = 0;
> +       msg->len = 2;
> +       msg->buf = data;
> +
> +       /* high byte goes out first */
> +       data[0] = (u8) (reg >> 8);;
> +       data[1] = (u8) (reg & 0xff);
> +       err = i2c_transfer(client->adapter, msg, 1);
> +       if (err >= 0) {
> +               msg->len = data_length;
> +               msg->flags = I2C_M_RD;
> +               err = i2c_transfer(client->adapter, msg, 1);
> +       }
> +       if (err >= 0) {
> +               *val = 0;
> +               /* high byte comes first */
> +               if (data_length == MT9P012_8BIT)
> +                       *val = data[0];
> +               else if (data_length == MT9P012_16BIT)
> +                       *val = data[1] + (data[0] << 8);
> +               else
> +                       *val = data[3] + (data[2] << 8) +
> +                               (data[1] << 16) + (data[0] << 24);
> +               return 0;
> +       }
> +       dev_err(&client->dev, "read from offset 0x%x error %d", reg, err);

doesn't this chip support smbus ?? It would be a lot simpler if it
does... :-s

> +static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power on)
> +{
> +       struct mt9p012_sensor *sensor = s->priv;
> +       struct i2c_client *c = sensor->i2c_client;
> +       int rval;
> +
> +       if ((on == V4L2_POWER_STANDBY) && (sensor->state == SENSOR_DETECTED))
> +               mt9p012_write_regs(c, stream_off_list);
> +
> +       if (on != V4L2_POWER_ON)
> +               sensor->pdata->set_xclk(0);
> +       else
> +               sensor->pdata->set_xclk(xclk_current);

I guess this should be clk_enable() and clk_disabled() calls.

> +
> +       rval = sensor->pdata->power_set(on);
> +       if (rval < 0) {
> +               dev_err(&c->dev, "Unable to set the power state: " DRIVER_NAME
> +                                                               " sensor\n");

dev_err() should already hold the driver name. This could be changed to:

dev_err(&c->dev, "Unable to set the power state, err %d\n"), rval);

> +               sensor->pdata->set_xclk(0);
> +               return rval;
> +       }
> +
> +       if ((on == V4L2_POWER_ON) && (sensor->state == SENSOR_DETECTED))
> +               mt9p012_configure(s);
> +
> +       if ((on == V4L2_POWER_ON) && (sensor->state == SENSOR_NOT_DETECTED)) {
> +               rval = mt9p012_detect(c);

this should be called during probe() and if it fails you bail out...
otherwise the device will always be available, I guess...

> +               if (rval < 0) {
> +                       dev_err(&c->dev, "Unable to detect " DRIVER_NAME
> +                                                               " sensor\n");
> +                       sensor->state = SENSOR_NOT_DETECTED;
> +                       return rval;
> +               }
> +               sensor->state = SENSOR_DETECTED;
> +               sensor->ver = rval;
> +               pr_info(DRIVER_NAME " chip version 0x%02x detected\n",
> +                                                               sensor->ver);

no pr_info, use dev_dbg();

> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT
> + * @s: pointer to standard V4L2 device structure
> + *
> + * Initialize the sensor device (call mt9p012_configure())
> + */
> +static int ioctl_init(struct v4l2_int_device *s)
> +{
> +       return 0;
> +}
> +
> +/**
> + * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num
> + * @s: pointer to standard V4L2 device structure
> + *
> + * Delinitialise the dev. at slave detach.  The complement of ioctl_dev_init.
> + */
> +static int ioctl_dev_exit(struct v4l2_int_device *s)
> +{
> +       return 0;
> +}
> +
> +/**
> + * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num
> + * @s: pointer to standard V4L2 device structure
> + *
> + * Initialise the device when slave attaches to the master.  Returns 0 if
> + * mt9p012 device could be found, otherwise returns appropriate error.
> + */
> +static int ioctl_dev_init(struct v4l2_int_device *s)
> +{
> +       return 0;
> +}
> +/**
> + * ioctl_enum_framesizes - V4L2 sensor if handler for vidioc_int_enum_framesizes
> + * @s: pointer to standard V4L2 device structure
> + * @frms: pointer to standard V4L2 framesizes enumeration structure
> + *
> + * Returns possible framesizes depending on choosen pixel format
> + **/
> +static int ioctl_enum_framesizes(struct v4l2_int_device *s,
> +                                       struct v4l2_frmsizeenum *frms)
> +{
> +       int ifmt;
> +
> +       for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) {
> +               if (frms->pixel_format == mt9p012_formats[ifmt].pixelformat)
> +                       break;
> +       }
> +       /* Is requested pixelformat not found on sensor? */
> +       if (ifmt == NUM_CAPTURE_FORMATS)
> +               return -EINVAL;
> +
> +       /* Do we already reached all discrete framesizes? */
> +       if (frms->index >= 5)
> +               return -EINVAL;
> +
> +       frms->type = V4L2_FRMSIZE_TYPE_DISCRETE;
> +       frms->discrete.width = mt9p012_sizes[frms->index].width;
> +       frms->discrete.height = mt9p012_sizes[frms->index].height;
> +
> +       return 0;
> +}
> +
> +const struct v4l2_fract mt9p012_frameintervals[] = {
> +       {  .numerator = 1, .denominator = 11 },
> +       {  .numerator = 1, .denominator = 15 },
> +       {  .numerator = 1, .denominator = 20 },
> +       {  .numerator = 1, .denominator = 25 },
> +       {  .numerator = 1, .denominator = 30 },
> +};
> +
> +static int ioctl_enum_frameintervals(struct v4l2_int_device *s,
> +                                       struct v4l2_frmivalenum *frmi)
> +{
> +       int ifmt;
> +
> +       for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) {
> +               if (frmi->pixel_format == mt9p012_formats[ifmt].pixelformat)
> +                       break;
> +       }
> +       /* Is requested pixelformat not found on sensor? */
> +       if (ifmt == NUM_CAPTURE_FORMATS)
> +               return -EINVAL;
> +
> +       /* Do we already reached all discrete framesizes? */
> +
> +       if (((frmi->width == mt9p012_sizes[4].width) &&
> +                               (frmi->height == mt9p012_sizes[4].height)) ||
> +                               ((frmi->width == mt9p012_sizes[3].width) &&
> +                               (frmi->height == mt9p012_sizes[3].height))) {
> +               /* FIXME: The only frameinterval supported by 5MP and 3MP
> +                * capture sizes is 1/11 fps
> +                */
> +               if (frmi->index != 0)
> +                       return -EINVAL;
> +       } else {
> +               if (frmi->index >= 5)
> +                       return -EINVAL;
> +       }
> +
> +       frmi->type = V4L2_FRMSIZE_TYPE_DISCRETE;
> +       frmi->discrete.numerator =
> +                               mt9p012_frameintervals[frmi->index].numerator;
> +       frmi->discrete.denominator =
> +                               mt9p012_frameintervals[frmi->index].denominator;
> +
> +       return 0;
> +}
> +
> +static struct v4l2_int_ioctl_desc mt9p012_ioctl_desc[] = {
> +       { .num = vidioc_int_enum_framesizes_num,
> +         .func = (v4l2_int_ioctl_func *)ioctl_enum_framesizes },
> +       { .num = vidioc_int_enum_frameintervals_num,
> +         .func = (v4l2_int_ioctl_func *)ioctl_enum_frameintervals },
> +       { .num = vidioc_int_dev_init_num,
> +         .func = (v4l2_int_ioctl_func *)ioctl_dev_init },
> +       { .num = vidioc_int_dev_exit_num,
> +         .func = (v4l2_int_ioctl_func *)ioctl_dev_exit },
> +       { .num = vidioc_int_s_power_num,
> +         .func = (v4l2_int_ioctl_func *)ioctl_s_power },
> +       { .num = vidioc_int_g_priv_num,
> +         .func = (v4l2_int_ioctl_func *)ioctl_g_priv },
> +       { .num = vidioc_int_init_num,
> +         .func = (v4l2_int_ioctl_func *)ioctl_init },
> +       { .num = vidioc_int_enum_fmt_cap_num,
> +         .func = (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap },
> +       { .num = vidioc_int_try_fmt_cap_num,
> +         .func = (v4l2_int_ioctl_func *)ioctl_try_fmt_cap },
> +       { .num = vidioc_int_g_fmt_cap_num,
> +         .func = (v4l2_int_ioctl_func *)ioctl_g_fmt_cap },
> +       { .num = vidioc_int_s_fmt_cap_num,
> +         .func = (v4l2_int_ioctl_func *)ioctl_s_fmt_cap },
> +       { .num = vidioc_int_g_parm_num,
> +         .func = (v4l2_int_ioctl_func *)ioctl_g_parm },
> +       { .num = vidioc_int_s_parm_num,
> +         .func = (v4l2_int_ioctl_func *)ioctl_s_parm },
> +       { .num = vidioc_int_queryctrl_num,
> +         .func = (v4l2_int_ioctl_func *)ioctl_queryctrl },
> +       { .num = vidioc_int_g_ctrl_num,
> +         .func = (v4l2_int_ioctl_func *)ioctl_g_ctrl },
> +       { .num = vidioc_int_s_ctrl_num,
> +         .func = (v4l2_int_ioctl_func *)ioctl_s_ctrl },
> +};
> +
> +static struct v4l2_int_slave mt9p012_slave = {
> +       .ioctls = mt9p012_ioctl_desc,
> +       .num_ioctls = ARRAY_SIZE(mt9p012_ioctl_desc),
> +};
> +
> +static struct v4l2_int_device mt9p012_int_device = {
> +       .module = THIS_MODULE,
> +       .name = DRIVER_NAME,
> +       .priv = &mt9p012,
> +       .type = v4l2_int_type_slave,
> +       .u = {
> +               .slave = &mt9p012_slave,
> +       },

please tabify this.

> +};
> +
> +/**
> + * mt9p012_probe - sensor driver i2c probe handler
> + * @client: i2c driver client device structure
> + *
> + * Register sensor as an i2c client device and V4L2
> + * device.
> + */
> +static int
> +mt9p012_probe(struct i2c_client *client, const struct i2c_device_id *id)
> +{
> +       struct mt9p012_sensor *sensor = &mt9p012;

you should kzalloc(sensor) during probe() and be sure to kfree() in the
error case and on remove().

> +       int err;
> +
> +       if (i2c_get_clientdata(client))
> +               return -EBUSY;
> +
> +       sensor->pdata = client->dev.platform_data;

it's not a good practice to hold the complete pdata. You should have
something like:


struct mt9p012_platform_data *pdata = client->dev.platorm_data;

if (!pdata) {
	dev_err(&client->dev, "no pdata\n";
	return -EINVAL
}

sensor->power_set = pdata->power_set;
sensor->... = pdata->...
> +
> +       if (!sensor->pdata) {
> +               dev_err(&client->dev, "no platform data?\n");
> +               return -ENODEV;

why no dev ?? the device seems to exist...

> +       }
> +
> +       sensor->v4l2_int_device = &mt9p012_int_device;
> +       sensor->i2c_client = client;

You don't wanna hold client, you just need dev. From dev you can fecth
the i2c client pointer again by:

sensor->dev = &client->dev;

...

client = to_i2c_client(sensor->dev);

> +       i2c_set_clientdata(client, sensor);
> +
> +       /* Make the default capture format QCIF V4L2_PIX_FMT_SGRBG10 */
> +       sensor->pix.width = MT9P012_VIDEO_WIDTH_4X_BINN_SCALED;
> +       sensor->pix.height = MT9P012_VIDEO_WIDTH_4X_BINN_SCALED;
> +       sensor->pix.pixelformat = V4L2_PIX_FMT_SGRBG10;
> +
> +       err = v4l2_int_device_register(sensor->v4l2_int_device);
> +       if (err)
> +               i2c_set_clientdata(client, NULL);
> +
> +       return err;
> +}
> +
> +/**
> + * mt9p012_remove - sensor driver i2c remove handler
> + * @client: i2c driver client device structure
> + *
> + * Unregister sensor as an i2c client device and V4L2
> + * device.  Complement of mt9p012_probe().
> + */
> +static int __exit
> +mt9p012_remove(struct i2c_client *client)

you can't do it, remove __exit from here. i2c drivers can't sit in
__init or __exit only sections.

> +{
> +       struct mt9p012_sensor *sensor = i2c_get_clientdata(client);
> +
> +       if (!client->adapter)
> +               return -ENODEV; /* our client isn't attached */

this won't happen, if it does, fix your driver :-p

> +
> +       v4l2_int_device_unregister(sensor->v4l2_int_device);
> +       i2c_set_clientdata(client, NULL);
> +
> +       return 0;
> +}
> +
> +static const struct i2c_device_id mt9p012_id[] = {
> +       { DRIVER_NAME, 0 },
> +       { },
> +};
> +MODULE_DEVICE_TABLE(i2c, mt9p012_id);
> +
> +static struct i2c_driver mt9p012sensor_i2c_driver = {
> +       .driver = {
> +               .name = DRIVER_NAME,
> +               .owner = THIS_MODULE,
> +       },
> +       .probe = mt9p012_probe,
> +       .remove = __exit_p(mt9p012_remove),

remove __exit_p()

> +       .id_table = mt9p012_id,
> +};
> +
> +static struct mt9p012_sensor mt9p012 = {
> +       .timeperframe = {
> +               .numerator = 1,
> +               .denominator = 15,
> +       },
> +       .state = SENSOR_NOT_DETECTED,
> +};
> +
> +/**
> + * mt9p012sensor_init - sensor driver module_init handler
> + *
> + * Registers driver as an i2c client driver.  Returns 0 on success,
> + * error code otherwise.
> + */
> +static int __init mt9p012sensor_init(void)
> +{
> +       return i2c_add_driver(&mt9p012sensor_i2c_driver);
> +}
> +module_init(mt9p012sensor_init);
> +
> +/**
> + * mt9p012sensor_cleanup - sensor driver module_exit handler
> + *
> + * Unregisters/deletes driver as an i2c client driver.
> + * Complement of mt9p012sensor_init.
> + */
> +static void __exit mt9p012sensor_cleanup(void)
> +{
> +       i2c_del_driver(&mt9p012sensor_i2c_driver);
> +}
> +module_exit(mt9p012sensor_cleanup);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("mt9p012 camera sensor driver");
> diff --git a/drivers/media/video/mt9p012_regs.h b/drivers/media/video/mt9p012_regs.h
> new file mode 100644
> index 0000000..70f6ee7
> --- /dev/null
> +++ b/drivers/media/video/mt9p012_regs.h
> @@ -0,0 +1,74 @@
> +/*
> + * drivers/media/video/mt9p012_regs.h
> + *
> + * Register definitions for the MT9P012 camera sensor.
> + *
> + * Author:
> + *     Sameer Venkatraman <sameerv@ti.com>
> + *     Sergio Aguirre <saaguirre@ti.com>
> + *     Martinez Leonides
> + *
> + *
> + * Copyright (C) 2008 Texas Instruments.
> + *
> + * This file is licensed under the terms of the GNU General Public License
> + * version 2. This program is licensed "as is" without any warranty of any
> + * kind, whether express or implied.
> + */

no reason to add this. It's only used in the sibbling C file, so move
these there.

> diff --git a/include/media/mt9p012.h b/include/media/mt9p012.h
> new file mode 100644
> index 0000000..13a9745
> --- /dev/null
> +++ b/include/media/mt9p012.h
> @@ -0,0 +1,37 @@
> +/*
> + * drivers/media/video/mt9p012.h

This path is wrong and nobody uses it anymore, it should be in form:

mt9p012.h - Register definitions for the MT9P012 camera sensor

> + *
> + * Register definitions for the MT9P012 camera sensor.
> + *
> + * Author:
> + *     Sameer Venkatraman <sameerv@ti.com>
> + *     Martinez Leonides
> + *
> + *
> + * Copyright (C) 2008 Texas Instruments.
> + *
> + * This file is licensed under the terms of the GNU General Public License
> + * version 2. This program is licensed "as is" without any warranty of any
> + * kind, whether express or implied.
> + */
> +
> +#ifndef MT9P012_H
> +#define MT9P012_H
> +
> +
> +#define MT9P012_I2C_ADDR               0x10
> +
> +/**
> + * struct mt9p012_platform_data - platform data values and access functions
> + * @power_set: Power state access function, zero is off, non-zero is on.
> + * @default_regs: Default registers written after power-on or reset.
> + * @ifparm: Interface parameters access function
> + * @priv_data_set: device private data (pointer) access function
> + */
> +struct mt9p012_platform_data {
> +       int (*power_set)(enum v4l2_power power);
> +       u32 (*set_xclk)(u32 xclkfreq);

You shouldn't need this function, should be using clk fw.
Aguirre Rodriguez, Sergio Alberto March 9, 2009, 5:26 p.m. UTC | #2
Hi Felipe,

Sorry for the delay replying this...

Find my responses below.

> -----Original Message-----
> From: Felipe Balbi [mailto:felipe.balbi@nokia.com]
> Sent: Wednesday, March 04, 2009 5:31 AM
> To: Aguirre Rodriguez, Sergio Alberto
> Cc: linux-media@vger.kernel.org; linux-omap@vger.kernel.org; Sakari Ailus;
> Toivonen Tuukka.O (Nokia-D/Oulu); Doyu Hiroshi (Nokia-D/Helsinki);
> DongSoo(Nathaniel) Kim; MiaoStanley; Nagalla, Hari; Hiremath, Vaibhav;
> Lakhani, Amish; Menon, Nishanth
> Subject: Re: [PATCH 1/5] MT9P012: Add driver
>
> Hi,
>
> not looking at v4l2 part since it's not my area...
>
>
> On Tue, Mar 03, 2009 at 09:44:14PM +0100, ext Aguirre Rodriguez, Sergio
> Alberto wrote:
> > +#define SENSOR_DETECTED                1
> > +#define SENSOR_NOT_DETECTED    0
>
> these two should be unneeded...

Agreed, got rid of them...

>
> > +
> > +/**
> > + * struct mt9p012_reg - mt9p012 register format
> > + * @length: length of the register
> > + * @reg: 16-bit offset to register
> > + * @val: 8/16/32-bit register value
> > + *
> > + * Define a structure for MT9P012 register initialization values
> > + */
> > +struct mt9p012_reg {
> > +       u16     length;
> > +       u16     reg;
> > +       u32     val;
> > +};
> > +
> > +enum image_size {
> > +       BIN4XSCALE,
> > +       BIN4X,
> > +       BIN2X,
> > +       THREE_MP,
> > +       FIVE_MP
>
> you probably wanna prefix these with MT9P012_ to avoid namespace
> conflicts.

Done.

>
> > +};
> > +
> > +enum pixel_format {
> > +       RAWBAYER10
> > +};
> > +
> > +#define NUM_IMAGE_SIZES                5
> > +#define NUM_PIXEL_FORMATS      1
> > +#define NUM_FPS                        2       /* 2 ranges */
> > +#define FPS_LOW_RANGE          0
> > +#define FPS_HIGH_RANGE         1
> > +
> > +/**
> > + * struct capture_size - image capture size information
> > + * @width: image width in pixels
> > + * @height: image height in pixels
> > + */
> > +struct capture_size {
> > +       unsigned long width;
> > +       unsigned long height;
> > +};
> > +
> > +/**
> > + * struct mt9p012_pll_settings - struct for storage of sensor pll
> values
> > + * @vt_pix_clk_div: vertical pixel clock divider
> > + * @vt_sys_clk_div: veritcal system clock divider
> > + * @pre_pll_div: pre pll divider
> > + * @fine_int_tm: fine resolution interval time
> > + * @frame_lines: number of lines in frame
> > + * @line_len: number of pixels in line
> > + * @min_pll: minimum pll multiplier
> > + * @max_pll: maximum pll multiplier
> > + */
> > +struct mt9p012_pll_settings {
> > +       u16     vt_pix_clk_div;
> > +       u16     vt_sys_clk_div;
> > +       u16     pre_pll_div;
> > +
> > +       u16     fine_int_tm;
> > +       u16     frame_lines;
> > +       u16     line_len;
> > +
> > +       u16     min_pll;
> > +       u16     max_pll;
> > +};
> > +
> > +/*
> > + * Array of image sizes supported by MT9P012.  These must be ordered
> from
> > + * smallest image size to largest.
> > + */
> > +const static struct capture_size mt9p012_sizes[] = {
> > +       {  216, 162 },  /* 4X BINNING+SCALING */
> > +       {  648, 486 },  /* 4X BINNING */
> > +       { 1296, 972 },  /* 2X BINNING */
> > +       { 2048, 1536},  /* 3 MP */
> > +       { 2592, 1944},  /* 5 MP */
> > +};
> > +
> > +/* PLL settings for MT9P012 */
> > +enum mt9p012_pll_type {
> > +  PLL_5MP = 0,
> > +  PLL_3MP,
> > +  PLL_1296_15FPS,
> > +  PLL_1296_30FPS,
> > +  PLL_648_15FPS,
> > +  PLL_648_30FPS,
> > +  PLL_216_15FPS,
> > +  PLL_216_30FPS
> > +};
>
> missing tabs, fix identation.

Done.

>
> > +
> > +/* Debug functions */
> > +static int debug;
> > +module_param(debug, bool, 0644);
> > +MODULE_PARM_DESC(debug, "Debug level (0-1)");
>
> if it's a bool it's not debug level, it's debug on/off switch :-p
>
> > +static struct mt9p012_sensor mt9p012;
> > +static struct i2c_driver mt9p012sensor_i2c_driver;
>
> unneeded.

You're right. Done.

>
> > +static unsigned long xclk_current = MT9P012_XCLK_NOM_1;
>
> why ??

Hmm, well. This is the xclk default value we use.

The driver basically uses 2 XCLK values to cover all the supported framerates/framesizes.

Anyways, I've added a xclk_current field as part of struct mt9p012_sensor instead. Removed this static var.

>
> > +static int
> > +find_vctrl(int id)
>
> I guess it fits in one line...

Done. Fixed all similar cases.

>
> > +static int
> > +mt9p012_read_reg(struct i2c_client *client, u16 data_length, u16 reg,
> u32 *val)
> > +{
> > +       int err;
> > +       struct i2c_msg msg[1];
> > +       unsigned char data[4];
> > +
> > +       if (!client->adapter)
> > +               return -ENODEV;
> > +       if (data_length != MT9P012_8BIT && data_length != MT9P012_16BIT
> > +                                       && data_length != MT9P012_32BIT)
> > +               return -EINVAL;
> > +
> > +       msg->addr = client->addr;
> > +       msg->flags = 0;
> > +       msg->len = 2;
> > +       msg->buf = data;
> > +
> > +       /* high byte goes out first */
> > +       data[0] = (u8) (reg >> 8);;
> > +       data[1] = (u8) (reg & 0xff);
> > +       err = i2c_transfer(client->adapter, msg, 1);
> > +       if (err >= 0) {
> > +               msg->len = data_length;
> > +               msg->flags = I2C_M_RD;
> > +               err = i2c_transfer(client->adapter, msg, 1);
> > +       }
> > +       if (err >= 0) {
> > +               *val = 0;
> > +               /* high byte comes first */
> > +               if (data_length == MT9P012_8BIT)
> > +                       *val = data[0];
> > +               else if (data_length == MT9P012_16BIT)
> > +                       *val = data[1] + (data[0] << 8);
> > +               else
> > +                       *val = data[3] + (data[2] << 8) +
> > +                               (data[1] << 16) + (data[0] << 24);
> > +               return 0;
> > +       }
> > +       dev_err(&client->dev, "read from offset 0x%x error %d", reg,
> err);
>
> doesn't this chip support smbus ?? It would be a lot simpler if it
> does... :-s

I know, but I had a discussion about this some time back with David Brownell and Jean Delvare (on linux-i2c ML), and we found out that SMBus spec doesn't support 16-bit commands, and we dropped the intent.

This 16-bit length commands are needed because most registers are 16-bit length address.

>
> > +static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power on)
> > +{
> > +       struct mt9p012_sensor *sensor = s->priv;
> > +       struct i2c_client *c = sensor->i2c_client;
> > +       int rval;
> > +
> > +       if ((on == V4L2_POWER_STANDBY) && (sensor->state ==
> SENSOR_DETECTED))
> > +               mt9p012_write_regs(c, stream_off_list);
> > +
> > +       if (on != V4L2_POWER_ON)
> > +               sensor->pdata->set_xclk(0);
> > +       else
> > +               sensor->pdata->set_xclk(xclk_current);
>
> I guess this should be clk_enable() and clk_disabled() calls.

I guess this will change as long as we change that on ISP driver.

I'll meanwhile keep it like this, and will do the corresponding adaptation once Sakari and I discuss about moving OMAP3 ISP XCLK setting to be handled with clk API...

>
> > +
> > +       rval = sensor->pdata->power_set(on);
> > +       if (rval < 0) {
> > +               dev_err(&c->dev, "Unable to set the power state: "
> DRIVER_NAME
> > +                                                               "
> sensor\n");
>
> dev_err() should already hold the driver name. This could be changed to:
>
> dev_err(&c->dev, "Unable to set the power state, err %d\n"), rval);
>
> > +               sensor->pdata->set_xclk(0);
> > +               return rval;
> > +       }
> > +
> > +       if ((on == V4L2_POWER_ON) && (sensor->state == SENSOR_DETECTED))
> > +               mt9p012_configure(s);
> > +
> > +       if ((on == V4L2_POWER_ON) && (sensor->state ==
> SENSOR_NOT_DETECTED)) {
> > +               rval = mt9p012_detect(c);
>
> this should be called during probe() and if it fails you bail out...
> otherwise the device will always be available, I guess...

This cannot be done, as in order to make the sensor work, we depend on omap3 camera initialization. (Depend on getting OMAP3 ISP clocks, which is done on main camera driver)

This can change as we are moving to v4l2_subdev, and thinking about the best ways to cleanup OMAP3ISP interface.

>
> > +               if (rval < 0) {
> > +                       dev_err(&c->dev, "Unable to detect " DRIVER_NAME
> > +                                                               "
> sensor\n");
> > +                       sensor->state = SENSOR_NOT_DETECTED;
> > +                       return rval;
> > +               }
> > +               sensor->state = SENSOR_DETECTED;
> > +               sensor->ver = rval;
> > +               pr_info(DRIVER_NAME " chip version 0x%02x detected\n",
> > +                                                               sensor-
> >ver);
>
> no pr_info, use dev_dbg();

Agreed. Done.

>
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +/**
> > + * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT
> > + * @s: pointer to standard V4L2 device structure
> > + *
> > + * Initialize the sensor device (call mt9p012_configure())
> > + */
> > +static int ioctl_init(struct v4l2_int_device *s)
> > +{
> > +       return 0;
> > +}
> > +
> > +/**
> > + * ioctl_dev_exit - V4L2 sensor interface handler for
> vidioc_int_dev_exit_num
> > + * @s: pointer to standard V4L2 device structure
> > + *
> > + * Delinitialise the dev. at slave detach.  The complement of
> ioctl_dev_init.
> > + */
> > +static int ioctl_dev_exit(struct v4l2_int_device *s)
> > +{
> > +       return 0;
> > +}
> > +
> > +/**
> > + * ioctl_dev_init - V4L2 sensor interface handler for
> vidioc_int_dev_init_num
> > + * @s: pointer to standard V4L2 device structure
> > + *
> > + * Initialise the device when slave attaches to the master.  Returns 0
> if
> > + * mt9p012 device could be found, otherwise returns appropriate error.
> > + */
> > +static int ioctl_dev_init(struct v4l2_int_device *s)
> > +{
> > +       return 0;
> > +}
> > +/**
> > + * ioctl_enum_framesizes - V4L2 sensor if handler for
> vidioc_int_enum_framesizes
> > + * @s: pointer to standard V4L2 device structure
> > + * @frms: pointer to standard V4L2 framesizes enumeration structure
> > + *
> > + * Returns possible framesizes depending on choosen pixel format
> > + **/
> > +static int ioctl_enum_framesizes(struct v4l2_int_device *s,
> > +                                       struct v4l2_frmsizeenum *frms)
> > +{
> > +       int ifmt;
> > +
> > +       for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) {
> > +               if (frms->pixel_format ==
> mt9p012_formats[ifmt].pixelformat)
> > +                       break;
> > +       }
> > +       /* Is requested pixelformat not found on sensor? */
> > +       if (ifmt == NUM_CAPTURE_FORMATS)
> > +               return -EINVAL;
> > +
> > +       /* Do we already reached all discrete framesizes? */
> > +       if (frms->index >= 5)
> > +               return -EINVAL;
> > +
> > +       frms->type = V4L2_FRMSIZE_TYPE_DISCRETE;
> > +       frms->discrete.width = mt9p012_sizes[frms->index].width;
> > +       frms->discrete.height = mt9p012_sizes[frms->index].height;
> > +
> > +       return 0;
> > +}
> > +
> > +const struct v4l2_fract mt9p012_frameintervals[] = {
> > +       {  .numerator = 1, .denominator = 11 },
> > +       {  .numerator = 1, .denominator = 15 },
> > +       {  .numerator = 1, .denominator = 20 },
> > +       {  .numerator = 1, .denominator = 25 },
> > +       {  .numerator = 1, .denominator = 30 },
> > +};
> > +
> > +static int ioctl_enum_frameintervals(struct v4l2_int_device *s,
> > +                                       struct v4l2_frmivalenum *frmi)
> > +{
> > +       int ifmt;
> > +
> > +       for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) {
> > +               if (frmi->pixel_format ==
> mt9p012_formats[ifmt].pixelformat)
> > +                       break;
> > +       }
> > +       /* Is requested pixelformat not found on sensor? */
> > +       if (ifmt == NUM_CAPTURE_FORMATS)
> > +               return -EINVAL;
> > +
> > +       /* Do we already reached all discrete framesizes? */
> > +
> > +       if (((frmi->width == mt9p012_sizes[4].width) &&
> > +                               (frmi->height ==
> mt9p012_sizes[4].height)) ||
> > +                               ((frmi->width == mt9p012_sizes[3].width)
> &&
> > +                               (frmi->height ==
> mt9p012_sizes[3].height))) {
> > +               /* FIXME: The only frameinterval supported by 5MP and
> 3MP
> > +                * capture sizes is 1/11 fps
> > +                */
> > +               if (frmi->index != 0)
> > +                       return -EINVAL;
> > +       } else {
> > +               if (frmi->index >= 5)
> > +                       return -EINVAL;
> > +       }
> > +
> > +       frmi->type = V4L2_FRMSIZE_TYPE_DISCRETE;
> > +       frmi->discrete.numerator =
> > +                               mt9p012_frameintervals[frmi-
> >index].numerator;
> > +       frmi->discrete.denominator =
> > +                               mt9p012_frameintervals[frmi-
> >index].denominator;
> > +
> > +       return 0;
> > +}
> > +
> > +static struct v4l2_int_ioctl_desc mt9p012_ioctl_desc[] = {
> > +       { .num = vidioc_int_enum_framesizes_num,
> > +         .func = (v4l2_int_ioctl_func *)ioctl_enum_framesizes },
> > +       { .num = vidioc_int_enum_frameintervals_num,
> > +         .func = (v4l2_int_ioctl_func *)ioctl_enum_frameintervals },
> > +       { .num = vidioc_int_dev_init_num,
> > +         .func = (v4l2_int_ioctl_func *)ioctl_dev_init },
> > +       { .num = vidioc_int_dev_exit_num,
> > +         .func = (v4l2_int_ioctl_func *)ioctl_dev_exit },
> > +       { .num = vidioc_int_s_power_num,
> > +         .func = (v4l2_int_ioctl_func *)ioctl_s_power },
> > +       { .num = vidioc_int_g_priv_num,
> > +         .func = (v4l2_int_ioctl_func *)ioctl_g_priv },
> > +       { .num = vidioc_int_init_num,
> > +         .func = (v4l2_int_ioctl_func *)ioctl_init },
> > +       { .num = vidioc_int_enum_fmt_cap_num,
> > +         .func = (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap },
> > +       { .num = vidioc_int_try_fmt_cap_num,
> > +         .func = (v4l2_int_ioctl_func *)ioctl_try_fmt_cap },
> > +       { .num = vidioc_int_g_fmt_cap_num,
> > +         .func = (v4l2_int_ioctl_func *)ioctl_g_fmt_cap },
> > +       { .num = vidioc_int_s_fmt_cap_num,
> > +         .func = (v4l2_int_ioctl_func *)ioctl_s_fmt_cap },
> > +       { .num = vidioc_int_g_parm_num,
> > +         .func = (v4l2_int_ioctl_func *)ioctl_g_parm },
> > +       { .num = vidioc_int_s_parm_num,
> > +         .func = (v4l2_int_ioctl_func *)ioctl_s_parm },
> > +       { .num = vidioc_int_queryctrl_num,
> > +         .func = (v4l2_int_ioctl_func *)ioctl_queryctrl },
> > +       { .num = vidioc_int_g_ctrl_num,
> > +         .func = (v4l2_int_ioctl_func *)ioctl_g_ctrl },
> > +       { .num = vidioc_int_s_ctrl_num,
> > +         .func = (v4l2_int_ioctl_func *)ioctl_s_ctrl },
> > +};
> > +
> > +static struct v4l2_int_slave mt9p012_slave = {
> > +       .ioctls = mt9p012_ioctl_desc,
> > +       .num_ioctls = ARRAY_SIZE(mt9p012_ioctl_desc),
> > +};
> > +
> > +static struct v4l2_int_device mt9p012_int_device = {
> > +       .module = THIS_MODULE,
> > +       .name = DRIVER_NAME,
> > +       .priv = &mt9p012,
> > +       .type = v4l2_int_type_slave,
> > +       .u = {
> > +               .slave = &mt9p012_slave,
> > +       },
>
> please tabify this.

Done.

>
> > +};
> > +
> > +/**
> > + * mt9p012_probe - sensor driver i2c probe handler
> > + * @client: i2c driver client device structure
> > + *
> > + * Register sensor as an i2c client device and V4L2
> > + * device.
> > + */
> > +static int
> > +mt9p012_probe(struct i2c_client *client, const struct i2c_device_id
> *id)
> > +{
> > +       struct mt9p012_sensor *sensor = &mt9p012;
>
> you should kzalloc(sensor) during probe() and be sure to kfree() in the
> error case and on remove().

Done.

>
> > +       int err;
> > +
> > +       if (i2c_get_clientdata(client))
> > +               return -EBUSY;
> > +
> > +       sensor->pdata = client->dev.platform_data;
>
> it's not a good practice to hold the complete pdata. You should have
> something like:
>
>
> struct mt9p012_platform_data *pdata = client->dev.platorm_data;
>
> if (!pdata) {
>       dev_err(&client->dev, "no pdata\n";
>       return -EINVAL
> }
>
> sensor->power_set = pdata->power_set;
> sensor->... = pdata->...

Agreed, Done.

> > +
> > +       if (!sensor->pdata) {
> > +               dev_err(&client->dev, "no platform data?\n");
> > +               return -ENODEV;
>
> why no dev ?? the device seems to exist...

Ok, should returning -EINVAL be ok? What do you think?

>
> > +       }
> > +
> > +       sensor->v4l2_int_device = &mt9p012_int_device;
> > +       sensor->i2c_client = client;
>
> You don't wanna hold client, you just need dev. From dev you can fecth
> the i2c client pointer again by:
>
> sensor->dev = &client->dev;
>
> ...
>
> client = to_i2c_client(sensor->dev);

Agreed, done.

>
> > +       i2c_set_clientdata(client, sensor);
> > +
> > +       /* Make the default capture format QCIF V4L2_PIX_FMT_SGRBG10 */
> > +       sensor->pix.width = MT9P012_VIDEO_WIDTH_4X_BINN_SCALED;
> > +       sensor->pix.height = MT9P012_VIDEO_WIDTH_4X_BINN_SCALED;
> > +       sensor->pix.pixelformat = V4L2_PIX_FMT_SGRBG10;
> > +
> > +       err = v4l2_int_device_register(sensor->v4l2_int_device);
> > +       if (err)
> > +               i2c_set_clientdata(client, NULL);
> > +
> > +       return err;
> > +}
> > +
> > +/**
> > + * mt9p012_remove - sensor driver i2c remove handler
> > + * @client: i2c driver client device structure
> > + *
> > + * Unregister sensor as an i2c client device and V4L2
> > + * device.  Complement of mt9p012_probe().
> > + */
> > +static int __exit
> > +mt9p012_remove(struct i2c_client *client)
>
> you can't do it, remove __exit from here. i2c drivers can't sit in
> __init or __exit only sections.

Done.
>
> > +{
> > +       struct mt9p012_sensor *sensor = i2c_get_clientdata(client);
> > +
> > +       if (!client->adapter)
> > +               return -ENODEV; /* our client isn't attached */
>
> this won't happen, if it does, fix your driver :-p

Agreed. Removed the check.

>
> > +
> > +       v4l2_int_device_unregister(sensor->v4l2_int_device);
> > +       i2c_set_clientdata(client, NULL);
> > +
> > +       return 0;
> > +}
> > +
> > +static const struct i2c_device_id mt9p012_id[] = {
> > +       { DRIVER_NAME, 0 },
> > +       { },
> > +};
> > +MODULE_DEVICE_TABLE(i2c, mt9p012_id);
> > +
> > +static struct i2c_driver mt9p012sensor_i2c_driver = {
> > +       .driver = {
> > +               .name = DRIVER_NAME,
> > +               .owner = THIS_MODULE,
> > +       },
> > +       .probe = mt9p012_probe,
> > +       .remove = __exit_p(mt9p012_remove),
>
> remove __exit_p()

Done.

>
> > +       .id_table = mt9p012_id,
> > +};
> > +
> > +static struct mt9p012_sensor mt9p012 = {
> > +       .timeperframe = {
> > +               .numerator = 1,
> > +               .denominator = 15,
> > +       },
> > +       .state = SENSOR_NOT_DETECTED,
> > +};
> > +
> > +/**
> > + * mt9p012sensor_init - sensor driver module_init handler
> > + *
> > + * Registers driver as an i2c client driver.  Returns 0 on success,
> > + * error code otherwise.
> > + */
> > +static int __init mt9p012sensor_init(void)
> > +{
> > +       return i2c_add_driver(&mt9p012sensor_i2c_driver);
> > +}
> > +module_init(mt9p012sensor_init);
> > +
> > +/**
> > + * mt9p012sensor_cleanup - sensor driver module_exit handler
> > + *
> > + * Unregisters/deletes driver as an i2c client driver.
> > + * Complement of mt9p012sensor_init.
> > + */
> > +static void __exit mt9p012sensor_cleanup(void)
> > +{
> > +       i2c_del_driver(&mt9p012sensor_i2c_driver);
> > +}
> > +module_exit(mt9p012sensor_cleanup);
> > +
> > +MODULE_LICENSE("GPL");
> > +MODULE_DESCRIPTION("mt9p012 camera sensor driver");
> > diff --git a/drivers/media/video/mt9p012_regs.h
> b/drivers/media/video/mt9p012_regs.h
> > new file mode 100644
> > index 0000000..70f6ee7
> > --- /dev/null
> > +++ b/drivers/media/video/mt9p012_regs.h
> > @@ -0,0 +1,74 @@
> > +/*
> > + * drivers/media/video/mt9p012_regs.h
> > + *
> > + * Register definitions for the MT9P012 camera sensor.
> > + *
> > + * Author:
> > + *     Sameer Venkatraman <sameerv@ti.com>
> > + *     Sergio Aguirre <saaguirre@ti.com>
> > + *     Martinez Leonides
> > + *
> > + *
> > + * Copyright (C) 2008 Texas Instruments.
> > + *
> > + * This file is licensed under the terms of the GNU General Public
> License
> > + * version 2. This program is licensed "as is" without any warranty of
> any
> > + * kind, whether express or implied.
> > + */
>
> no reason to add this. It's only used in the sibbling C file, so move
> these there.

Done.

>
> > diff --git a/include/media/mt9p012.h b/include/media/mt9p012.h
> > new file mode 100644
> > index 0000000..13a9745
> > --- /dev/null
> > +++ b/include/media/mt9p012.h
> > @@ -0,0 +1,37 @@
> > +/*
> > + * drivers/media/video/mt9p012.h
>
> This path is wrong and nobody uses it anymore, it should be in form:
>
> mt9p012.h - Register definitions for the MT9P012 camera sensor

Done.

>
> > + *
> > + * Register definitions for the MT9P012 camera sensor.
> > + *
> > + * Author:
> > + *     Sameer Venkatraman <sameerv@ti.com>
> > + *     Martinez Leonides
> > + *
> > + *
> > + * Copyright (C) 2008 Texas Instruments.
> > + *
> > + * This file is licensed under the terms of the GNU General Public
> License
> > + * version 2. This program is licensed "as is" without any warranty of
> any
> > + * kind, whether express or implied.
> > + */
> > +
> > +#ifndef MT9P012_H
> > +#define MT9P012_H
> > +
> > +
> > +#define MT9P012_I2C_ADDR               0x10
> > +
> > +/**
> > + * struct mt9p012_platform_data - platform data values and access
> functions
> > + * @power_set: Power state access function, zero is off, non-zero is on.
> > + * @default_regs: Default registers written after power-on or reset.
> > + * @ifparm: Interface parameters access function
> > + * @priv_data_set: device private data (pointer) access function
> > + */
> > +struct mt9p012_platform_data {
> > +       int (*power_set)(enum v4l2_power power);
> > +       u32 (*set_xclk)(u32 xclkfreq);
>
> You shouldn't need this function, should be using clk fw.

Agree, but left it like this meanwhile, as we need to rework that on ISP driver first.

Thanks a lot for your time!
Sergio
>
> --
> balbi

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 3cdb5a4..05e9fa1 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -305,6 +305,14 @@  config VIDEO_TCM825X
          This is a driver for the Toshiba TCM825x VGA camera sensor.
          It is used for example in Nokia N800.

+config VIDEO_MT9P012
+       tristate "Micron MT9P012 raw sensor driver (5MP)"
+       depends on I2C && VIDEO_V4L2
+       ---help---
+         This is a Video4Linux2 sensor-level driver for the Micron
+         MT9P012 camera.  It is currently working with the TI OMAP3
+         camera controller.
+
 config VIDEO_SAA7110
        tristate "Philips SAA7110 video decoder"
        depends on VIDEO_V4L1 && I2C
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 74a684e..c445099 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -111,6 +111,7 @@  obj-y                               += isp/
 obj-$(CONFIG_VIDEO_OMAP3) += omap34xxcam.o

 obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
+obj-$(CONFIG_VIDEO_MT9P012)     += mt9p012.o

 obj-$(CONFIG_USB_DABUSB)        += dabusb.o
 obj-$(CONFIG_USB_OV511)         += ov511.o
diff --git a/drivers/media/video/mt9p012.c b/drivers/media/video/mt9p012.c
new file mode 100644
index 0000000..466570a
--- /dev/null
+++ b/drivers/media/video/mt9p012.c
@@ -0,0 +1,1890 @@ 
+/*
+ * drivers/media/video/mt9p012.c
+ *
+ * mt9p012 sensor driver
+ *
+ * Copyright (C) 2008 Texas Instruments.
+ *
+ * Contributors:
+ *     Sameer Venkatraman <sameerv@ti.com>
+ *     Sergio Aguirre <saaguirre@ti.com>
+ *     Martinez Leonides
+ *
+ * Leverage OV9640.c
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <media/v4l2-int-device.h>
+
+#include <media/mt9p012.h>
+#include "mt9p012_regs.h"
+
+#define DRIVER_NAME  "mt9p012"
+#define MOD_NAME "MT9P012: "
+
+/* MT9P012 has 8/16/32 registers */
+#define MT9P012_8BIT                   1
+#define MT9P012_16BIT                  2
+#define MT9P012_32BIT                  4
+
+/* terminating token for reg list */
+#define MT9P012_TOK_TERM               0xFF
+
+/* delay token for reg list */
+#define MT9P012_TOK_DELAY              100
+
+/* The ID values we are looking for */
+#define MT9P012_MOD_ID                 0x2800
+#define MT9P012_MFR_ID                 0x0006
+
+/* FPS Capabilities */
+#define MT9P012_MIN_FPS                11
+#define MT9P012_DEF_FPS                15
+#define MT9P012_MAX_FPS                30
+
+#define MT9P012_I2C_RETRY_COUNT        5
+
+#define MT9P012_XCLK_NOM_1 12000000
+#define MT9P012_XCLK_NOM_2 24000000
+
+/* Still capture 5 MP */
+#define MT9P012_IMAGE_WIDTH_MAX                2592
+#define MT9P012_IMAGE_HEIGHT_MAX       1944
+/* Still capture 3 MP and down to VGA, using ISP resizer */
+#define MT9P012_IMAGE_WIDTH_MIN                2048
+#define MT9P012_IMAGE_HEIGHT_MIN       1536
+
+/* Video mode, for D1 NTSC, D1 PAL */
+#define MT9P012_VIDEO_WIDTH_2X_BINN    1296
+#define MT9P012_VIDEO_HEIGHT_2X_BINN   972
+
+/* Sensor Video mode size for VGA, CIF, QVGA in 4x binning mode */
+#define MT9P012_VIDEO_WIDTH_4X_BINN    648
+#define MT9P012_VIDEO_HEIGHT_4X_BINN   486
+/* To improve image quality in VGA */
+#define CIF_PIXELS             (352 * 288)
+#define QQVGA_PIXELS           (160 * 120)
+
+/* Video mode, for QCIF, SQCIF */
+#define MT9P012_VIDEO_WIDTH_4X_BINN_SCALED      216
+#define MT9P012_VIDEO_HEIGHT_4X_BINN_SCALED     162
+
+/* Default coarse integration times to get a good exposure */
+#define COARSE_INT_TIME_216             550
+#define COARSE_INT_TIME_648             550
+#define COARSE_INT_TIME_216_30FPS      1350
+#define COARSE_INT_TIME_648_30FPS      1350
+#define COARSE_INT_TIME_1296           1000
+#define COARSE_INT_TIME_3MP            1700
+#define COARSE_INT_TIME_5MP            1700
+#define COARSE_INT_TIME_INDEX          1
+#define TST_PAT                        0x0
+
+/* Analog gain values */
+#define MIN_GAIN       0x08
+#define MAX_GAIN       0x7F
+#define DEF_GAIN       0x43
+#define GAIN_STEP      0x1
+
+#define GAIN_INDEX     1
+
+/* Exposure time values */
+#define DEF_MIN_EXPOSURE       0x08
+#define DEF_MAX_EXPOSURE       0x7F
+#define DEF_EXPOSURE           0x43
+#define EXPOSURE_STEP          1
+
+#define SENSOR_DETECTED                1
+#define SENSOR_NOT_DETECTED    0
+
+/**
+ * struct mt9p012_reg - mt9p012 register format
+ * @length: length of the register
+ * @reg: 16-bit offset to register
+ * @val: 8/16/32-bit register value
+ *
+ * Define a structure for MT9P012 register initialization values
+ */
+struct mt9p012_reg {
+       u16     length;
+       u16     reg;
+       u32     val;
+};
+
+enum image_size {
+       BIN4XSCALE,
+       BIN4X,
+       BIN2X,
+       THREE_MP,
+       FIVE_MP
+};
+
+enum pixel_format {
+       RAWBAYER10
+};
+
+#define NUM_IMAGE_SIZES                5
+#define NUM_PIXEL_FORMATS      1
+#define NUM_FPS                        2       /* 2 ranges */
+#define FPS_LOW_RANGE          0
+#define FPS_HIGH_RANGE         1
+
+/**
+ * struct capture_size - image capture size information
+ * @width: image width in pixels
+ * @height: image height in pixels
+ */
+struct capture_size {
+       unsigned long width;
+       unsigned long height;
+};
+
+/**
+ * struct mt9p012_pll_settings - struct for storage of sensor pll values
+ * @vt_pix_clk_div: vertical pixel clock divider
+ * @vt_sys_clk_div: veritcal system clock divider
+ * @pre_pll_div: pre pll divider
+ * @fine_int_tm: fine resolution interval time
+ * @frame_lines: number of lines in frame
+ * @line_len: number of pixels in line
+ * @min_pll: minimum pll multiplier
+ * @max_pll: maximum pll multiplier
+ */
+struct mt9p012_pll_settings {
+       u16     vt_pix_clk_div;
+       u16     vt_sys_clk_div;
+       u16     pre_pll_div;
+
+       u16     fine_int_tm;
+       u16     frame_lines;
+       u16     line_len;
+
+       u16     min_pll;
+       u16     max_pll;
+};
+
+/*
+ * Array of image sizes supported by MT9P012.  These must be ordered from
+ * smallest image size to largest.
+ */
+const static struct capture_size mt9p012_sizes[] = {
+       {  216, 162 },  /* 4X BINNING+SCALING */
+       {  648, 486 },  /* 4X BINNING */
+       { 1296, 972 },  /* 2X BINNING */
+       { 2048, 1536},  /* 3 MP */
+       { 2592, 1944},  /* 5 MP */
+};
+
+/* PLL settings for MT9P012 */
+enum mt9p012_pll_type {
+  PLL_5MP = 0,
+  PLL_3MP,
+  PLL_1296_15FPS,
+  PLL_1296_30FPS,
+  PLL_648_15FPS,
+  PLL_648_30FPS,
+  PLL_216_15FPS,
+  PLL_216_30FPS
+};
+
+/* Debug functions */
+static int debug;
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+/**
+ * struct mt9p012_sensor - main structure for storage of sensor information
+ * @pdata: access functions and data for platform level information
+ * @v4l2_int_device: V4L2 device structure structure
+ * @i2c_client: iic client device structure
+ * @pix: V4L2 pixel format information structure
+ * @timeperframe: time per frame expressed as V4L fraction
+ * @scaler:
+ * @ver: mt9p012 chip version
+ * @fps: frames per second value
+ */
+struct mt9p012_sensor {
+       const struct mt9p012_platform_data *pdata;
+       struct v4l2_int_device *v4l2_int_device;
+       struct i2c_client *i2c_client;
+       struct v4l2_pix_format pix;
+       struct v4l2_fract timeperframe;
+       int scaler;
+       int ver;
+       int fps;
+       int state;
+};
+
+static struct mt9p012_sensor mt9p012;
+static struct i2c_driver mt9p012sensor_i2c_driver;
+static unsigned long xclk_current = MT9P012_XCLK_NOM_1;
+
+/* list of image formats supported by mt9p012 sensor */
+const static struct v4l2_fmtdesc mt9p012_formats[] = {
+       {
+               .description    = "Bayer10 (GrR/BGb)",
+               .pixelformat    = V4L2_PIX_FMT_SGRBG10,
+       }
+};
+
+#define NUM_CAPTURE_FORMATS ARRAY_SIZE(mt9p012_formats)
+
+/* Enters soft standby, all settings are maintained */
+const static struct mt9p012_reg stream_off_list[] = {
+       {.length = MT9P012_8BIT, .reg = REG_MODE_SELECT, .val = 0x00},
+       {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0}
+};
+
+/* Exits soft standby */
+const static struct mt9p012_reg stream_on_list[] = {
+       {.length = MT9P012_8BIT, .reg = REG_MODE_SELECT, .val = 0x01},
+       /* Sensor datasheet says we need 1 ms to allow PLL lock */
+       {.length = MT9P012_TOK_DELAY, .reg = 0x00, .val = 1},
+       {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0}
+};
+
+/* Structure which will set the exposure time */
+static struct mt9p012_reg set_exposure_time[] = {
+       {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x01},
+       /* less than frame_lines-1 */
+       {.length = MT9P012_16BIT, .reg = REG_COARSE_INT_TIME, .val = 500},
+        /* updating */
+       {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x00},
+       {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0}
+};
+
+/* Structure to set analog gain */
+static struct mt9p012_reg set_analog_gain[] = {
+       {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x01},
+       {.length = MT9P012_16BIT, .reg = REG_ANALOG_GAIN_GLOBAL,
+               .val = MIN_GAIN},
+        /* updating */
+       {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x00},
+       {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0},
+};
+
+/*
+ * Common MT9P012 register initialization for all image sizes, pixel formats,
+ * and frame rates
+ */
+const static struct mt9p012_reg mt9p012_common[] = {
+       {MT9P012_8BIT, REG_SOFTWARE_RESET, 0x01},
+       {MT9P012_TOK_DELAY, 0x00, 5}, /* Delay = 5ms, min 2400 xcks */
+       {MT9P012_16BIT, REG_RESET_REGISTER, 0x10C8},
+       {MT9P012_8BIT, REG_GROUPED_PAR_HOLD, 0x01}, /* hold */
+       {MT9P012_16BIT, REG_ANALOG_GAIN_GREENR, 0x0020},
+       {MT9P012_16BIT, REG_ANALOG_GAIN_RED, 0x0020},
+       {MT9P012_16BIT, REG_ANALOG_GAIN_BLUE, 0x0020},
+       {MT9P012_16BIT, REG_ANALOG_GAIN_GREENB, 0x0020},
+       {MT9P012_16BIT, REG_DIGITAL_GAIN_GREENR, 0x0100},
+       {MT9P012_16BIT, REG_DIGITAL_GAIN_RED, 0x0100},
+       {MT9P012_16BIT, REG_DIGITAL_GAIN_BLUE, 0x0100},
+       {MT9P012_16BIT, REG_DIGITAL_GAIN_GREENB, 0x0100},
+       /* Recommended values for image quality, sensor Rev 1 */
+       {MT9P012_16BIT, 0x3088, 0x6FFB},
+       {MT9P012_16BIT, 0x308E, 0x2020},
+       {MT9P012_16BIT, 0x309E, 0x4400},
+       {MT9P012_16BIT, 0x30D4, 0x9080},
+       {MT9P012_16BIT, 0x3126, 0x00FF},
+       {MT9P012_16BIT, 0x3154, 0x1482},
+       {MT9P012_16BIT, 0x3158, 0x97C7},
+       {MT9P012_16BIT, 0x315A, 0x97C6},
+       {MT9P012_16BIT, 0x3162, 0x074C},
+       {MT9P012_16BIT, 0x3164, 0x0756},
+       {MT9P012_16BIT, 0x3166, 0x0760},
+       {MT9P012_16BIT, 0x316E, 0x8488},
+       {MT9P012_16BIT, 0x3172, 0x0003},
+       {MT9P012_16BIT, 0x30EA, 0x3F06},
+       {MT9P012_8BIT, REG_GROUPED_PAR_HOLD, 0x00}, /* update all at once */
+       {MT9P012_TOK_TERM, 0, 0}
+
+};
+
+/*
+ * mt9p012 register configuration for all combinations of pixel format and
+ * image size
+ */
+       /* 4X BINNING+SCALING */
+const static struct mt9p012_reg enter_video_216_15fps[] = {
+       /* stream off */
+       {.length = MT9P012_8BIT, .reg = REG_MODE_SELECT, .val = 0x00},
+       {.length = MT9P012_TOK_DELAY, .reg = 0x00, .val = 100},
+        /* hold */
+       {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x01},
+       {.length = MT9P012_16BIT, .reg = REG_VT_PIX_CLK_DIV, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_VT_SYS_CLK_DIV, .val = 2},
+       {.length = MT9P012_16BIT, .reg = REG_PRE_PLL_CLK_DIV, .val = 2},
+       {.length = MT9P012_16BIT, .reg = REG_PLL_MULTIPLIER, .val = 126},
+       {.length = MT9P012_16BIT, .reg = REG_OP_PIX_CLK_DIV, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_OP_SYS_CLK_DIV, .val = 2},
+       {.length = MT9P012_16BIT, .reg = REG_RESERVED_MFR_3064,
+               .val = 0x0805},
+       {.length = MT9P012_16BIT, .reg = REG_X_OUTPUT_SIZE,
+               .val = MT9P012_VIDEO_WIDTH_4X_BINN_SCALED},
+       {.length = MT9P012_16BIT, .reg = REG_Y_OUTPUT_SIZE,
+               .val = MT9P012_VIDEO_HEIGHT_4X_BINN_SCALED},
+       {.length = MT9P012_16BIT, .reg = REG_X_ADDR_START, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_START, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_X_ADDR_END, .val = 2593},
+       {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_END, .val = 1945},
+       {.length = MT9P012_16BIT, .reg = REG_READ_MODE, .val = 0x04FC},
+       {.length = MT9P012_16BIT, .reg = REG_FINE_INT_TIME, .val = 1794},
+       {.length = MT9P012_16BIT, .reg = REG_FRAME_LEN_LINES, .val = 574},
+       {.length = MT9P012_16BIT, .reg = REG_LINE_LEN_PCK, .val = 2712},
+        /* 0x10/0x30 = 0.3333 */
+       {.length = MT9P012_16BIT, .reg = REG_SCALE_M, .val = 0x0030},
+       /* enable scaler */
+       {.length = MT9P012_16BIT, .reg = REG_SCALING_MODE, .val = 0x0002},
+       {.length = MT9P012_16BIT, .reg = REG_COARSE_INT_TIME,
+               .val = COARSE_INT_TIME_216},
+       /* update */
+       {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x00},
+       {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0}
+       };
+
+       /* Video mode, 4x binning + scaling, range 16 - 30 fps */
+const static struct mt9p012_reg enter_video_216_30fps[] = {
+       /* stream off */
+       {.length = MT9P012_8BIT, .reg = REG_MODE_SELECT, .val = 0x00},
+       {.length = MT9P012_TOK_DELAY, .reg = 0x00, .val = 100},
+       /* hold */
+       {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x01},
+       {.length = MT9P012_16BIT, .reg = REG_VT_PIX_CLK_DIV, .val = 5},
+       {.length = MT9P012_16BIT, .reg = REG_VT_SYS_CLK_DIV, .val = 2},
+       {.length = MT9P012_16BIT, .reg = REG_PRE_PLL_CLK_DIV, .val = 3},
+       {.length = MT9P012_16BIT, .reg = REG_PLL_MULTIPLIER, .val = 192},
+       {.length = MT9P012_16BIT, .reg = REG_OP_PIX_CLK_DIV, .val = 10},
+       {.length = MT9P012_16BIT, .reg = REG_OP_SYS_CLK_DIV, .val = 2},
+       {.length = MT9P012_16BIT, .reg = REG_RESERVED_MFR_3064, .val = 0x0805},
+       {.length = MT9P012_16BIT, .reg = REG_X_OUTPUT_SIZE,
+               .val = MT9P012_VIDEO_WIDTH_4X_BINN},
+       {.length = MT9P012_16BIT, .reg = REG_Y_OUTPUT_SIZE,
+               .val = MT9P012_VIDEO_HEIGHT_4X_BINN},
+       {.length = MT9P012_16BIT, .reg = REG_X_ADDR_START, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_START, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_X_ADDR_END, .val = 2593},
+       {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_END, .val = 1945},
+       {.length = MT9P012_16BIT, .reg = REG_READ_MODE, .val = 0x04FC},
+       {.length = MT9P012_16BIT, .reg = REG_FINE_INT_TIME, .val = 1794},
+       {.length = MT9P012_16BIT, .reg = REG_FRAME_LEN_LINES, .val = 1374},
+       {.length = MT9P012_16BIT, .reg = REG_LINE_LEN_PCK, .val = 3712},
+       /* 0x10/0x30 = 0.3333 */
+       {.length = MT9P012_16BIT, .reg = REG_SCALE_M, .val = 0x0030},
+       /* enable scaler */
+       {.length = MT9P012_16BIT, .reg = REG_SCALING_MODE, .val = 0x0002},
+       {.length = MT9P012_16BIT, .reg = REG_COARSE_INT_TIME,
+               .val = COARSE_INT_TIME_216_30FPS},
+       /* update */
+       {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x00},
+       {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0}
+       };
+
+
+       /*Video mode, 4x binning: 648 x 486, range 8 - 15 fps*/
+const static struct mt9p012_reg enter_video_648_15fps[] = {
+       /* stream off */
+       {.length = MT9P012_8BIT, .reg = REG_MODE_SELECT, .val = 0x00},
+       {.length = MT9P012_TOK_DELAY, .reg = 0x00, .val = 100},
+       /* hold */
+       {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x01},
+       {.length = MT9P012_16BIT, .reg = REG_VT_PIX_CLK_DIV, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_VT_SYS_CLK_DIV, .val = 2},
+       {.length = MT9P012_16BIT, .reg = REG_PRE_PLL_CLK_DIV, .val = 2},
+       {.length = MT9P012_16BIT, .reg = REG_PLL_MULTIPLIER, .val = 126},
+       {.length = MT9P012_16BIT, .reg = REG_OP_PIX_CLK_DIV, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_OP_SYS_CLK_DIV, .val = 2},
+       {.length = MT9P012_16BIT, .reg = REG_RESERVED_MFR_3064, .val = 0x0805},
+       {.length = MT9P012_16BIT, .reg = REG_X_OUTPUT_SIZE,
+               .val = MT9P012_VIDEO_WIDTH_4X_BINN},
+       {.length = MT9P012_16BIT, .reg = REG_Y_OUTPUT_SIZE,
+               .val = MT9P012_VIDEO_HEIGHT_4X_BINN},
+       {.length = MT9P012_16BIT, .reg = REG_X_ADDR_START, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_START, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_X_ADDR_END, .val = 2593},
+       {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_END, .val = 1945},
+       {.length = MT9P012_16BIT, .reg = REG_READ_MODE, .val = 0x04FC},
+       {.length = MT9P012_16BIT, .reg = REG_FINE_INT_TIME, .val = 1794},
+       {.length = MT9P012_16BIT, .reg = REG_FRAME_LEN_LINES, .val = 574},
+       {.length = MT9P012_16BIT, .reg = REG_LINE_LEN_PCK, .val = 2712},
+       {.length = MT9P012_16BIT, .reg = REG_SCALING_MODE, .val = 0x0000},
+       {.length = MT9P012_16BIT, .reg = REG_COARSE_INT_TIME,
+               .val = COARSE_INT_TIME_648},
+       /* update */
+       {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x00},
+       {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0}
+};
+
+       /* Video mode, 4x binning: 648 x 486, range 16 - 30 fps */
+const static struct mt9p012_reg enter_video_648_30fps[] = {
+       /* stream off */
+       {.length = MT9P012_8BIT, .reg = REG_MODE_SELECT, .val = 0x00},
+       {.length = MT9P012_TOK_DELAY, .reg = 0x00, .val = 100},
+       /* hold */
+       {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x01},
+       {.length = MT9P012_16BIT, .reg = REG_VT_PIX_CLK_DIV, .val = 5},
+       {.length = MT9P012_16BIT, .reg = REG_VT_SYS_CLK_DIV, .val = 2},
+       {.length = MT9P012_16BIT, .reg = REG_PRE_PLL_CLK_DIV, .val = 3},
+       {.length = MT9P012_16BIT, .reg = REG_PLL_MULTIPLIER, .val = 192},
+       {.length = MT9P012_16BIT, .reg = REG_OP_PIX_CLK_DIV, .val = 10},
+       {.length = MT9P012_16BIT, .reg = REG_OP_SYS_CLK_DIV, .val = 2},
+       {.length = MT9P012_16BIT, .reg = REG_RESERVED_MFR_3064, .val = 0x0805},
+       {.length = MT9P012_16BIT, .reg = REG_X_OUTPUT_SIZE,
+               .val = MT9P012_VIDEO_WIDTH_4X_BINN},
+       {.length = MT9P012_16BIT, .reg = REG_Y_OUTPUT_SIZE,
+               .val = MT9P012_VIDEO_HEIGHT_4X_BINN},
+       {.length = MT9P012_16BIT, .reg = REG_X_ADDR_START, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_START, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_X_ADDR_END, .val = 2593},
+       {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_END, .val = 1945},
+       {.length = MT9P012_16BIT, .reg = REG_READ_MODE, .val = 0x04FC},
+       {.length = MT9P012_16BIT, .reg = REG_FINE_INT_TIME, .val = 1794},
+       {.length = MT9P012_16BIT, .reg = REG_FRAME_LEN_LINES, .val = 1374},
+       {.length = MT9P012_16BIT, .reg = REG_LINE_LEN_PCK, .val = 3712},
+       {.length = MT9P012_16BIT, .reg = REG_SCALING_MODE, .val = 0x0000},
+       {.length = MT9P012_16BIT, .reg = REG_COARSE_INT_TIME,
+               .val = COARSE_INT_TIME_648_30FPS},
+       /* update */
+       {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x00},
+       {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0}
+};
+
+       /* Video mode, scaler off: 1296 x 972, range  11 - 21 fps*/
+const static struct mt9p012_reg enter_video_1296_15fps[] = {
+       /* stream off */
+       {.length = MT9P012_8BIT, .reg = REG_MODE_SELECT, .val = 0x00},
+       {.length = MT9P012_TOK_DELAY, .reg = 0x00, .val = 100},
+       /* hold */
+       {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x01},
+       {.length = MT9P012_16BIT, .reg = REG_VT_PIX_CLK_DIV, .val = 5},
+       {.length = MT9P012_16BIT, .reg = REG_VT_SYS_CLK_DIV, .val = 2},
+       {.length = MT9P012_16BIT, .reg = REG_PRE_PLL_CLK_DIV, .val = 3},
+       {.length = MT9P012_16BIT, .reg = REG_PLL_MULTIPLIER, .val = 134},
+       {.length = MT9P012_16BIT, .reg = REG_OP_PIX_CLK_DIV, .val = 10},
+       {.length = MT9P012_16BIT, .reg = REG_OP_SYS_CLK_DIV, .val = 1},
+       {.length = MT9P012_16BIT, .reg = REG_RESERVED_MFR_3064, .val = 0x0805},
+       {.length = MT9P012_16BIT, .reg = REG_X_OUTPUT_SIZE,
+               .val = MT9P012_VIDEO_WIDTH_2X_BINN},
+       {.length = MT9P012_16BIT, .reg = REG_Y_OUTPUT_SIZE,
+               .val = MT9P012_VIDEO_HEIGHT_2X_BINN},
+       {.length = MT9P012_16BIT, .reg = REG_X_ADDR_START, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_START, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_X_ADDR_END, .val = 2597},
+       {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_END, .val = 1949},
+       {.length = MT9P012_16BIT, .reg = REG_READ_MODE, .val = 0x046C},
+       {.length = MT9P012_16BIT, .reg = REG_FINE_INT_TIME, .val = 1794},
+       {.length = MT9P012_16BIT, .reg = REG_FRAME_LEN_LINES, .val = 1061},
+       {.length = MT9P012_16BIT, .reg = REG_LINE_LEN_PCK, .val = 3360},
+       {.length = MT9P012_16BIT, .reg = REG_SCALING_MODE, .val = 0x0000},
+       {.length = MT9P012_16BIT, .reg = REG_COARSE_INT_TIME,
+               .val = COARSE_INT_TIME_1296},
+       /* update */
+       {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x00},
+       {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0}
+};
+
+       /* YUV (YCbCr) VGA */
+const static struct mt9p012_reg enter_video_1296_30fps[] = {
+       /* stream off */
+       {.length = MT9P012_8BIT, .reg = REG_MODE_SELECT, .val = 0x00},
+       {.length = MT9P012_TOK_DELAY, .reg = 0x00, .val = 100},
+       /* hold */
+       {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x01},
+       {.length = MT9P012_16BIT, .reg = REG_VT_PIX_CLK_DIV, .val = 5},
+       {.length = MT9P012_16BIT, .reg = REG_VT_SYS_CLK_DIV, .val = 1},
+       {.length = MT9P012_16BIT, .reg = REG_PRE_PLL_CLK_DIV, .val = 3},
+       {.length = MT9P012_16BIT, .reg = REG_PLL_MULTIPLIER, .val = 134},
+       {.length = MT9P012_16BIT, .reg = REG_OP_PIX_CLK_DIV, .val = 10},
+       {.length = MT9P012_16BIT, .reg = REG_OP_SYS_CLK_DIV, .val = 1},
+       {.length = MT9P012_16BIT, .reg = REG_RESERVED_MFR_3064, .val = 0x0805},
+       {.length = MT9P012_16BIT, .reg = REG_X_OUTPUT_SIZE,
+               .val = MT9P012_VIDEO_WIDTH_2X_BINN},
+       {.length = MT9P012_16BIT, .reg = REG_Y_OUTPUT_SIZE,
+               .val = MT9P012_VIDEO_HEIGHT_2X_BINN},
+       {.length = MT9P012_16BIT, .reg = REG_X_ADDR_START, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_START, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_X_ADDR_END, .val = 2597},
+       {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_END, .val = 1949},
+       {.length = MT9P012_16BIT, .reg = REG_READ_MODE, .val = 0x046C},
+       {.length = MT9P012_16BIT, .reg = REG_FINE_INT_TIME, .val = 1794},
+       {.length = MT9P012_16BIT, .reg = REG_FRAME_LEN_LINES, .val = 1061},
+       {.length = MT9P012_16BIT, .reg = REG_LINE_LEN_PCK, .val = 3360},
+       {.length = MT9P012_16BIT, .reg = REG_SCALING_MODE, .val = 0x0000},
+       {.length = MT9P012_16BIT, .reg = REG_COARSE_INT_TIME,
+               .val = COARSE_INT_TIME_1296},
+        /* update */
+       {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x00},
+       {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0}
+};
+
+const static struct mt9p012_reg enter_image_mode_3MP_10fps[] = {
+       /* stream off */
+       {.length = MT9P012_8BIT, .reg = REG_MODE_SELECT, .val = 0x00},
+       {.length = MT9P012_TOK_DELAY, .reg = 0x00, .val = 100},
+       /* hold */
+       {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x01},
+       {.length = MT9P012_16BIT, .reg = REG_VT_PIX_CLK_DIV, .val = 4},
+       {.length = MT9P012_16BIT, .reg = REG_VT_SYS_CLK_DIV, .val = 1},
+       {.length = MT9P012_16BIT, .reg = REG_PRE_PLL_CLK_DIV, .val = 5},
+       /* 10 fps */
+       {.length = MT9P012_16BIT, .reg = REG_PLL_MULTIPLIER, .val = 184},
+       {.length = MT9P012_16BIT, .reg = REG_OP_PIX_CLK_DIV, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_OP_SYS_CLK_DIV, .val = 1},
+       {.length = MT9P012_16BIT, .reg = REG_RESERVED_MFR_3064, .val = 0x0805},
+       {.length = MT9P012_16BIT, .reg = REG_X_OUTPUT_SIZE,
+               .val = MT9P012_IMAGE_WIDTH_MIN},
+       {.length = MT9P012_16BIT, .reg = REG_Y_OUTPUT_SIZE,
+               .val = MT9P012_IMAGE_HEIGHT_MIN},
+       {.length = MT9P012_16BIT, .reg = REG_X_ADDR_START, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_START, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_X_ADDR_END, .val = 2599},
+       {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_END, .val = 1951},
+       {.length = MT9P012_16BIT, .reg = REG_READ_MODE, .val = 0x0024},
+       {.length = MT9P012_16BIT, .reg = REG_FINE_INT_TIME, .val = 882},
+       {.length = MT9P012_16BIT, .reg = REG_FRAME_LEN_LINES, .val = 2056},
+       {.length = MT9P012_16BIT, .reg = REG_LINE_LEN_PCK, .val = 5372},
+       /* 0x10/0x14 = 0.80 */
+       {.length = MT9P012_16BIT, .reg = REG_SCALE_M, .val = 0x0014},
+       /* enable scaler */
+       {.length = MT9P012_16BIT, .reg = REG_SCALING_MODE, .val = 0x0002},
+       {.length = MT9P012_16BIT, .reg = REG_TEST_PATTERN, .val = TST_PAT},
+       {.length = MT9P012_16BIT, .reg = REG_COARSE_INT_TIME,
+               .val = COARSE_INT_TIME_3MP},
+       /* update */
+       {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x00},
+       {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0}
+};
+
+/* Image mode, 5 MP @ 10 fps */
+const static struct mt9p012_reg enter_image_mode_5MP_10fps[] = {
+       /* stream off */
+       {.length = MT9P012_8BIT, .reg = REG_MODE_SELECT, .val = 0x00},
+       {.length = MT9P012_TOK_DELAY, .reg = 0x00, .val = 100},
+       /* hold */
+       {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x01},
+       {.length = MT9P012_16BIT, .reg = REG_VT_PIX_CLK_DIV, .val = 4},
+       {.length = MT9P012_16BIT, .reg = REG_VT_SYS_CLK_DIV, .val = 1},
+       {.length = MT9P012_16BIT, .reg = REG_PRE_PLL_CLK_DIV, .val = 5},
+       /* 10 fps */
+       {.length = MT9P012_16BIT, .reg = REG_PLL_MULTIPLIER, .val = 184},
+       {.length = MT9P012_16BIT, .reg = REG_OP_PIX_CLK_DIV, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_OP_SYS_CLK_DIV, .val = 1},
+       {.length = MT9P012_16BIT, .reg = REG_RESERVED_MFR_3064, .val = 0x0805},
+       {.length = MT9P012_16BIT, .reg = REG_X_OUTPUT_SIZE,
+               .val = MT9P012_IMAGE_WIDTH_MAX},
+       {.length = MT9P012_16BIT, .reg = REG_Y_OUTPUT_SIZE,
+               .val = MT9P012_IMAGE_HEIGHT_MAX},
+       {.length = MT9P012_16BIT, .reg = REG_X_ADDR_START, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_START, .val = 8},
+       {.length = MT9P012_16BIT, .reg = REG_X_ADDR_END, .val = 2599},
+       {.length = MT9P012_16BIT, .reg = REG_Y_ADDR_END, .val = 1951},
+       {.length = MT9P012_16BIT, .reg = REG_READ_MODE, .val = 0x0024},
+       {.length = MT9P012_16BIT, .reg = REG_FINE_INT_TIME, .val = 882},
+       {.length = MT9P012_16BIT, .reg = REG_FRAME_LEN_LINES, .val = 2056},
+       {.length = MT9P012_16BIT, .reg = REG_LINE_LEN_PCK, .val = 5372},
+       {.length = MT9P012_16BIT, .reg = REG_SCALE_M, .val = 0x0000},
+       /* disable scaler */
+       {.length = MT9P012_16BIT, .reg = REG_SCALING_MODE, .val = 0x0000},
+       {.length = MT9P012_16BIT, .reg = REG_COARSE_INT_TIME,
+               .val = COARSE_INT_TIME_5MP},
+       /* update */
+       {.length = MT9P012_8BIT, .reg = REG_GROUPED_PAR_HOLD, .val = 0x00},
+       {.length = MT9P012_TOK_TERM, .reg = 0, .val = 0}
+};
+
+static u32 min_exposure_time;
+static u32 max_exposure_time;
+static u32 pix_clk_freq;
+
+/* Structure to set frame rate */
+static struct mt9p012_reg set_fps[2];
+
+/**
+ * struct mt9p012_pll_settings - struct for storage of sensor pll values
+ * @vt_pix_clk_div: vertical pixel clock divider
+ * @vt_sys_clk_div: veritcal system clock divider
+ * @pre_pll_div: pre pll divider
+ * @fine_int_tm: fine resolution interval time
+ * @frame_lines: number of lines in frame
+ * @line_len: number of pixels in line
+ * @min_pll: minimum pll multiplier
+ * @max_pll: maximum pll multiplier
+ */
+const static struct mt9p012_pll_settings all_pll_settings[] = {
+       /* PLL_5MP */
+       {.vt_pix_clk_div = 4, .vt_sys_clk_div = 1, .pre_pll_div = 5,
+       .fine_int_tm = 882, .frame_lines = 2056, .line_len = 5372,
+       .min_pll = 160, .max_pll = 200},
+       /* PLL_3MP */
+       {.vt_pix_clk_div = 4, .vt_sys_clk_div = 1, .pre_pll_div = 5,
+       .fine_int_tm = 882, .frame_lines = 2056, .line_len = 5372,
+       .min_pll = 160, .max_pll = 200},
+       /* PLL_1296_15FPS */
+       {.vt_pix_clk_div = 5, .vt_sys_clk_div = 2, .pre_pll_div = 3,
+       .fine_int_tm = 1794, .frame_lines = 1061, .line_len = 3360,
+       .min_pll = 96, .max_pll = 190},
+       /* PLL_1296_30FPS */
+       {.vt_pix_clk_div = 5, .vt_sys_clk_div = 1, .pre_pll_div = 3,
+       .fine_int_tm = 1794, .frame_lines = 1061, .line_len = 3360,
+       .min_pll = 96, .max_pll = 150},
+       /* PLL_648_15FPS */
+       {.vt_pix_clk_div = 8, .vt_sys_clk_div = 2, .pre_pll_div = 2,
+       .fine_int_tm = 1794, .frame_lines = 574, .line_len = 2712,
+       .min_pll = 92, .max_pll = 128},
+       /* PLL_648_30FPS */
+       {.vt_pix_clk_div = 5, .vt_sys_clk_div = 2, .pre_pll_div = 3,
+       .fine_int_tm = 1794, .frame_lines = 1374, .line_len = 3712,
+       .min_pll = 96, .max_pll = 192},
+       /* PLL_216_15FPS */
+       {.vt_pix_clk_div = 8, .vt_sys_clk_div = 2, .pre_pll_div = 2,
+       .fine_int_tm = 1794,  .frame_lines = 574, .line_len = 2712,
+       .min_pll = 92, .max_pll = 126},
+       /* PLL_216_30FPS */
+       {.vt_pix_clk_div = 5, .vt_sys_clk_div = 2, .pre_pll_div = 3,
+       .fine_int_tm = 1794,  .frame_lines = 1374, .line_len = 3712,
+       .min_pll = 96, .max_pll = 192}
+};
+
+static enum mt9p012_pll_type current_pll_video;
+
+const static struct mt9p012_reg *
+       mt9p012_reg_init[NUM_FPS][NUM_IMAGE_SIZES] =
+{
+       {
+               enter_video_216_15fps,
+               enter_video_648_15fps,
+               enter_video_1296_15fps,
+               enter_image_mode_3MP_10fps,
+               enter_image_mode_5MP_10fps
+       },
+       {
+               enter_video_216_30fps,
+               enter_video_648_30fps,
+               enter_video_1296_30fps,
+               enter_image_mode_3MP_10fps,
+               enter_image_mode_5MP_10fps
+       },
+};
+
+/**
+ * struct vcontrol - Video controls
+ * @v4l2_queryctrl: V4L2 VIDIOC_QUERYCTRL ioctl structure
+ * @current_value: current value of this control
+ */
+static struct vcontrol {
+       struct v4l2_queryctrl qc;
+       int current_value;
+} video_control[] = {
+       {
+               {
+                       .id = V4L2_CID_EXPOSURE,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "Exposure",
+                       .minimum = DEF_MIN_EXPOSURE,
+                       .maximum = DEF_MAX_EXPOSURE,
+                       .step = EXPOSURE_STEP,
+                       .default_value = DEF_EXPOSURE,
+               },
+               .current_value = DEF_EXPOSURE,
+       },
+       {
+               {
+                       .id = V4L2_CID_GAIN,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "Gain",
+                       .minimum = MIN_GAIN,
+                       .maximum = MAX_GAIN,
+                       .step = GAIN_STEP,
+                       .default_value = DEF_GAIN,
+               },
+               .current_value = DEF_GAIN,
+       }
+};
+
+/**
+ * find_vctrl - Finds the requested ID in the video control structure array
+ * @id: ID of control to search the video control array for
+ *
+ * Returns the index of the requested ID from the control structure array
+ */
+static int
+find_vctrl(int id)
+{
+       int i;
+
+       if (id < V4L2_CID_BASE)
+               return -EDOM;
+
+       for (i = (ARRAY_SIZE(video_control) - 1); i >= 0; i--)
+               if (video_control[i].qc.id == id)
+                       break;
+       if (i < 0)
+               i = -EINVAL;
+       return i;
+}
+
+/**
+ * mt9p012_read_reg - Read a value from a register in an mt9p012 sensor device
+ * @client: i2c driver client structure
+ * @data_length: length of data to be read
+ * @reg: register address / offset
+ * @val: stores the value that gets read
+ *
+ * Read a value from a register in an mt9p012 sensor device.
+ * The value is returned in 'val'.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int
+mt9p012_read_reg(struct i2c_client *client, u16 data_length, u16 reg, u32 *val)
+{
+       int err;
+       struct i2c_msg msg[1];
+       unsigned char data[4];
+
+       if (!client->adapter)
+               return -ENODEV;
+       if (data_length != MT9P012_8BIT && data_length != MT9P012_16BIT
+                                       && data_length != MT9P012_32BIT)
+               return -EINVAL;
+
+       msg->addr = client->addr;
+       msg->flags = 0;
+       msg->len = 2;
+       msg->buf = data;
+
+       /* high byte goes out first */
+       data[0] = (u8) (reg >> 8);;
+       data[1] = (u8) (reg & 0xff);
+       err = i2c_transfer(client->adapter, msg, 1);
+       if (err >= 0) {
+               msg->len = data_length;
+               msg->flags = I2C_M_RD;
+               err = i2c_transfer(client->adapter, msg, 1);
+       }
+       if (err >= 0) {
+               *val = 0;
+               /* high byte comes first */
+               if (data_length == MT9P012_8BIT)
+                       *val = data[0];
+               else if (data_length == MT9P012_16BIT)
+                       *val = data[1] + (data[0] << 8);
+               else
+                       *val = data[3] + (data[2] << 8) +
+                               (data[1] << 16) + (data[0] << 24);
+               return 0;
+       }
+       dev_err(&client->dev, "read from offset 0x%x error %d", reg, err);
+       return err;
+}
+/**
+ * mt9p012_write_reg - Write a value to a register in an mt9p012 sensor device
+ * @client: i2c driver client structure
+ * @data_length: length of data to be read
+ * @reg: register address / offset
+ * @val: value to be written to specified register
+ *
+ * Write a value to a register in an mt9p012 sensor device.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int
+mt9p012_write_reg(struct i2c_client *client, u16 data_length, u16 reg, u32 val)
+{
+       int err;
+       struct i2c_msg msg[1];
+       unsigned char data[6];
+       int retry = 0;
+
+       if (!client->adapter)
+               return -ENODEV;
+
+       if (data_length != MT9P012_8BIT && data_length != MT9P012_16BIT
+                                       && data_length != MT9P012_32BIT)
+               return -EINVAL;
+
+again:
+       msg->addr = client->addr;
+       msg->flags = 0;
+       msg->len = 2 + data_length;
+       msg->buf = data;
+
+       /* high byte goes out first */
+       data[0] = (u8) (reg >> 8);;
+       data[1] = (u8) (reg & 0xff);
+
+       if (data_length == MT9P012_8BIT)
+               data[2] = (u8) (val & 0xff);
+       else if (data_length == MT9P012_16BIT) {
+               data[2] = (u8) (val >> 8);
+               data[3] = (u8) (val & 0xff);
+       } else {
+               data[2] = (u8) (val >> 24);
+               data[3] = (u8) (val >> 16);
+               data[4] = (u8) (val >> 8);
+               data[5] = (u8) (val & 0xff);
+       }
+
+       err = i2c_transfer(client->adapter, msg, 1);
+       if (err >= 0)
+               return 0;
+
+       v4l_dbg(1, debug, client, "wrote 0x%x to offset 0x%x error %d", val,
+                                                       reg, err);
+       if (retry <= MT9P012_I2C_RETRY_COUNT) {
+               v4l_warn(client, "retry ... %d", retry);
+               retry++;
+               mdelay(20);
+               goto again;
+       }
+       return err;
+}
+
+/**
+ * mt9p012_write_regs - Initializes a list of MT9P012 registers
+ * @client: i2c driver client structure
+ * @reglist: list of registers to be written
+ *
+ * Initializes a list of MT9P012 registers. The list of registers is
+ * terminated by MT9P012_TOK_TERM.
+ */
+static int
+mt9p012_write_regs(struct i2c_client *client,
+                       const struct mt9p012_reg reglist[])
+{
+       int err;
+       const struct mt9p012_reg *next = reglist;
+
+       for (; next->length != MT9P012_TOK_TERM; next++) {
+               if (next->length == MT9P012_TOK_DELAY) {
+                       mdelay(next->val);
+                       continue;
+               }
+
+               err = mt9p012_write_reg(client, next->length,
+                                               next->reg, next->val);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+/**
+ * mt9p012_calc_pll - Calculate PLL settings based on input image size
+ * @isize: enum value corresponding to image size
+ * @xclk: xclk value (calculate by mt9p012sensor_calc_xclk())
+ * @sensor: pointer to sensor device information structure
+ *
+ * Calculates sensor PLL related settings (scaler, fps, pll_multiplier,
+ * pix_clk_freq, min_exposure_time, max_exposure_time) based on input
+ * image size.  It then applies the fps register settings based on
+ * these calculations.
+ */
+static int
+mt9p012_calc_pll(enum image_size isize,
+               unsigned long xclk,
+               struct mt9p012_sensor *sensor)
+{
+       int err = 0, row = 1, i = 0;
+       unsigned int vt_pix_clk;
+       unsigned int pll_multiplier;
+       unsigned int exposure_factor, pix_clk_scaled;
+       struct i2c_client *client = sensor->i2c_client;
+       struct vcontrol *lvc;
+
+       /* Greater than 1296x972
+       1. Scaler is 0
+       2. fps is 10
+       3. Apply image mode settings
+       4. Turn Streaming ON.
+       5. Exit
+       */
+       if (isize > BIN2X) {
+               /* Burst Mode */
+               sensor->scaler = 0;
+               sensor->fps = 10;
+               current_pll_video = PLL_5MP;
+               return 0;
+       }
+
+       /* Greater than 648X486 case
+       1. Scaler is 0
+       2. If fps>21 then choose PLL for 30
+       3. If fps<21 then choose PLL for 15
+
+       Greater than 216X162 case
+       1. Scaler is 1
+       2. If fps>15 then choose PLL for 30
+       3. If fps<15 then choose PLL for 15
+
+       Greater than 0 to 216x162
+       1. Scaler is 2.
+       2. If fps>15 then choose PLL for 30
+       3. If fps<15 then choose PLL for 15
+       */
+
+       if (isize > BIN4X) {
+               sensor->scaler = 0;
+               if (sensor->fps > 21)
+                       current_pll_video = PLL_1296_30FPS;
+               else
+                       current_pll_video = PLL_1296_15FPS;
+       } else if (isize > BIN4XSCALE) {
+               sensor->scaler = 1;
+               if (sensor->fps > 15)
+                       current_pll_video = PLL_648_30FPS;
+               else
+                       current_pll_video = PLL_648_15FPS;
+       } else {
+               sensor->scaler = 2;
+               if (sensor->fps > 15)
+                       current_pll_video = PLL_216_30FPS;
+               else
+                       current_pll_video = PLL_216_15FPS;
+       }
+
+       /* Row adjustment */
+       if (sensor->scaler && (sensor->fps < 16))
+               row = 2; /* Adjustment when using 4x binning and 12 MHz clk */
+
+       /* Calculate the PLL, set fps register */
+       vt_pix_clk = sensor->fps *
+               all_pll_settings[current_pll_video].frame_lines *
+               all_pll_settings[current_pll_video].line_len;
+
+       pll_multiplier =
+               (((vt_pix_clk
+                  * all_pll_settings[current_pll_video].vt_pix_clk_div
+                  * all_pll_settings[current_pll_video].vt_sys_clk_div
+                  * row) / xclk)
+                  * all_pll_settings[current_pll_video].pre_pll_div) + 1;
+
+       if (pll_multiplier < all_pll_settings[current_pll_video].min_pll)
+               pll_multiplier = all_pll_settings[current_pll_video].min_pll;
+       else if (pll_multiplier > all_pll_settings[current_pll_video].max_pll)
+               pll_multiplier = all_pll_settings[current_pll_video].max_pll;
+
+       pix_clk_freq = (xclk /
+                       (all_pll_settings[current_pll_video].pre_pll_div
+                        * all_pll_settings[current_pll_video].vt_pix_clk_div
+                        * all_pll_settings[current_pll_video].vt_sys_clk_div
+                        * row)) * pll_multiplier;
+       min_exposure_time = (all_pll_settings[current_pll_video].fine_int_tm
+                            * 1000000 / pix_clk_freq) + 1;
+       exposure_factor = (all_pll_settings[current_pll_video].frame_lines - 1)
+                               * all_pll_settings[current_pll_video].line_len;
+       exposure_factor += all_pll_settings[current_pll_video].fine_int_tm;
+       exposure_factor *= 100;
+       pix_clk_scaled = pix_clk_freq / 100;
+       max_exposure_time = (exposure_factor / pix_clk_scaled) * 100;
+
+       /* Apply the fps settings */
+       set_fps[0].length = MT9P012_16BIT;
+       set_fps[0].reg = REG_PLL_MULTIPLIER;
+       set_fps[0].val = pll_multiplier;
+       set_fps[1].length = MT9P012_TOK_TERM;
+       set_fps[1].reg = 0;
+       set_fps[1].val = 0;
+
+       /* Update min/max for query control */
+       i = find_vctrl(V4L2_CID_EXPOSURE);
+       if (i >= 0) {
+               lvc = &video_control[i];
+               lvc->qc.minimum = min_exposure_time;
+               lvc->qc.maximum = max_exposure_time;
+       }
+
+       err = mt9p012_write_regs(client, set_fps);
+       return err;
+}
+
+/**
+ * mt9p012_calc_size - Find the best match for a requested image capture size
+ * @width: requested image width in pixels
+ * @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 enum image_size mt9p012_calc_size(unsigned int width,
+                                                       unsigned int height)
+{
+       enum image_size isize;
+       unsigned long pixels = width * height;
+
+       for (isize = BIN4XSCALE; isize <= FIVE_MP; isize++) {
+               if (mt9p012_sizes[isize].height *
+                                       mt9p012_sizes[isize].width >= pixels) {
+                       /* To improve image quality in VGA */
+                       if ((pixels > CIF_PIXELS) && (isize == BIN4X)) {
+                               isize = BIN2X;
+                       } else if ((pixels > QQVGA_PIXELS) &&
+                                                       (isize == BIN4XSCALE)) {
+                               isize = BIN4X;
+                       }
+                       return isize;
+               }
+       }
+
+       return FIVE_MP;
+}
+
+/**
+ * mt9p012_find_isize - Find the best match for a requested image capture size
+ * @width: requested image width in pixels
+ * @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 enum image_size mt9p012_find_isize(unsigned int width)
+{
+       enum image_size isize;
+
+       for (isize = BIN4XSCALE; isize <= FIVE_MP; isize++) {
+               if (mt9p012_sizes[isize].width >= width)
+                       break;
+       }
+
+       return isize;
+}
+/**
+ * mt9p012_find_fps_index - Find the best fps range match for a
+ *  requested frame rate
+ * @fps: desired frame rate
+ * @isize: enum value corresponding to image size
+ *
+ * Find the best match for a requested frame rate.  The best match
+ * is chosen between two fps ranges (11 - 15 and 16 - 30 fps) depending on
+ * the image size. For image sizes larger than BIN2X, frame rate is fixed
+ * at 10 fps.
+ */
+static unsigned int mt9p012_find_fps_index(unsigned int fps,
+                                                       enum image_size isize)
+{
+       unsigned int index = FPS_LOW_RANGE;
+
+       if (isize > BIN4X) {
+               if (fps > 21)
+                       index = FPS_HIGH_RANGE;
+       } else {
+               if (fps > 15)
+                       index = FPS_HIGH_RANGE;
+       }
+
+       return index;
+}
+
+/**
+ * mt9p012sensor_calc_xclk - Calculate the required xclk frequency
+ * @c: i2c client driver structure
+ *
+ * Given the image capture format in pix, the nominal frame period in
+ * timeperframe, calculate and return the required xclk frequency
+ */
+static unsigned long mt9p012sensor_calc_xclk(struct i2c_client *c)
+{
+       struct mt9p012_sensor *sensor = i2c_get_clientdata(c);
+       struct v4l2_fract *timeperframe = &sensor->timeperframe;
+       struct v4l2_pix_format *pix = &sensor->pix;
+
+       if ((timeperframe->numerator == 0)
+       || (timeperframe->denominator == 0)) {
+               /* supply a default nominal_timeperframe */
+               timeperframe->numerator = 1;
+               timeperframe->denominator = MT9P012_DEF_FPS;
+       }
+
+       sensor->fps = timeperframe->denominator/timeperframe->numerator;
+       if (sensor->fps < MT9P012_MIN_FPS)
+               sensor->fps = MT9P012_MIN_FPS;
+       else if (sensor->fps > MT9P012_MAX_FPS)
+               sensor->fps = MT9P012_MAX_FPS;
+
+       timeperframe->numerator = 1;
+       timeperframe->denominator = sensor->fps;
+
+       if ((pix->width <= MT9P012_VIDEO_WIDTH_4X_BINN) && (sensor->fps > 15))
+               xclk_current = MT9P012_XCLK_NOM_2;
+       else
+               xclk_current = MT9P012_XCLK_NOM_1;
+
+       return xclk_current;
+}
+
+/**
+ * mt9p012_configure - Configure the mt9p012 for the specified image mode
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Configure the mt9p012 for a specified image size, pixel format, and frame
+ * period.  xclk is the frequency (in Hz) of the xclk input to the mt9p012.
+ * fper is the frame period (in seconds) expressed as a fraction.
+ * Returns zero if successful, or non-zero otherwise.
+ * The actual frame period is returned in fper.
+ */
+static int mt9p012_configure(struct v4l2_int_device *s)
+{
+       struct mt9p012_sensor *sensor = s->priv;
+       struct v4l2_pix_format *pix = &sensor->pix;
+       struct i2c_client *client = sensor->i2c_client;
+       enum image_size isize;
+       unsigned int fps_index;
+       int err;
+
+       isize = mt9p012_find_isize(pix->width);
+
+       /* common register initialization */
+       err = mt9p012_write_regs(client, mt9p012_common);
+       if (err)
+               return err;
+
+       fps_index = mt9p012_find_fps_index(sensor->fps, isize);
+
+       /* configure image size and pixel format */
+       err = mt9p012_write_regs(client, mt9p012_reg_init[fps_index][isize]);
+       if (err)
+               return err;
+
+       /* configure frame rate */
+       err = mt9p012_calc_pll(isize, xclk_current, sensor);
+       if (err)
+               return err;
+
+       /* configure streaming ON */
+       err = mt9p012_write_regs(client, stream_on_list);
+
+       return err;
+}
+
+/**
+ * mt9p012_detect - Detect if an mt9p012 is present, and if so which revision
+ * @client: pointer to the i2c client driver structure
+ *
+ * Detect if an mt9p012 is present, and if so which revision.
+ * A device is considered to be detected if the manufacturer ID (MIDH and MIDL)
+ * and the product ID (PID) registers match the expected values.
+ * Any value of the version ID (VER) register is accepted.
+ * Here are the version numbers we know about:
+ *     0x48 --> mt9p012 Revision 1 or mt9p012 Revision 2
+ *     0x49 --> mt9p012 Revision 3
+ * Returns a negative error number if no device is detected, or the
+ * non-negative value of the version ID register if a device is detected.
+ */
+static int
+mt9p012_detect(struct i2c_client *client)
+{
+       u32 model_id, mfr_id, rev;
+
+       if (!client)
+               return -ENODEV;
+
+       if (mt9p012_read_reg(client, MT9P012_16BIT, REG_MODEL_ID, &model_id))
+               return -ENODEV;
+       if (mt9p012_read_reg(client, MT9P012_8BIT, REG_MANUFACTURER_ID,
+                               &mfr_id))
+               return -ENODEV;
+       if (mt9p012_read_reg(client, MT9P012_8BIT, REG_REVISION_NUMBER, &rev))
+               return -ENODEV;
+
+       dev_info(&client->dev, "model id detected 0x%x mfr 0x%x\n", model_id,
+                                                               mfr_id);
+       if ((model_id != MT9P012_MOD_ID) || (mfr_id != MT9P012_MFR_ID)) {
+               /* We didn't read the values we expected, so
+                * this must not be an MT9P012.
+                */
+               dev_warn(&client->dev, "model id mismatch 0x%x mfr 0x%x\n",
+                       model_id, mfr_id);
+
+               return -ENODEV;
+       }
+       return 0;
+
+}
+
+/**
+ * mt9p012sensor_set_exposure_time - sets exposure time per input value
+ * @exp_time: exposure time to be set on device
+ * @s: pointer to standard V4L2 device structure
+ * @lvc: pointer to V4L2 exposure entry in video_controls array
+ *
+ * If the requested exposure time is within the allowed limits, the HW
+ * is configured to use the new exposure time, and the video_controls
+ * array is updated with the new current value.
+ * The function returns 0 upon success.  Otherwise an error code is
+ * returned.
+ */
+int
+mt9p012sensor_set_exposure_time(u32 exp_time, struct v4l2_int_device *s,
+                                               struct vcontrol *lvc)
+{
+       int err;
+       struct mt9p012_sensor *sensor = s->priv;
+       struct i2c_client *client = sensor->i2c_client;
+       u32 coarse_int_time = 0;
+
+       if ((exp_time < min_exposure_time) ||
+                       (exp_time > max_exposure_time)) {
+               dev_err(&client->dev, "Exposure time not within the "
+                       "legal range.\n");
+               dev_err(&client->dev, "Min time %d us Max time %d us",
+                       min_exposure_time, max_exposure_time);
+               return -EINVAL;
+       }
+       coarse_int_time = ((((exp_time / 10) * (pix_clk_freq / 1000)) / 1000) -
+               (all_pll_settings[current_pll_video].fine_int_tm / 10)) /
+               (all_pll_settings[current_pll_video].line_len / 10);
+
+       dev_dbg(&client->dev, "coarse_int_time calculated = %d\n",
+                                               coarse_int_time);
+
+       set_exposure_time[COARSE_INT_TIME_INDEX].val = coarse_int_time;
+       err = mt9p012_write_regs(client, set_exposure_time);
+
+       if (err)
+               dev_err(&client->dev, "Error setting exposure time %d\n",
+                                                                       err);
+       else
+               lvc->current_value = exp_time;
+
+       return err;
+}
+
+/**
+ * mt9p012sensor_set_gain - sets sensor analog gain per input value
+ * @gain: analog gain value to be set on device
+ * @s: pointer to standard V4L2 device structure
+ * @lvc: pointer to V4L2 analog gain entry in video_controls array
+ *
+ * If the requested analog gain is within the allowed limits, the HW
+ * is configured to use the new gain value, and the video_controls
+ * array is updated with the new current value.
+ * The function returns 0 upon success.  Otherwise an error code is
+ * returned.
+ */
+int
+mt9p012sensor_set_gain(u16 gain, struct v4l2_int_device *s,
+                                       struct vcontrol *lvc)
+{
+       int err;
+       struct mt9p012_sensor *sensor = s->priv;
+       struct i2c_client *client = sensor->i2c_client;
+
+       if ((gain < MIN_GAIN) || (gain > MAX_GAIN)) {
+               dev_err(&client->dev, "Gain not within the legal range");
+               return -EINVAL;
+       }
+       set_analog_gain[GAIN_INDEX].val = gain;
+       err = mt9p012_write_regs(client, set_analog_gain);
+       if (err) {
+               dev_err(&client->dev, "Error setting gain.%d", err);
+               return err;
+       } else
+               lvc->current_value = gain;
+
+       return err;
+}
+
+/**
+ * ioctl_queryctrl - V4L2 sensor interface handler for VIDIOC_QUERYCTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @qc: standard V4L2 VIDIOC_QUERYCTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control information
+ * from the video_control[] array.  Otherwise, returns -EINVAL if the
+ * control is not supported.
+ */
+static int ioctl_queryctrl(struct v4l2_int_device *s,
+                               struct v4l2_queryctrl *qc)
+{
+       int i;
+
+       i = find_vctrl(qc->id);
+       if (i == -EINVAL)
+               qc->flags = V4L2_CTRL_FLAG_DISABLED;
+
+       if (i < 0)
+               return -EINVAL;
+
+       *qc = video_control[i].qc;
+       return 0;
+}
+
+/**
+ * ioctl_g_ctrl - V4L2 sensor interface handler for VIDIOC_G_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_G_CTRL ioctl structure
+ *
+ * If the requested control is supported, returns the control's current
+ * value from the video_control[] array.  Otherwise, returns -EINVAL
+ * if the control is not supported.
+ */
+static int ioctl_g_ctrl(struct v4l2_int_device *s,
+                            struct v4l2_control *vc)
+{
+       struct vcontrol *lvc;
+       int i;
+
+       i = find_vctrl(vc->id);
+       if (i < 0)
+               return -EINVAL;
+       lvc = &video_control[i];
+
+       switch (vc->id) {
+       case  V4L2_CID_EXPOSURE:
+               vc->value = lvc->current_value;
+               break;
+       case V4L2_CID_GAIN:
+               vc->value = lvc->current_value;
+               break;
+       }
+
+       return 0;
+}
+
+/**
+ * ioctl_s_ctrl - V4L2 sensor interface handler for VIDIOC_S_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @vc: standard V4L2 VIDIOC_S_CTRL ioctl structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW (and updates the video_control[] array).  Otherwise,
+ * returns -EINVAL if the control is not supported.
+ */
+static int ioctl_s_ctrl(struct v4l2_int_device *s,
+                            struct v4l2_control *vc)
+{
+       int retval = -EINVAL;
+       int i;
+       struct vcontrol *lvc;
+
+       i = find_vctrl(vc->id);
+       if (i < 0)
+               return -EINVAL;
+       lvc = &video_control[i];
+
+       switch (vc->id) {
+       case V4L2_CID_EXPOSURE:
+               retval = mt9p012sensor_set_exposure_time(vc->value, s, lvc);
+               break;
+       case V4L2_CID_GAIN:
+               retval = mt9p012sensor_set_gain(vc->value, s, lvc);
+               break;
+       }
+
+       return retval;
+}
+
+
+/**
+ * ioctl_enum_fmt_cap - Implement the CAPTURE buffer VIDIOC_ENUM_FMT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @fmt: standard V4L2 VIDIOC_ENUM_FMT ioctl structure
+ *
+ * Implement the VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type.
+ */
+static int ioctl_enum_fmt_cap(struct v4l2_int_device *s,
+                                  struct v4l2_fmtdesc *fmt)
+{
+       int index = fmt->index;
+       enum v4l2_buf_type type = fmt->type;
+
+       memset(fmt, 0, sizeof(*fmt));
+       fmt->index = index;
+       fmt->type = type;
+
+       switch (fmt->type) {
+       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+               if (index >= NUM_CAPTURE_FORMATS)
+                       return -EINVAL;
+       break;
+       default:
+               return -EINVAL;
+       }
+
+       fmt->flags = mt9p012_formats[index].flags;
+       strlcpy(fmt->description, mt9p012_formats[index].description,
+                                       sizeof(fmt->description));
+       fmt->pixelformat = mt9p012_formats[index].pixelformat;
+
+       return 0;
+}
+
+/**
+ * ioctl_try_fmt_cap - Implement the CAPTURE buffer VIDIOC_TRY_FMT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure
+ *
+ * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type.  This
+ * ioctl is used to negotiate the image capture size and pixel format
+ * without actually making it take effect.
+ */
+static int ioctl_try_fmt_cap(struct v4l2_int_device *s,
+                            struct v4l2_format *f)
+{
+       enum image_size isize;
+       int ifmt;
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+       struct mt9p012_sensor *sensor = s->priv;
+       struct v4l2_pix_format *pix2 = &sensor->pix;
+
+       isize = mt9p012_calc_size(pix->width, pix->height);
+
+       pix->width = mt9p012_sizes[isize].width;
+       pix->height = mt9p012_sizes[isize].height;
+       for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) {
+               if (pix->pixelformat == mt9p012_formats[ifmt].pixelformat)
+                       break;
+       }
+       if (ifmt == NUM_CAPTURE_FORMATS)
+               ifmt = 0;
+       pix->pixelformat = mt9p012_formats[ifmt].pixelformat;
+       pix->field = V4L2_FIELD_NONE;
+       pix->bytesperline = pix->width * 2;
+       pix->sizeimage = pix->bytesperline * pix->height;
+       pix->priv = 0;
+       switch (pix->pixelformat) {
+       case V4L2_PIX_FMT_YUYV:
+       case V4L2_PIX_FMT_UYVY:
+               pix->colorspace = V4L2_COLORSPACE_JPEG;
+               break;
+       case V4L2_PIX_FMT_RGB565:
+       case V4L2_PIX_FMT_RGB565X:
+       case V4L2_PIX_FMT_RGB555:
+       case V4L2_PIX_FMT_SGRBG10:
+       case V4L2_PIX_FMT_RGB555X:
+       default:
+               pix->colorspace = V4L2_COLORSPACE_SRGB;
+               break;
+       }
+       *pix2 = *pix;
+       return 0;
+}
+
+/**
+ * ioctl_s_fmt_cap - V4L2 sensor interface handler for VIDIOC_S_FMT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure
+ *
+ * If the requested format is supported, configures the HW to use that
+ * format, returns error code if format not supported or HW can't be
+ * correctly configured.
+ */
+static int ioctl_s_fmt_cap(struct v4l2_int_device *s,
+                               struct v4l2_format *f)
+{
+       struct mt9p012_sensor *sensor = s->priv;
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+       int rval;
+
+       rval = ioctl_try_fmt_cap(s, f);
+       if (!rval)
+               sensor->pix = *pix;
+
+       return rval;
+}
+
+/**
+ * ioctl_g_fmt_cap - V4L2 sensor interface handler for ioctl_g_fmt_cap
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the sensor's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s,
+                               struct v4l2_format *f)
+{
+       struct mt9p012_sensor *sensor = s->priv;
+       f->fmt.pix = sensor->pix;
+
+       return 0;
+}
+
+/**
+ * ioctl_g_parm - V4L2 sensor interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the sensor's video CAPTURE parameters.
+ */
+static int ioctl_g_parm(struct v4l2_int_device *s,
+                            struct v4l2_streamparm *a)
+{
+       struct mt9p012_sensor *sensor = s->priv;
+       struct v4l2_captureparm *cparm = &a->parm.capture;
+
+       if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       memset(a, 0, sizeof(*a));
+       a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+       cparm->capability = V4L2_CAP_TIMEPERFRAME;
+       cparm->timeperframe = sensor->timeperframe;
+
+       return 0;
+}
+
+/**
+ * ioctl_s_parm - V4L2 sensor interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the sensor to use the input parameters, if possible.  If
+ * not possible, reverts to the old parameters and returns the
+ * appropriate error code.
+ */
+static int ioctl_s_parm(struct v4l2_int_device *s,
+                            struct v4l2_streamparm *a)
+{
+       struct mt9p012_sensor *sensor = s->priv;
+       struct i2c_client *client = sensor->i2c_client;
+       struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+
+       sensor->timeperframe = *timeperframe;
+       mt9p012sensor_calc_xclk(client);
+       *timeperframe = sensor->timeperframe;
+
+       return 0;
+}
+
+/**
+ * ioctl_g_priv - V4L2 sensor interface handler for vidioc_int_g_priv_num
+ * @s: pointer to standard V4L2 device structure
+ * @p: void pointer to hold sensor's private data address
+ *
+ * Returns device's (sensor's) private data area address in p parameter
+ */
+static int ioctl_g_priv(struct v4l2_int_device *s, void *p)
+{
+       struct mt9p012_sensor *sensor = s->priv;
+
+       return sensor->pdata->priv_data_set(p);
+}
+
+/**
+ * ioctl_s_power - V4L2 sensor interface handler for vidioc_int_s_power_num
+ * @s: pointer to standard V4L2 device structure
+ * @on: power state to which device is to be set
+ *
+ * Sets devices power state to requrested state, if possible.
+ */
+static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power on)
+{
+       struct mt9p012_sensor *sensor = s->priv;
+       struct i2c_client *c = sensor->i2c_client;
+       int rval;
+
+       if ((on == V4L2_POWER_STANDBY) && (sensor->state == SENSOR_DETECTED))
+               mt9p012_write_regs(c, stream_off_list);
+
+       if (on != V4L2_POWER_ON)
+               sensor->pdata->set_xclk(0);
+       else
+               sensor->pdata->set_xclk(xclk_current);
+
+       rval = sensor->pdata->power_set(on);
+       if (rval < 0) {
+               dev_err(&c->dev, "Unable to set the power state: " DRIVER_NAME
+                                                               " sensor\n");
+               sensor->pdata->set_xclk(0);
+               return rval;
+       }
+
+       if ((on == V4L2_POWER_ON) && (sensor->state == SENSOR_DETECTED))
+               mt9p012_configure(s);
+
+       if ((on == V4L2_POWER_ON) && (sensor->state == SENSOR_NOT_DETECTED)) {
+               rval = mt9p012_detect(c);
+               if (rval < 0) {
+                       dev_err(&c->dev, "Unable to detect " DRIVER_NAME
+                                                               " sensor\n");
+                       sensor->state = SENSOR_NOT_DETECTED;
+                       return rval;
+               }
+               sensor->state = SENSOR_DETECTED;
+               sensor->ver = rval;
+               pr_info(DRIVER_NAME " chip version 0x%02x detected\n",
+                                                               sensor->ver);
+       }
+
+       return 0;
+}
+
+/**
+ * ioctl_init - V4L2 sensor interface handler for VIDIOC_INT_INIT
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialize the sensor device (call mt9p012_configure())
+ */
+static int ioctl_init(struct v4l2_int_device *s)
+{
+       return 0;
+}
+
+/**
+ * ioctl_dev_exit - V4L2 sensor interface handler for vidioc_int_dev_exit_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Delinitialise the dev. at slave detach.  The complement of ioctl_dev_init.
+ */
+static int ioctl_dev_exit(struct v4l2_int_device *s)
+{
+       return 0;
+}
+
+/**
+ * ioctl_dev_init - V4L2 sensor interface handler for vidioc_int_dev_init_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialise the device when slave attaches to the master.  Returns 0 if
+ * mt9p012 device could be found, otherwise returns appropriate error.
+ */
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+       return 0;
+}
+/**
+ * ioctl_enum_framesizes - V4L2 sensor if handler for vidioc_int_enum_framesizes
+ * @s: pointer to standard V4L2 device structure
+ * @frms: pointer to standard V4L2 framesizes enumeration structure
+ *
+ * Returns possible framesizes depending on choosen pixel format
+ **/
+static int ioctl_enum_framesizes(struct v4l2_int_device *s,
+                                       struct v4l2_frmsizeenum *frms)
+{
+       int ifmt;
+
+       for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) {
+               if (frms->pixel_format == mt9p012_formats[ifmt].pixelformat)
+                       break;
+       }
+       /* Is requested pixelformat not found on sensor? */
+       if (ifmt == NUM_CAPTURE_FORMATS)
+               return -EINVAL;
+
+       /* Do we already reached all discrete framesizes? */
+       if (frms->index >= 5)
+               return -EINVAL;
+
+       frms->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+       frms->discrete.width = mt9p012_sizes[frms->index].width;
+       frms->discrete.height = mt9p012_sizes[frms->index].height;
+
+       return 0;
+}
+
+const struct v4l2_fract mt9p012_frameintervals[] = {
+       {  .numerator = 1, .denominator = 11 },
+       {  .numerator = 1, .denominator = 15 },
+       {  .numerator = 1, .denominator = 20 },
+       {  .numerator = 1, .denominator = 25 },
+       {  .numerator = 1, .denominator = 30 },
+};
+
+static int ioctl_enum_frameintervals(struct v4l2_int_device *s,
+                                       struct v4l2_frmivalenum *frmi)
+{
+       int ifmt;
+
+       for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) {
+               if (frmi->pixel_format == mt9p012_formats[ifmt].pixelformat)
+                       break;
+       }
+       /* Is requested pixelformat not found on sensor? */
+       if (ifmt == NUM_CAPTURE_FORMATS)
+               return -EINVAL;
+
+       /* Do we already reached all discrete framesizes? */
+
+       if (((frmi->width == mt9p012_sizes[4].width) &&
+                               (frmi->height == mt9p012_sizes[4].height)) ||
+                               ((frmi->width == mt9p012_sizes[3].width) &&
+                               (frmi->height == mt9p012_sizes[3].height))) {
+               /* FIXME: The only frameinterval supported by 5MP and 3MP
+                * capture sizes is 1/11 fps
+                */
+               if (frmi->index != 0)
+                       return -EINVAL;
+       } else {
+               if (frmi->index >= 5)
+                       return -EINVAL;
+       }
+
+       frmi->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+       frmi->discrete.numerator =
+                               mt9p012_frameintervals[frmi->index].numerator;
+       frmi->discrete.denominator =
+                               mt9p012_frameintervals[frmi->index].denominator;
+
+       return 0;
+}
+
+static struct v4l2_int_ioctl_desc mt9p012_ioctl_desc[] = {
+       { .num = vidioc_int_enum_framesizes_num,
+         .func = (v4l2_int_ioctl_func *)ioctl_enum_framesizes },
+       { .num = vidioc_int_enum_frameintervals_num,
+         .func = (v4l2_int_ioctl_func *)ioctl_enum_frameintervals },
+       { .num = vidioc_int_dev_init_num,
+         .func = (v4l2_int_ioctl_func *)ioctl_dev_init },
+       { .num = vidioc_int_dev_exit_num,
+         .func = (v4l2_int_ioctl_func *)ioctl_dev_exit },
+       { .num = vidioc_int_s_power_num,
+         .func = (v4l2_int_ioctl_func *)ioctl_s_power },
+       { .num = vidioc_int_g_priv_num,
+         .func = (v4l2_int_ioctl_func *)ioctl_g_priv },
+       { .num = vidioc_int_init_num,
+         .func = (v4l2_int_ioctl_func *)ioctl_init },
+       { .num = vidioc_int_enum_fmt_cap_num,
+         .func = (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap },
+       { .num = vidioc_int_try_fmt_cap_num,
+         .func = (v4l2_int_ioctl_func *)ioctl_try_fmt_cap },
+       { .num = vidioc_int_g_fmt_cap_num,
+         .func = (v4l2_int_ioctl_func *)ioctl_g_fmt_cap },
+       { .num = vidioc_int_s_fmt_cap_num,
+         .func = (v4l2_int_ioctl_func *)ioctl_s_fmt_cap },
+       { .num = vidioc_int_g_parm_num,
+         .func = (v4l2_int_ioctl_func *)ioctl_g_parm },
+       { .num = vidioc_int_s_parm_num,
+         .func = (v4l2_int_ioctl_func *)ioctl_s_parm },
+       { .num = vidioc_int_queryctrl_num,
+         .func = (v4l2_int_ioctl_func *)ioctl_queryctrl },
+       { .num = vidioc_int_g_ctrl_num,
+         .func = (v4l2_int_ioctl_func *)ioctl_g_ctrl },
+       { .num = vidioc_int_s_ctrl_num,
+         .func = (v4l2_int_ioctl_func *)ioctl_s_ctrl },
+};
+
+static struct v4l2_int_slave mt9p012_slave = {
+       .ioctls = mt9p012_ioctl_desc,
+       .num_ioctls = ARRAY_SIZE(mt9p012_ioctl_desc),
+};
+
+static struct v4l2_int_device mt9p012_int_device = {
+       .module = THIS_MODULE,
+       .name = DRIVER_NAME,
+       .priv = &mt9p012,
+       .type = v4l2_int_type_slave,
+       .u = {
+               .slave = &mt9p012_slave,
+       },
+};
+
+/**
+ * mt9p012_probe - sensor driver i2c probe handler
+ * @client: i2c driver client device structure
+ *
+ * Register sensor as an i2c client device and V4L2
+ * device.
+ */
+static int
+mt9p012_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+       struct mt9p012_sensor *sensor = &mt9p012;
+       int err;
+
+       if (i2c_get_clientdata(client))
+               return -EBUSY;
+
+       sensor->pdata = client->dev.platform_data;
+
+       if (!sensor->pdata) {
+               dev_err(&client->dev, "no platform data?\n");
+               return -ENODEV;
+       }
+
+       sensor->v4l2_int_device = &mt9p012_int_device;
+       sensor->i2c_client = client;
+
+       i2c_set_clientdata(client, sensor);
+
+       /* Make the default capture format QCIF V4L2_PIX_FMT_SGRBG10 */
+       sensor->pix.width = MT9P012_VIDEO_WIDTH_4X_BINN_SCALED;
+       sensor->pix.height = MT9P012_VIDEO_WIDTH_4X_BINN_SCALED;
+       sensor->pix.pixelformat = V4L2_PIX_FMT_SGRBG10;
+
+       err = v4l2_int_device_register(sensor->v4l2_int_device);
+       if (err)
+               i2c_set_clientdata(client, NULL);
+
+       return err;
+}
+
+/**
+ * mt9p012_remove - sensor driver i2c remove handler
+ * @client: i2c driver client device structure
+ *
+ * Unregister sensor as an i2c client device and V4L2
+ * device.  Complement of mt9p012_probe().
+ */
+static int __exit
+mt9p012_remove(struct i2c_client *client)
+{
+       struct mt9p012_sensor *sensor = i2c_get_clientdata(client);
+
+       if (!client->adapter)
+               return -ENODEV; /* our client isn't attached */
+
+       v4l2_int_device_unregister(sensor->v4l2_int_device);
+       i2c_set_clientdata(client, NULL);
+
+       return 0;
+}
+
+static const struct i2c_device_id mt9p012_id[] = {
+       { DRIVER_NAME, 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, mt9p012_id);
+
+static struct i2c_driver mt9p012sensor_i2c_driver = {
+       .driver = {
+               .name = DRIVER_NAME,
+               .owner = THIS_MODULE,
+       },
+       .probe = mt9p012_probe,
+       .remove = __exit_p(mt9p012_remove),
+       .id_table = mt9p012_id,
+};
+
+static struct mt9p012_sensor mt9p012 = {
+       .timeperframe = {
+               .numerator = 1,
+               .denominator = 15,
+       },
+       .state = SENSOR_NOT_DETECTED,
+};
+
+/**
+ * mt9p012sensor_init - sensor driver module_init handler
+ *
+ * Registers driver as an i2c client driver.  Returns 0 on success,
+ * error code otherwise.
+ */
+static int __init mt9p012sensor_init(void)
+{
+       return i2c_add_driver(&mt9p012sensor_i2c_driver);
+}
+module_init(mt9p012sensor_init);
+
+/**
+ * mt9p012sensor_cleanup - sensor driver module_exit handler
+ *
+ * Unregisters/deletes driver as an i2c client driver.
+ * Complement of mt9p012sensor_init.
+ */
+static void __exit mt9p012sensor_cleanup(void)
+{
+       i2c_del_driver(&mt9p012sensor_i2c_driver);
+}
+module_exit(mt9p012sensor_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("mt9p012 camera sensor driver");
diff --git a/drivers/media/video/mt9p012_regs.h b/drivers/media/video/mt9p012_regs.h
new file mode 100644
index 0000000..70f6ee7
--- /dev/null
+++ b/drivers/media/video/mt9p012_regs.h
@@ -0,0 +1,74 @@ 
+/*
+ * drivers/media/video/mt9p012_regs.h
+ *
+ * Register definitions for the MT9P012 camera sensor.
+ *
+ * Author:
+ *     Sameer Venkatraman <sameerv@ti.com>
+ *     Sergio Aguirre <saaguirre@ti.com>
+ *     Martinez Leonides
+ *
+ *
+ * Copyright (C) 2008 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#define REG_MODEL_ID                   0x0000
+#define REG_REVISION_NUMBER            0x0002
+#define REG_MANUFACTURER_ID            0x0003
+
+#define REG_MODE_SELECT                        0x0100
+#define REG_IMAGE_ORIENTATION          0x0101
+#define REG_SOFTWARE_RESET             0x0103
+#define REG_GROUPED_PAR_HOLD           0x0104
+
+#define REG_FINE_INT_TIME              0x0200
+#define REG_COARSE_INT_TIME            0x0202
+
+#define REG_ANALOG_GAIN_GLOBAL         0x0204
+#define REG_ANALOG_GAIN_GREENR         0x0206
+#define REG_ANALOG_GAIN_RED            0x0208
+#define REG_ANALOG_GAIN_BLUE           0x020A
+#define REG_ANALOG_GAIN_GREENB         0x020C
+#define REG_DIGITAL_GAIN_GREENR                0x020E
+#define REG_DIGITAL_GAIN_RED           0x0210
+#define REG_DIGITAL_GAIN_BLUE          0x0212
+#define REG_DIGITAL_GAIN_GREENB                0x0214
+
+#define REG_VT_PIX_CLK_DIV             0x0300
+#define REG_VT_SYS_CLK_DIV             0x0302
+#define REG_PRE_PLL_CLK_DIV            0x0304
+#define REG_PLL_MULTIPLIER             0x0306
+#define REG_OP_PIX_CLK_DIV             0x0308
+#define REG_OP_SYS_CLK_DIV             0x030A
+
+#define REG_FRAME_LEN_LINES            0x0340
+#define REG_LINE_LEN_PCK               0x0342
+
+#define REG_X_ADDR_START               0x0344
+#define REG_Y_ADDR_START               0x0346
+#define REG_X_ADDR_END                 0x0348
+#define REG_Y_ADDR_END                 0x034A
+#define REG_X_OUTPUT_SIZE              0x034C
+#define REG_Y_OUTPUT_SIZE              0x034E
+#define REG_X_ODD_INC                  0x0382
+#define REG_Y_ODD_INC                  0x0386
+
+#define REG_SCALING_MODE               0x0400
+#define REG_SCALE_M                    0x0404
+#define REG_SCALE_N                    0x0406
+
+#define REG_ROW_SPEED                  0x3016
+#define REG_RESET_REGISTER             0x301A
+#define REG_PIXEL_ORDER                        0x3024
+#define REG_READ_MODE                  0x3040
+
+#define REG_DATAPATH_STATUS            0x306A
+#define REG_DATAPATH_SELECT            0x306E
+
+#define REG_RESERVED_MFR_3064          0x3064
+#define REG_TEST_PATTERN               0x3070
+
diff --git a/include/media/mt9p012.h b/include/media/mt9p012.h
new file mode 100644
index 0000000..13a9745
--- /dev/null
+++ b/include/media/mt9p012.h
@@ -0,0 +1,37 @@ 
+/*
+ * drivers/media/video/mt9p012.h
+ *
+ * Register definitions for the MT9P012 camera sensor.
+ *
+ * Author:
+ *     Sameer Venkatraman <sameerv@ti.com>
+ *     Martinez Leonides
+ *
+ *
+ * Copyright (C) 2008 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef MT9P012_H
+#define MT9P012_H
+
+
+#define MT9P012_I2C_ADDR               0x10
+
+/**
+ * struct mt9p012_platform_data - platform data values and access functions
+ * @power_set: Power state access function, zero is off, non-zero is on.
+ * @default_regs: Default registers written after power-on or reset.
+ * @ifparm: Interface parameters access function
+ * @priv_data_set: device private data (pointer) access function
+ */
+struct mt9p012_platform_data {
+       int (*power_set)(enum v4l2_power power);
+       u32 (*set_xclk)(u32 xclkfreq);
+       int (*priv_data_set)(void *);
+};
+
+#endif /* ifndef MT9P012_H */