Re: [PATCH v2 1/2] MT9P031: Add support for Aptina mt9p031 sensor.

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

 



On 21 May 2011 17:29, Guennadi Liakhovetski <g.liakhovetski@xxxxxx> wrote:
> On Fri, 20 May 2011, Javier Martin wrote:
>
>> This driver adds basic support for Aptina mt9p031 sensor.
>>
>> Signed-off-by: Javier Martin <javier.martin@xxxxxxxxxxxxxxxxx>
>> ---
>>  drivers/media/video/Kconfig   |    8 +
>>  drivers/media/video/Makefile  |    1 +
>>  drivers/media/video/mt9p031.c |  751 +++++++++++++++++++++++++++++++++++++++++
>>  include/media/mt9p031.h       |   11 +
>>  4 files changed, 771 insertions(+), 0 deletions(-)
>>  create mode 100644 drivers/media/video/mt9p031.c
>>  create mode 100644 include/media/mt9p031.h
>>
>> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
>> index 00f51dd..5c96b89 100644
>> --- a/drivers/media/video/Kconfig
>> +++ b/drivers/media/video/Kconfig
>> @@ -329,6 +329,14 @@ config VIDEO_OV7670
>>         OV7670 VGA camera.  It currently only works with the M88ALP01
>>         controller.
>>
>> +config VIDEO_MT9P031
>> +     tristate "Aptina MT9P031 support"
>> +     depends on I2C && VIDEO_V4L2
>> +     ---help---
>> +       This driver supports MT9P031 cameras from Micron
>> +       This is a Video4Linux2 sensor-level driver for the Micron
>> +       mt0p031 5 Mpixel camera.
>
> Two sentences seem to repeat the same with other words, and it's better to
> stay consistent: just use Aptina everywhere, maybe put Micron in brackets
> at one location.
>

OK, I will fix it.

>> +
>>  config VIDEO_MT9V011
>>       tristate "Micron mt9v011 sensor support"
>>       depends on I2C && VIDEO_V4L2
>> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
>> index ace5d8b..912b29b 100644
>> --- a/drivers/media/video/Makefile
>> +++ b/drivers/media/video/Makefile
>> @@ -65,6 +65,7 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
>>  obj-$(CONFIG_VIDEO_OV7670)   += ov7670.o
>>  obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
>>  obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
>> +obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
>>  obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
>>  obj-$(CONFIG_VIDEO_SR030PC30)        += sr030pc30.o
>>  obj-$(CONFIG_VIDEO_NOON010PC30)      += noon010pc30.o
>> diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c
>> new file mode 100644
>> index 0000000..e406b64
>> --- /dev/null
>> +++ b/drivers/media/video/mt9p031.c
>> @@ -0,0 +1,751 @@
>> +/*
>> + * Driver for MT9P031 CMOS Image Sensor from Aptina
>> + *
>> + * Copyright (C) 2011, Javier Martin <javier.martin@xxxxxxxxxxxxxxxxx>
>> + *
>> + * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@xxxxxx>
>> + *
>> + * Based on the MT9V032 driver and Bastian Hecht's code.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +#include <linux/i2c.h>
>> +#include <linux/log2.h>
>> +#include <linux/pm.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/slab.h>
>> +#include <media/v4l2-subdev.h>
>> +#include <linux/videodev2.h>
>> +
>> +#include <media/mt9p031.h>
>> +#include <media/v4l2-chip-ident.h>
>> +#include <media/v4l2-subdev.h>
>> +#include <media/v4l2-device.h>
>> +
>> +/* mt9p031 selected register addresses */
>> +#define MT9P031_CHIP_VERSION                 0x00
>> +#define              MT9P031_CHIP_VERSION_VALUE      0x1801
>> +#define MT9P031_ROW_START                    0x01
>
> Don't mix spaces and TABs between "#define" and the macro - just use one
> space everywhere.
>

I've done this in order to follow Laurent's directions. He does the
same in mt9v032 driver.
So, unless Laurent and you agree I think I won't change it.

>
>> +#define              MT9P031_ROW_START_SKIP          54
>> +#define MT9P031_COLUMN_START                 0x02
>> +#define              MT9P031_COLUMN_START_SKIP       16
>> +#define MT9P031_WINDOW_HEIGHT                        0x03
>> +#define MT9P031_WINDOW_WIDTH                 0x04
>> +#define MT9P031_H_BLANKING                   0x05
>> +#define              MT9P031_H_BLANKING_VALUE        0
>> +#define MT9P031_V_BLANKING                   0x06
>> +#define              MT9P031_V_BLANKING_VALUE        25
>> +#define MT9P031_OUTPUT_CONTROL                       0x07
>> +#define              MT9P031_OUTPUT_CONTROL_CEN      2
>> +#define              MT9P031_OUTPUT_CONTROL_SYN      1
>> +#define MT9P031_SHUTTER_WIDTH_UPPER          0x08
>> +#define MT9P031_SHUTTER_WIDTH                        0x09
>> +#define MT9P031_PIXEL_CLOCK_CONTROL          0x0a
>> +#define MT9P031_FRAME_RESTART                        0x0b
>> +#define MT9P031_SHUTTER_DELAY                        0x0c
>> +#define MT9P031_RST                          0x0d
>> +#define              MT9P031_RST_ENABLE              1
>> +#define              MT9P031_RST_DISABLE             0
>> +#define MT9P031_READ_MODE_1                  0x1e
>> +#define MT9P031_READ_MODE_2                  0x20
>> +#define              MT9P031_READ_MODE_2_ROW_MIR     0x8000
>> +#define              MT9P031_READ_MODE_2_COL_MIR     0x4000
>> +#define MT9P031_ROW_ADDRESS_MODE             0x22
>> +#define MT9P031_COLUMN_ADDRESS_MODE          0x23
>> +#define MT9P031_GLOBAL_GAIN                  0x35
>> +
>> +#define MT9P031_MAX_HEIGHT                   1944
>> +#define MT9P031_MAX_WIDTH                    2592
>> +#define MT9P031_MIN_HEIGHT                   2
>> +#define MT9P031_MIN_WIDTH                    18
>> +
>> +struct mt9p031 {
>> +     struct v4l2_subdev subdev;
>> +     struct media_pad pad;
>> +     struct v4l2_rect rect;  /* Sensor window */
>> +     struct v4l2_mbus_framefmt format;
>> +     struct mt9p031_platform_data *pdata;
>> +     struct mutex power_lock;
>
> Don't locks _always_ have to be documented? And this one: you only protect
> set_power() with it, Laurent, is this correct?
>

Just following the model Laurent applies in mt9v032. Let's see what he
has to say about this.

>
>> +     int power_count;
>> +     u16 xskip;
>> +     u16 yskip;
>> +     u16 output_control;
>> +     struct regulator *reg_1v8;
>> +     struct regulator *reg_2v8;
>> +};
>> +
>> +static struct mt9p031 *to_mt9p031(const struct i2c_client *client)
>> +{
>> +     return container_of(i2c_get_clientdata(client), struct mt9p031, subdev);
>> +}
>> +
>> +static int reg_read(struct i2c_client *client, const u8 reg)
>> +{
>> +     s32 data = i2c_smbus_read_word_data(client, reg);
>> +     return data < 0 ? data : swab16(data);
>> +}
>> +
>> +static int reg_write(struct i2c_client *client, const u8 reg,
>> +                     const u16 data)
>> +{
>> +     return i2c_smbus_write_word_data(client, reg, swab16(data));
>> +}
>> +
>> +static int mt9p031_set_output_control(struct mt9p031 *mt9p031, u16 clear,
>> +                                   u16 set)
>> +{
>> +     struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
>> +     u16 value = (mt9p031->output_control & ~clear) | set;
>> +     int ret;
>> +
>> +     ret = reg_write(client, MT9P031_OUTPUT_CONTROL, value);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     mt9p031->output_control = value;
>> +     return 0;
>> +}
>> +
>> +static int mt9p031_reset(struct i2c_client *client)
>> +{
>> +     struct mt9p031 *mt9p031 = to_mt9p031(client);
>> +     int ret;
>> +
>> +     /* Disable chip output, synchronous option update */
>> +     ret = reg_write(client, MT9P031_RST, MT9P031_RST_ENABLE);
>> +     if (ret < 0)
>> +             return -EIO;
>> +     ret = reg_write(client, MT9P031_RST, MT9P031_RST_DISABLE);
>> +     if (ret < 0)
>> +             return -EIO;
>> +     ret = mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN, 0);
>> +     if (ret < 0)
>> +             return -EIO;
>> +     return 0;
>
>
> I think, a sequence like
>
>        ret = fn();
>        if (!ret)
>                ret = fn();
>        if (!ret)
>                ret = fn();
>        return ret;
>
> is a better way to achieve the same.
>

Sorry, but I have to disagree. I understand what you want to achieve
but this seems quite tricky to me.
I explicitly changed parts of the code that were written using that
style because I think It was better understandable.


>> +}
>> +
>> +static int mt9p031_power_on(struct mt9p031 *mt9p031)
>> +{
>> +     int ret;
>> +
>> +     /* turn on VDD_IO */
>> +     ret = regulator_enable(mt9p031->reg_2v8);
>> +     if (ret) {
>> +             pr_err("Failed to enable 2.8v regulator: %d\n", ret);
>
> dev_err()

ok

>> +             return ret;
>> +     }

[snip]

>> +
>> +     pr_info("%s(%ux%u@%u:%u : %u)\n", __func__,
>> +                     crop->rect.width, crop->rect.height,
>> +                     crop->rect.left, crop->rect.top, crop->which);
>
> dev_dbg()

ok

>> +
>> +     /*
>> +      * Clamp the crop rectangle boundaries and align them to a multiple of 2
>> +      * pixels.
>> +      */
>> +     rect.width = ALIGN(clamp(crop->rect.width,
>> +                              MT9P031_MIN_WIDTH, MT9P031_MAX_WIDTH), 2);
>> +     rect.height = ALIGN(clamp(crop->rect.height,
>> +                               MT9P031_MIN_HEIGHT, MT9P031_MAX_HEIGHT), 2);
>> +     rect.left = ALIGN(clamp(crop->rect.left,
>> +                             0, MT9P031_MAX_WIDTH - rect.width), 2);
>> +     rect.top = ALIGN(clamp(crop->rect.top,
>> +                            0, MT9P031_MAX_HEIGHT - rect.height), 2);
>> +
>> +     c = mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which);
>> +
>> +     if (rect.width != c->width || rect.height != c->height) {
>> +             /*
>> +              * Reset the output image size if the crop rectangle size has
>> +              * been modified.
>> +              */
>> +             f = mt9p031_get_pad_format(mt9p031, fh, crop->pad,
>> +                                                 crop->which);
>> +             width = f->width;
>> +             height = f->height;
>> +
>> +             xskip = mt9p031_skip_for_crop(rect.width, &width, 7);
>> +             yskip = mt9p031_skip_for_crop(rect.height, &height, 8);
>> +     } else {
>> +             xskip = mt9p031->xskip;
>> +             yskip = mt9p031->yskip;
>> +             f = NULL;
>> +     }
>
> Hm, looks like something is missing here: you dropped
>
>        if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
>                ret = mt9p031_set_params(client, &rect, xskip, yskip);
>                if (ret < 0)
>                        return ret;
>        }
>
> from my version, without which no cropping is actually taking place. Or
> have you also switched to the convention of only configuring the hardware
> on set_stream(1)?
>

Yes I did, following Laurent's directions.

[snip]
>> +
>> +     f = mt9p031_get_pad_format(mt9p031, fh, fmt->pad, fmt->which);
>> +
>> +     if (f->width == format->width && f->height == format->height)
>> +             return 0;
>> +
>> +
>
> One empty line is usually enough, especially inside a function.
>
ok, I'll fix.

>> +     c = mt9p031_get_pad_crop(mt9p031, fh, fmt->pad, fmt->which);
>> +
>> +     rect.width = c->width;
>> +     rect.height = c->height;
>> +
>> +     xskip = mt9p031_skip_for_scale(&rect.width, format->width, 7,
>> +                                    MT9P031_MAX_WIDTH);
>> +     if (rect.width + c->left > MT9P031_MAX_WIDTH)
>> +             rect.left = (MT9P031_MAX_WIDTH - rect.width) / 2;
>> +     else
>> +             rect.left = c->left;
>> +     yskip = mt9p031_skip_for_scale(&rect.height, format->height, 8,
>> +                                    MT9P031_MAX_HEIGHT);
>> +     if (rect.height + c->top > MT9P031_MAX_HEIGHT)
>> +             rect.top = (MT9P031_MAX_HEIGHT - rect.height) / 2;
>> +     else
>> +             rect.top = c->top;
>> +
>> +
>> +     pr_info("%s(%ux%u : %u)\n", __func__,
>> +             format->width, format->height, fmt->which);
>
> dev_dbg()

ok

>
>> +     if (c)
>> +             *c = rect;
>> +
>> +     *f = *format;
>> +     fmt->format = *format;
>> +
>> +     mt9p031->xskip = xskip;
>> +     mt9p031->yskip = yskip;
>> +     mt9p031->rect = *c;
>> +     return 0;
>> +}
>> +
>> +static int mt9p031_s_stream(struct v4l2_subdev *sd, int enable)
>> +{
>> +     struct mt9p031 *mt9p031 = container_of(sd, struct mt9p031, subdev);
>> +     struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
>> +     struct v4l2_rect rect = mt9p031->rect;
>> +     u16 xskip = mt9p031->xskip;
>> +     u16 yskip = mt9p031->yskip;
>> +     int ret;
>> +
>> +     if (enable) {
>> +             ret = mt9p031_set_params(client, &rect, xskip, yskip);
>> +             if (ret < 0)
>> +                     return ret;
>> +             /* Switch to master "normal" mode */
>> +             ret = mt9p031_set_output_control(mt9p031, 0, MT9P031_OUTPUT_CONTROL_CEN);
>> +     } else {
>> +             /* Stop sensor readout */
>> +             ret = mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN, 0);
>> +     }
>> +     if (ret < 0)
>> +             return -EIO;
>> +
>> +     return 0;
>> +}
>> +
>> +/*
>> + * Interface active, can use i2c. If it fails, it can indeed mean, that
>> + * this wasn't our capture interface, so, we wait for the right one
>> + */
>> +static int mt9p031_video_probe(struct i2c_client *client)
>> +{
>> +     s32 data;
>> +     int ret;
>> +
>> +     /* Read out the chip version register */
>> +     data = reg_read(client, MT9P031_CHIP_VERSION);
>> +     if (data != MT9P031_CHIP_VERSION_VALUE) {
>> +             dev_err(&client->dev,
>> +                     "No MT9P031 chip detected, register read %x\n", data);
>> +             return -ENODEV;
>> +     }
>> +
>> +     dev_info(&client->dev, "Detected a MT9P031 chip ID %x\n", data);
>> +
>> +     ret = mt9p031_reset(client);
>> +     if (ret < 0)
>> +             dev_err(&client->dev, "Failed to initialise the camera\n");
>> +
>> +     return ret;
>> +}
>> +
>> +static int mt9p031_set_power(struct v4l2_subdev *sd, int on)
>> +{
>> +     struct mt9p031 *mt9p031 = container_of(sd, struct mt9p031, subdev);
>> +     int ret = 0;
>> +
>> +     mutex_lock(&mt9p031->power_lock);
>> +
>> +     /* If the power count is modified from 0 to != 0 or from != 0 to 0,
>> +      * update the power state.
>> +      */
>
> Multi-line comment style
>

Sure, let me fix that.

>> +     if (mt9p031->power_count == !on) {
>> +             if (on) {
>> +                     ret = mt9p031_power_on(mt9p031);
>> +                     if (ret) {
>> +                             pr_err("Failed to enable 2.8v regulator: %d\n", ret);
>
> dev_err

ok

>
>> +                             goto out;
>> +                     }
>> +             } else {
>> +                     mt9p031_power_off(mt9p031);
>> +             }
>> +     }
>> +
>> +     /* Update the power count. */
>> +     mt9p031->power_count +=on ? 1: -1;
>> +     WARN_ON(mt9p031->power_count < 0);
>> +
>> +out:
>> +     mutex_unlock(&mt9p031->power_lock);
>> +     return ret;
>> +}
>> +
>> +static int mt9p031_registered(struct v4l2_subdev *sd)
>> +{
>> +     struct mt9p031 *mt9p031 = container_of(sd, struct mt9p031, subdev);
>> +     struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
>> +     int ret;
>> +
>> +     ret = mt9p031_set_power(&mt9p031->subdev, 1);
>> +     if (ret) {
>> +             pr_err("Failed to power on device: %d\n", ret);
>
> dev_err()
>

ok

>> +             goto pwron;
>> +     }
>> +     if (mt9p031->pdata->reset)
>> +             mt9p031->pdata->reset(&mt9p031->subdev, 1);
>> +     msleep(50);
>> +     if (mt9p031->pdata->reset)
>> +             mt9p031->pdata->reset(&mt9p031->subdev, 0);
>> +     msleep(50);
>> +
>> +     ret = mt9p031_video_probe(client);
>> +     if (ret)
>> +             goto evprobe;
>> +
>> +     mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE;
>> +     ret = media_entity_init(&mt9p031->subdev.entity, 1, &mt9p031->pad, 0);
>> +     if (ret)
>> +             goto evprobe;
>> +
>> +     mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>> +     mt9p031_set_power(&mt9p031->subdev, 0);
>> +
>> +     return 0;
>> +evprobe:
>> +     mt9p031_set_power(&mt9p031->subdev, 0);
>> +pwron:
>> +     return ret;
>> +}
>> +
>> +static int mt9p031_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
>> +{
>> +     struct mt9p031 *mt9p031;
>> +     mt9p031 = container_of(sd, struct mt9p031, subdev);
>> +
>> +     return mt9p031_set_power(sd, 1);
>
> Is open() called only for the first open, or for each one? If for each,
> you'll want to reference count yourself. Besides, isn't
> core_ops::s_power() called anyway, maybe you don't need these open() /
> close() at all?
>

Yes, you are right. As I only enable/disable power in open/close, they
can be safely removed.

[snip]
>> +     mt9p031 = kzalloc(sizeof(struct mt9p031), GFP_KERNEL);
>> +     if (!mt9p031)
>> +             return -ENOMEM;
>> +
>> +     mutex_init(&mt9p031->power_lock);
>> +     v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops);
>> +     mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops;
>> +
>> +     mt9p031->pdata          = pdata;
>> +     mt9p031->rect.left      = 0;
>> +     mt9p031->rect.top       = 0;
>
> No need - kzalloc() has nullified it for you.

OK, I'll remove this.



-- 
Javier Martin
Vista Silicon S.L.
CDTUC - FASE C - Oficina S-345
Avda de los Castros s/n
39005- Santander. Cantabria. Spain
+34 942 25 32 60
www.vista-silicon.com
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


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