Re: [PATCH v2 2/2] media: Add a driver for the ov5645 camera sensor.

2016-05-27 Thread Todor Tomov
Hi Stan,

Thanks for the review.

On 05/20/2016 06:20 PM, Stanimir Varbanov wrote:
> Hi Todor,
> 
> Thanks for the patch, few comments below.
> 
> On 05/18/2016 02:50 PM, Todor Tomov wrote:
>> The ov5645 sensor from Omnivision supports up to 2592x1944
>> and CSI2 interface.
>>
>> The driver adds support for the following modes:
>> - 1280x960
>> - 1920x1080
>> - 2592x1944
>>
>> Output format is packed 8bit UYVY.
>>
>> Signed-off-by: Todor Tomov 
>> ---
>>  drivers/media/i2c/Kconfig  |   11 +
>>  drivers/media/i2c/Makefile |1 +
>>  drivers/media/i2c/ov5645.c | 1425 
>> 
>>  3 files changed, 1437 insertions(+)
>>  create mode 100644 drivers/media/i2c/ov5645.c
>>
>> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
>> index 521bbf1..aa17eba 100644
>> --- a/drivers/media/i2c/Kconfig
>> +++ b/drivers/media/i2c/Kconfig
>> @@ -490,6 +490,17 @@ config VIDEO_OV2659
>>To compile this driver as a module, choose M here: the
>>module will be called ov2659.
>>  
>> +config VIDEO_OV5645
>> +tristate "OmniVision OV5645 sensor support"
>> +depends on I2C && VIDEO_V4L2
>> +depends on MEDIA_CAMERA_SUPPORT
> 
> depends on OF ?
Yes, the driver expects to be able to parse data from DT, so I'll add "depends 
on OF".

> 
> 
> 
>> +
>> +struct ov5645 {
>> +struct i2c_client *i2c_client;
>> +struct device *dev;
>> +struct v4l2_subdev sd;
>> +struct media_pad pad;
>> +struct v4l2_of_endpoint ep;
>> +struct v4l2_mbus_framefmt fmt;
>> +struct v4l2_rect crop;
>> +struct clk *xclk;
>> +u32 xclk_freq;
> 
> this become unused?
Yes, I'll remove it.

> 
>> +
>> +struct regulator *io_regulator;
>> +struct regulator *core_regulator;
>> +struct regulator *analog_regulator;
>> +
>> +enum ov5645_mode current_mode;
>> +
>> +/* Cached control values */
>> +struct v4l2_ctrl_handler ctrls;
>> +struct v4l2_ctrl *saturation;
>> +struct v4l2_ctrl *hflip;
>> +struct v4l2_ctrl *vflip;
>> +struct v4l2_ctrl *autogain;
>> +struct v4l2_ctrl *autoexposure;
>> +struct v4l2_ctrl *awb;
>> +struct v4l2_ctrl *pattern;
>> +
>> +struct mutex power_lock; /* lock to protect power state */
>> +int power;
>> +
>> +struct gpio_desc *pwdn_gpio;
>> +struct gpio_desc *rst_gpio;
>> +};
>> +
>> +static inline struct ov5645 *to_ov5645(struct v4l2_subdev *sd)
>> +{
>> +return container_of(sd, struct ov5645, sd);
>> +}
> 
> 
> 
>> +static int ov5645_s_power(struct v4l2_subdev *sd, int on)
>> +{
>> +struct ov5645 *ov5645 = to_ov5645(sd);
>> +int ret = 0;
>> +
>> +dev_dbg(ov5645->dev, "%s: on = %d\n", __func__, on);
>> +
>> +mutex_lock(>power_lock);
>> +
>> +/* If the power count is modified from 0 to != 0 or from != 0 to 0,
>> + * update the power state.
>> + */
>> +if (ov5645->power == !on) {
>> +if (on) {
>> +ret = ov5645_set_power_on(ov5645);
>> +if (ret < 0) {
>> +dev_err(ov5645->dev, "could not set power %s\n",
>> +on ? "on" : "off");
>> +goto exit;
>> +}
>> +
>> +ret = ov5645_init(ov5645);
>> +if (ret < 0) {
>> +dev_err(ov5645->dev,
>> +"could not set init registers\n");
>> +goto exit;
>> +}
>> +
>> +ov5645_write_reg(ov5645, OV5645_SYSTEM_CTRL0,
>> + OV5645_SYSTEM_CTRL0_STOP);
> 
> please check the error code.
Ok.

> 
>> +} else {
>> +ov5645_set_power_off(ov5645);
>> +}
>> +
>> +/* Update the power count. */
>> +ov5645->power += on ? 1 : -1;
>> +WARN_ON(ov5645->power < 0);
>> +}
>> +
>> +exit:
>> +mutex_unlock(>power_lock);
>> +
>> +return ret;
>> +}
>> +
> 
> 
> 
>> +
>> +static int ov5645_registered(struct v4l2_subdev *subdev)
>> +{
>> +struct i2c_client *client = v4l2_get_subdevdata(subdev);
>> +struct ov5645 *ov5645 = to_ov5645(subdev);
>> +u8 chip_id_high, chip_id_low;
>> +int ret;
>> +
>> +ov5645_s_power(>sd, true);
> 
> check for error here and on the other places where call s_power.
Ok.

> 
>> +
>> +ret = ov5645_read_reg(ov5645, OV5645_CHIP_ID_HIGH_REG, _id_high);
>> +if (ret < 0 || chip_id_high != OV5645_CHIP_ID_HIGH) {
>> +dev_err(ov5645->dev, "could not read ID high\n");
>> +ret = -ENODEV;
>> +goto reg_power_off;
>> +}
>> +ret = ov5645_read_reg(ov5645, OV5645_CHIP_ID_LOW_REG, _id_low);
>> +if (ret < 0 || chip_id_low != OV5645_CHIP_ID_LOW) {
>> +dev_err(ov5645->dev, "could not read ID low\n");
>> +ret = -ENODEV;
>> +goto reg_power_off;
>> +}

Re: [PATCH v2 2/2] media: Add a driver for the ov5645 camera sensor.

2016-05-26 Thread Todor Tomov
Hi Hans,

Thanks for the review.

On 05/23/2016 01:36 PM, Hans Verkuil wrote:
> Hi Todor,
> 
> Thanks for the patch series! I got a few comments:
> 
> On 05/18/2016 01:50 PM, Todor Tomov wrote:
>> The ov5645 sensor from Omnivision supports up to 2592x1944
>> and CSI2 interface.
>>
>> The driver adds support for the following modes:
>> - 1280x960
>> - 1920x1080
>> - 2592x1944
>>
>> Output format is packed 8bit UYVY.
>>
>> Signed-off-by: Todor Tomov 
>> ---
>>  drivers/media/i2c/Kconfig  |   11 +
>>  drivers/media/i2c/Makefile |1 +
>>  drivers/media/i2c/ov5645.c | 1425 
>> 
>>  3 files changed, 1437 insertions(+)
>>  create mode 100644 drivers/media/i2c/ov5645.c
> 
> 
> 
>>
>> +static int ov5645_change_mode(struct ov5645 *ov5645, enum ov5645_mode mode)
>> +{
>> +struct reg_value *settings;
>> +u32 num_settings;
>> +int ret = 0;
>> +
>> +settings = ov5645_mode_info_data[mode].data;
>> +num_settings = ov5645_mode_info_data[mode].data_size;
>> +ret = ov5645_set_register_array(ov5645, settings, num_settings);
> 
> Just do 'return ov5645_set_register_array(ov5645, settings, num_settings);'
> No need for the 'ret' variable.
Ok.

> 
>> +
>> +return ret;
>> +}
>> +
>> +static int ov5645_set_power_on(struct ov5645 *ov5645)
>> +{
>> +int ret = 0;
>> +
>> +dev_dbg(ov5645->dev, "%s: Enter\n", __func__);
>> +
>> +ret = clk_prepare_enable(ov5645->xclk);
>> +if (ret < 0) {
>> +dev_err(ov5645->dev, "clk prepare enable failed\n");
>> +return ret;
>> +}
>> +
>> +ret = ov5645_regulators_enable(ov5645);
>> +if (ret < 0) {
>> +clk_disable_unprepare(ov5645->xclk);
>> +return ret;
>> +}
>> +
>> +usleep_range(5000, 15000);
>> +if (ov5645->pwdn_gpio)
>> +gpiod_set_value_cansleep(ov5645->pwdn_gpio, 1);
>> +
>> +usleep_range(1000, 2000);
>> +if (ov5645->rst_gpio)
>> +gpiod_set_value_cansleep(ov5645->rst_gpio, 1);
>> +msleep(20);
>> +
>> +return ret;
>> +}
>> +
>> +static void ov5645_set_power_off(struct ov5645 *ov5645)
>> +{
>> +dev_dbg(ov5645->dev, "%s: Enter\n", __func__);
>> +
>> +if (ov5645->rst_gpio)
>> +gpiod_set_value_cansleep(ov5645->rst_gpio, 0);
>> +if (ov5645->pwdn_gpio)
>> +gpiod_set_value_cansleep(ov5645->pwdn_gpio, 0);
>> +ov5645_regulators_disable(ov5645);
>> +clk_disable_unprepare(ov5645->xclk);
>> +}
>> +
>> +static int ov5645_s_power(struct v4l2_subdev *sd, int on)
>> +{
>> +struct ov5645 *ov5645 = to_ov5645(sd);
>> +int ret = 0;
>> +
>> +dev_dbg(ov5645->dev, "%s: on = %d\n", __func__, on);
>> +
>> +mutex_lock(>power_lock);
>> +
>> +/* If the power count is modified from 0 to != 0 or from != 0 to 0,
>> + * update the power state.
>> + */
>> +if (ov5645->power == !on) {
>> +if (on) {
>> +ret = ov5645_set_power_on(ov5645);
>> +if (ret < 0) {
>> +dev_err(ov5645->dev, "could not set power %s\n",
>> +on ? "on" : "off");
>> +goto exit;
>> +}
>> +
>> +ret = ov5645_init(ov5645);
>> +if (ret < 0) {
>> +dev_err(ov5645->dev,
>> +"could not set init registers\n");
>> +goto exit;
>> +}
>> +
>> +ov5645_write_reg(ov5645, OV5645_SYSTEM_CTRL0,
>> + OV5645_SYSTEM_CTRL0_STOP);
>> +} else {
>> +ov5645_set_power_off(ov5645);
>> +}
>> +
>> +/* Update the power count. */
>> +ov5645->power += on ? 1 : -1;
> 
> Huh? Is ov5645->power a bool or a counter? If it is a counter then this line
> should be outside this 'if'. If it is a bool, then the comments (and the
> power field itself!) should be updated.
> 
> As far as I can tell, the 'power' field should be a bool and everything should
> be updated accordingly.
Yes, I'll change it to bool.

> 
> 
>> +WARN_ON(ov5645->power < 0);
>> +}
>> +
>> +exit:
>> +mutex_unlock(>power_lock);
>> +
>> +return ret;
>> +}
>> +
>> +
>> +static int ov5645_set_saturation(struct ov5645 *ov5645, s32 value)
>> +{
>> +u32 reg_value = (value * 0x10) + 0x40;
>> +int ret = 0;
>> +
>> +ret |= ov5645_write_reg(ov5645, OV5645_SDE_SAT_U, reg_value);
>> +ret |= ov5645_write_reg(ov5645, OV5645_SDE_SAT_V, reg_value);
>> +
>> +dev_dbg(ov5645->dev, "%s: value = %d\n", __func__, value);
>> +
>> +return ret;
>> +}
>> +
>> +static int ov5645_set_hflip(struct ov5645 *ov5645, s32 value)
>> +{
>> +u8 val;
>> +
>> +ov5645_read_reg(ov5645, OV5645_TIMING_TC_REG21, );
>> +if (value == 0)
>> +val &= ~(OV5645_SENSOR_MIRROR);
>> +else

Re: [PATCH v2 2/2] media: Add a driver for the ov5645 camera sensor.

2016-05-23 Thread Hans Verkuil
Hi Todor,

Thanks for the patch series! I got a few comments:

On 05/18/2016 01:50 PM, Todor Tomov wrote:
> The ov5645 sensor from Omnivision supports up to 2592x1944
> and CSI2 interface.
> 
> The driver adds support for the following modes:
> - 1280x960
> - 1920x1080
> - 2592x1944
> 
> Output format is packed 8bit UYVY.
> 
> Signed-off-by: Todor Tomov 
> ---
>  drivers/media/i2c/Kconfig  |   11 +
>  drivers/media/i2c/Makefile |1 +
>  drivers/media/i2c/ov5645.c | 1425 
> 
>  3 files changed, 1437 insertions(+)
>  create mode 100644 drivers/media/i2c/ov5645.c



> 
> +static int ov5645_change_mode(struct ov5645 *ov5645, enum ov5645_mode mode)
> +{
> + struct reg_value *settings;
> + u32 num_settings;
> + int ret = 0;
> +
> + settings = ov5645_mode_info_data[mode].data;
> + num_settings = ov5645_mode_info_data[mode].data_size;
> + ret = ov5645_set_register_array(ov5645, settings, num_settings);

Just do 'return ov5645_set_register_array(ov5645, settings, num_settings);'
No need for the 'ret' variable.

> +
> + return ret;
> +}
> +
> +static int ov5645_set_power_on(struct ov5645 *ov5645)
> +{
> + int ret = 0;
> +
> + dev_dbg(ov5645->dev, "%s: Enter\n", __func__);
> +
> + ret = clk_prepare_enable(ov5645->xclk);
> + if (ret < 0) {
> + dev_err(ov5645->dev, "clk prepare enable failed\n");
> + return ret;
> + }
> +
> + ret = ov5645_regulators_enable(ov5645);
> + if (ret < 0) {
> + clk_disable_unprepare(ov5645->xclk);
> + return ret;
> + }
> +
> + usleep_range(5000, 15000);
> + if (ov5645->pwdn_gpio)
> + gpiod_set_value_cansleep(ov5645->pwdn_gpio, 1);
> +
> + usleep_range(1000, 2000);
> + if (ov5645->rst_gpio)
> + gpiod_set_value_cansleep(ov5645->rst_gpio, 1);
> + msleep(20);
> +
> + return ret;
> +}
> +
> +static void ov5645_set_power_off(struct ov5645 *ov5645)
> +{
> + dev_dbg(ov5645->dev, "%s: Enter\n", __func__);
> +
> + if (ov5645->rst_gpio)
> + gpiod_set_value_cansleep(ov5645->rst_gpio, 0);
> + if (ov5645->pwdn_gpio)
> + gpiod_set_value_cansleep(ov5645->pwdn_gpio, 0);
> + ov5645_regulators_disable(ov5645);
> + clk_disable_unprepare(ov5645->xclk);
> +}
> +
> +static int ov5645_s_power(struct v4l2_subdev *sd, int on)
> +{
> + struct ov5645 *ov5645 = to_ov5645(sd);
> + int ret = 0;
> +
> + dev_dbg(ov5645->dev, "%s: on = %d\n", __func__, on);
> +
> + mutex_lock(>power_lock);
> +
> + /* If the power count is modified from 0 to != 0 or from != 0 to 0,
> +  * update the power state.
> +  */
> + if (ov5645->power == !on) {
> + if (on) {
> + ret = ov5645_set_power_on(ov5645);
> + if (ret < 0) {
> + dev_err(ov5645->dev, "could not set power %s\n",
> + on ? "on" : "off");
> + goto exit;
> + }
> +
> + ret = ov5645_init(ov5645);
> + if (ret < 0) {
> + dev_err(ov5645->dev,
> + "could not set init registers\n");
> + goto exit;
> + }
> +
> + ov5645_write_reg(ov5645, OV5645_SYSTEM_CTRL0,
> +  OV5645_SYSTEM_CTRL0_STOP);
> + } else {
> + ov5645_set_power_off(ov5645);
> + }
> +
> + /* Update the power count. */
> + ov5645->power += on ? 1 : -1;

Huh? Is ov5645->power a bool or a counter? If it is a counter then this line
should be outside this 'if'. If it is a bool, then the comments (and the
power field itself!) should be updated.

As far as I can tell, the 'power' field should be a bool and everything should
be updated accordingly.


> + WARN_ON(ov5645->power < 0);
> + }
> +
> +exit:
> + mutex_unlock(>power_lock);
> +
> + return ret;
> +}
> +
> +
> +static int ov5645_set_saturation(struct ov5645 *ov5645, s32 value)
> +{
> + u32 reg_value = (value * 0x10) + 0x40;
> + int ret = 0;
> +
> + ret |= ov5645_write_reg(ov5645, OV5645_SDE_SAT_U, reg_value);
> + ret |= ov5645_write_reg(ov5645, OV5645_SDE_SAT_V, reg_value);
> +
> + dev_dbg(ov5645->dev, "%s: value = %d\n", __func__, value);
> +
> + return ret;
> +}
> +
> +static int ov5645_set_hflip(struct ov5645 *ov5645, s32 value)
> +{
> + u8 val;
> +
> + ov5645_read_reg(ov5645, OV5645_TIMING_TC_REG21, );
> + if (value == 0)
> + val &= ~(OV5645_SENSOR_MIRROR);
> + else
> + val |= (OV5645_SENSOR_MIRROR);
> +
> + dev_dbg(ov5645->dev, "%s: value = %d\n", __func__, value);
> +
> + return ov5645_write_reg(ov5645, OV5645_TIMING_TC_REG21, val);
> +}
> +
> +static int 

Re: [PATCH v2 2/2] media: Add a driver for the ov5645 camera sensor.

2016-05-20 Thread Stanimir Varbanov
Hi Todor,

Thanks for the patch, few comments below.

On 05/18/2016 02:50 PM, Todor Tomov wrote:
> The ov5645 sensor from Omnivision supports up to 2592x1944
> and CSI2 interface.
> 
> The driver adds support for the following modes:
> - 1280x960
> - 1920x1080
> - 2592x1944
> 
> Output format is packed 8bit UYVY.
> 
> Signed-off-by: Todor Tomov 
> ---
>  drivers/media/i2c/Kconfig  |   11 +
>  drivers/media/i2c/Makefile |1 +
>  drivers/media/i2c/ov5645.c | 1425 
> 
>  3 files changed, 1437 insertions(+)
>  create mode 100644 drivers/media/i2c/ov5645.c
> 
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 521bbf1..aa17eba 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -490,6 +490,17 @@ config VIDEO_OV2659
> To compile this driver as a module, choose M here: the
> module will be called ov2659.
>  
> +config VIDEO_OV5645
> + tristate "OmniVision OV5645 sensor support"
> + depends on I2C && VIDEO_V4L2
> + depends on MEDIA_CAMERA_SUPPORT

depends on OF ?



> +
> +struct ov5645 {
> + struct i2c_client *i2c_client;
> + struct device *dev;
> + struct v4l2_subdev sd;
> + struct media_pad pad;
> + struct v4l2_of_endpoint ep;
> + struct v4l2_mbus_framefmt fmt;
> + struct v4l2_rect crop;
> + struct clk *xclk;
> + u32 xclk_freq;

this become unused?

> +
> + struct regulator *io_regulator;
> + struct regulator *core_regulator;
> + struct regulator *analog_regulator;
> +
> + enum ov5645_mode current_mode;
> +
> + /* Cached control values */
> + struct v4l2_ctrl_handler ctrls;
> + struct v4l2_ctrl *saturation;
> + struct v4l2_ctrl *hflip;
> + struct v4l2_ctrl *vflip;
> + struct v4l2_ctrl *autogain;
> + struct v4l2_ctrl *autoexposure;
> + struct v4l2_ctrl *awb;
> + struct v4l2_ctrl *pattern;
> +
> + struct mutex power_lock; /* lock to protect power state */
> + int power;
> +
> + struct gpio_desc *pwdn_gpio;
> + struct gpio_desc *rst_gpio;
> +};
> +
> +static inline struct ov5645 *to_ov5645(struct v4l2_subdev *sd)
> +{
> + return container_of(sd, struct ov5645, sd);
> +}



> +static int ov5645_s_power(struct v4l2_subdev *sd, int on)
> +{
> + struct ov5645 *ov5645 = to_ov5645(sd);
> + int ret = 0;
> +
> + dev_dbg(ov5645->dev, "%s: on = %d\n", __func__, on);
> +
> + mutex_lock(>power_lock);
> +
> + /* If the power count is modified from 0 to != 0 or from != 0 to 0,
> +  * update the power state.
> +  */
> + if (ov5645->power == !on) {
> + if (on) {
> + ret = ov5645_set_power_on(ov5645);
> + if (ret < 0) {
> + dev_err(ov5645->dev, "could not set power %s\n",
> + on ? "on" : "off");
> + goto exit;
> + }
> +
> + ret = ov5645_init(ov5645);
> + if (ret < 0) {
> + dev_err(ov5645->dev,
> + "could not set init registers\n");
> + goto exit;
> + }
> +
> + ov5645_write_reg(ov5645, OV5645_SYSTEM_CTRL0,
> +  OV5645_SYSTEM_CTRL0_STOP);

please check the error code.

> + } else {
> + ov5645_set_power_off(ov5645);
> + }
> +
> + /* Update the power count. */
> + ov5645->power += on ? 1 : -1;
> + WARN_ON(ov5645->power < 0);
> + }
> +
> +exit:
> + mutex_unlock(>power_lock);
> +
> + return ret;
> +}
> +



> +
> +static int ov5645_registered(struct v4l2_subdev *subdev)
> +{
> + struct i2c_client *client = v4l2_get_subdevdata(subdev);
> + struct ov5645 *ov5645 = to_ov5645(subdev);
> + u8 chip_id_high, chip_id_low;
> + int ret;
> +
> + ov5645_s_power(>sd, true);

check for error here and on the other places where call s_power.

> +
> + ret = ov5645_read_reg(ov5645, OV5645_CHIP_ID_HIGH_REG, _id_high);
> + if (ret < 0 || chip_id_high != OV5645_CHIP_ID_HIGH) {
> + dev_err(ov5645->dev, "could not read ID high\n");
> + ret = -ENODEV;
> + goto reg_power_off;
> + }
> + ret = ov5645_read_reg(ov5645, OV5645_CHIP_ID_LOW_REG, _id_low);
> + if (ret < 0 || chip_id_low != OV5645_CHIP_ID_LOW) {
> + dev_err(ov5645->dev, "could not read ID low\n");
> + ret = -ENODEV;
> + goto reg_power_off;
> + }
> +
> + dev_info(>dev, "OV5645 detected at address 0x%02x\n",
> +  client->addr);
> +
> + ov5645_s_power(>sd, false);
> +
> + return 0;
> +
> +reg_power_off:
> + ov5645_s_power(>sd, false);
> + return ret;
> +}
> +
> +static int ov5645_s_stream(struct v4l2_subdev *subdev, 

[PATCH v2 2/2] media: Add a driver for the ov5645 camera sensor.

2016-05-18 Thread Todor Tomov
The ov5645 sensor from Omnivision supports up to 2592x1944
and CSI2 interface.

The driver adds support for the following modes:
- 1280x960
- 1920x1080
- 2592x1944

Output format is packed 8bit UYVY.

Signed-off-by: Todor Tomov 
---
 drivers/media/i2c/Kconfig  |   11 +
 drivers/media/i2c/Makefile |1 +
 drivers/media/i2c/ov5645.c | 1425 
 3 files changed, 1437 insertions(+)
 create mode 100644 drivers/media/i2c/ov5645.c

diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 521bbf1..aa17eba 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -490,6 +490,17 @@ config VIDEO_OV2659
  To compile this driver as a module, choose M here: the
  module will be called ov2659.
 
+config VIDEO_OV5645
+   tristate "OmniVision OV5645 sensor support"
+   depends on I2C && VIDEO_V4L2
+   depends on MEDIA_CAMERA_SUPPORT
+   ---help---
+ This is a Video4Linux2 sensor-level driver for the OmniVision
+ OV5645 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ov5645.
+
 config VIDEO_OV7640
tristate "OmniVision OV7640 sensor support"
depends on I2C && VIDEO_V4L2
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 07db257..29003df 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o
 obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o
 obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
 obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
+obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
 obj-$(CONFIG_VIDEO_OV7640) += ov7640.o
 obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
 obj-$(CONFIG_VIDEO_OV9650) += ov9650.o
diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c
new file mode 100644
index 000..7fafbcf
--- /dev/null
+++ b/drivers/media/i2c/ov5645.c
@@ -0,0 +1,1425 @@
+/*
+ * Driver for the OV5645 camera sensor.
+ *
+ * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2015 By Tech Design S.L. All Rights Reserved.
+ * Copyright (C) 2012-2013 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * Based on:
+ * - the OV5645 driver from QC msm-3.10 kernel on codeaurora.org:
+ *   https://us.codeaurora.org/cgit/quic/la/kernel/msm-3.10/tree/drivers/
+ *   media/platform/msm/camera_v2/sensor/ov5645.c?h=LA.BR.1.2.4_rb1.41
+ * - the OV5640 driver posted on linux-media:
+ *   https://www.mail-archive.com/linux-media%40vger.kernel.org/msg92671.html
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define OV5645_VOLTAGE_ANALOG   280
+#define OV5645_VOLTAGE_DIGITAL_CORE 150
+#define OV5645_VOLTAGE_DIGITAL_IO   180
+
+#define OV5645_XCLK_MIN 600
+#define OV5645_XCLK_MAX 2400
+
+#define OV5645_SYSTEM_CTRL00x3008
+#defineOV5645_SYSTEM_CTRL0_START   0x02
+#defineOV5645_SYSTEM_CTRL0_STOP0x42
+#define OV5645_CHIP_ID_HIGH_REG0x300A
+#defineOV5645_CHIP_ID_HIGH 0x56
+#define OV5645_CHIP_ID_LOW_REG 0x300B
+#defineOV5645_CHIP_ID_LOW  0x45
+#define OV5645_AWB_MANUAL_CONTROL  0x3406
+#defineOV5645_AWB_MANUAL_ENABLEBIT(0)
+#define OV5645_AEC_PK_MANUAL   0x3503
+#defineOV5645_AEC_MANUAL_ENABLEBIT(0)
+#defineOV5645_AGC_MANUAL_ENABLEBIT(1)
+#define OV5645_TIMING_TC_REG20 0x3820
+#defineOV5645_SENSOR_VFLIP BIT(1)
+#defineOV5645_ISP_VFLIPBIT(2)
+#define OV5645_TIMING_TC_REG21 0x3821
+#defineOV5645_SENSOR_MIRRORBIT(1)
+#define OV5645_PRE_ISP_TEST_SETTING_1  0x503d
+#defineOV5645_TEST_PATTERN_MASK0x3
+#defineOV5645_SET_TEST_PATTERN(x)  ((x) & 
OV5645_TEST_PATTERN_MASK)
+#defineOV5645_TEST_PATTERN_ENABLE  BIT(7)
+#define OV5645_SDE_SAT_U   0x5583
+#define OV5645_SDE_SAT_V   0x5584
+
+enum ov5645_mode {
+   OV5645_MODE_MIN = 0,
+   OV5645_MODE_SXGA = 0,
+   OV5645_MODE_1080P = 1,
+