From: martin@neutronstar.dyndns.org
To: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Cc: Marek Vasut <marek.vasut@gmail.com>,
Hans Verkuil <hverkuil@xs4all.nl>,
linux-media@vger.kernel.org
Subject: Re: [PATCH v3] v4l: Add driver for Micron MT9M032 camera sensor
Date: Wed, 14 Dec 2011 19:58:45 +0100 [thread overview]
Message-ID: <1323889126.283763.19222@localhost> (raw)
In-Reply-To: <201112141449.05926.laurent.pinchart@ideasonboard.com>
On Wed, Dec 14, 2011 at 02:49:05PM +0100, Laurent Pinchart wrote:
> Hi Martin,
>
> On Wednesday 14 December 2011 08:14:01 martin@neutronstar.dyndns.org wrote:
> > On Wed, Dec 14, 2011 at 02:55:31AM +0100, Marek Vasut wrote:
> > > > The MT9M032 is a parallel 1.6MP sensor from Micron controlled through
> > > > I2C.
> > > >
> > > > The driver creates a V4L2 subdevice. It currently supports cropping,
> > > > gain, exposure and v/h flipping controls in monochrome mode with an
> > > > external pixel clock.
> > > >
> > > > Signed-off-by: Martin Hostettler <martin@neutronstar.dyndns.org>
> > > > ---
>
> [snip]
>
> > > > diff --git a/drivers/media/video/mt9m032.c
> > > > b/drivers/media/video/mt9m032.c new file mode 100644
> > > > index 0000000..b4159c7
> > > > --- /dev/null
> > > > +++ b/drivers/media/video/mt9m032.c
> > > > @@ -0,0 +1,819 @@
>
> [snip]
>
> > > > +static int mt9m032_read_reg(struct mt9m032 *sensor, u8 reg)
> > > > +{
> > > > + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
> > > > + s32 data = i2c_smbus_read_word_data(client, reg);
> > > > +
> > > > + return data < 0 ? data : swab16(data);
> > >
> > > Uhm ... why ?
> >
> > error codes are not swab16-ed, data values are. Hardware needs swapping of
> > data values.
>
> You can use i2c_smbus_read_word_swapped() and i2c_smbus_write_word_swapped().
Yes, i didn't notice that they have been added. Nicer to use them.
>
> > > > +}
> > > > +
> > > > +static int mt9m032_write_reg(struct mt9m032 *sensor, u8 reg,
> > > > + const u16 data)
> > > > +{
> > > > + struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
> > > > +
> > > > + return i2c_smbus_write_word_data(client, reg, swab16(data));
> > > > +}
>
> [snip]
>
> > > > +static int update_read_mode2(struct mt9m032 *sensor, bool vflip, bool
> > > > hflip) +{
> > > > + int reg_val = (!!vflip) << MT9M032_READ_MODE2_VFLIP_SHIFT
> > >
> > > !!(bool) ? hmm ...
> >
> > a true bool can be any value except 0. !!bool is guaranteed to be either
> > 0 or 1 and this suitable for use in bitshifting to set a specific bit.
>
> I'd use
>
> int reg_val = (vflip ? MT9M032_READ_MODE2_VFLIP : 0)
> | (hflip ? MT9M032_READ_MODE2_HFLIP : 0)
> ...
>
> but that might just be me.
I like shifts more, easy to check in the datasheet.
>
> BTW, what about using fixed-size types for register values ? u16 would make
> sense here.
I don't think i would make a difference here.
>
> > > > + | (!!hflip) << MT9M032_READ_MODE2_HFLIP_SHIFT
> > > > + | MT9M032_READ_MODE2_ROW_BLC
> > > > + | 0x0007;
> > > > +
> > > > + return mt9m032_write_reg(sensor, MT9M032_READ_MODE2, reg_val);
> > > > +}
>
> [snip]
>
> > > > +static int mt9m032_set_gain(struct mt9m032 *sensor, s32 val)
> > > > +{
> > > > + int digital_gain_val; /* in 1/8th (0..127) */
> > > > + int analog_mul; /* 0 or 1 */
> > > > + int analog_gain_val; /* in 1/16th. (0..63) */
> > > > + u16 reg_val;
> > > > +
> > > > + digital_gain_val = 51; /* from setup example */
> > > > +
> > > > + if (val < 63) {
> > > > + analog_mul = 0;
> > > > + analog_gain_val = val;
> > > > + } else {
> > > > + analog_mul = 1;
> > > > + analog_gain_val = val / 2;
> > > > + }
> > > > +
> > > > + /* a_gain = (1+analog_mul) + (analog_gain_val+1)/16 */
> > > > + /* overall_gain = a_gain * (1 + digital_gain_val / 8) */
> > > > +
> > > > + reg_val = (digital_gain_val & MT9M032_GAIN_DIGITAL_MASK) <<
> > > > MT9M032_GAIN_DIGITAL_SHIFT + | (analog_mul & 1) <<
> > > > MT9M032_GAIN_AMUL_SHIFT
> > > > + | (analog_gain_val & MT9M032_GAIN_ANALOG_MASK);
> > > > +
> > > > + return mt9m032_write_reg(sensor, MT9M032_GAIN_ALL, reg_val);
> > > > +}
>
> Lot's of Aptina sensors have a similar gain setup with two or three stages. Do
> you think it would be worth it creating a function that could be shared
> between them ?
I don't have time to work on such an extension in a meaningful way.
I'm not sure what's the best way to use this kind of staged gain hardware
best anyway.
>
> > > > +static int mt9m032_setup_pll(struct mt9m032 *sensor)
> > > > +{
> > > > + struct mt9m032_platform_data* pdata = sensor->pdata;
> > > > + u16 reg_pll1;
> > > > + unsigned int pre_div;
> > > > + int res, ret;
> > > > +
> > > > + /* TODO: also support other pre-div values */
>
> I might already have mentioned this, but wouldn't it be time to work a on real
> PLL setup code that compute the pre-divisor, multiplier and output divisor
> dynamically from the input and output clock frequencies ?
I'm not sure what the implications for quality and stability of such a
generic setup would be. My gut feeling is most users go with known working
hardcoded values.
Also in the datasheet i have access to, this is totally underdocumented.
>
> > > > + if (pdata->pll_pre_div != 6) {
> > > > + dev_warn(to_dev(sensor),
> > > > + "Unsupported PLL pre-divisor value %u, using default
> > >
> > > 6\n",
> > >
> > > > + pdata->pll_pre_div);
> > > > + }
> > > > + pre_div = 6;
> > > > +
> > > > + sensor->pix_clock = pdata->ext_clock * pdata->pll_mul /
> > > > + (pre_div * pdata->pll_out_div);
> > > > +
> > > > + reg_pll1 = ((pdata->pll_out_div - 1) &
> > > > MT9M032_PLL_CONFIG1_OUTDIV_MASK) + | pdata->pll_mul <<
> > > > MT9M032_PLL_CONFIG1_MUL_SHIFT;
> > > > +
> > > > + ret = mt9m032_write_reg(sensor, MT9M032_PLL_CONFIG1, reg_pll1);
> > > > + if (!ret)
> > > > + ret = mt9m032_write_reg(sensor, 0x10, 0x53); /* Select PLL as
> > >
> > > clock
> > >
> > > > source */ +
> > >
> > > urm ... magic ... black magic ...
> >
> > yes, indeed. But i don't have any more information.
> > The datasheet nicely states "reserved" and in the bringup section
> > "poke in these magical values, please". :/ Not even a register name.
>
> What version of the datasheet do you have ? According to the MT9P031
Rev. B 10/07 EN
> datasheet, register 0x10 is called PLL Control and bits 0 and 1 are described
> as
>
> 1 Use PLL
> When set, use the PLL output as the system clock. When clear, use EXTCLK as
> the system clock.
> 0 Power PLL
> When set, the PLL is powered. When clear, it is not powered.
>
> The other bits are not documented. The mt9p031 driver has
>
> #define MT9P031_PLL_CONTROL 0x10
> #define MT9P031_PLL_CONTROL_PWROFF 0x0050
> #define MT9P031_PLL_CONTROL_PWRON 0x0051
> #define MT9P031_PLL_CONTROL_USEPLL 0x0052
>
> which you could reuse.
Yes, it seems close enough. But i'm a bit uneasy about mixing from
different datasheets. Would i keep the original names so everybody can see
that it's from a data sheet of a similar but different hardware?
It would be
MT9P031_PLL_CONTROL_PWRON | MT9P031_PLL_CONTROL_USEPLL
then witch resolves to a slightly odd 0x0051 | 0x0052.
>
> > > > + if (!ret)
> > > > + ret = mt9m032_write_reg(sensor, MT9M032_READ_MODE1, 0x8006);
> > > > + /* more reserved, Continuous */
> > > > + /* Master Mode */
> > > > + if (!ret)
> > > > + res = mt9m032_read_reg(sensor, MT9M032_READ_MODE1);
> > > > +
> > > > + if (!ret)
> > > > + ret = mt9m032_write_reg(sensor, MT9M032_FORMATTER1, 0x111e);
> > > > + /* Set 14-bit mode, select 7 divider */
> > > > +
> > > > + return ret;
> > > > +}
>
>
> [snip]
>
> > > > + /*
> > > > +* This driver was developed with a camera module with seperate external
> > > > +* pix clock. For setups which use the clock from the camera interface
> > > > +* the code will need to be extended with the appropriate platform
> > > > +* callback to setup the clock.
> > > > + */
> > > > + chip_version = mt9m032_read_reg(sensor, MT9M032_CHIP_VERSION);
> > > > + if (0x1402 == chip_version) {
> > >
> > > So I suspect this can also cast fireballs ? Also, why do you use Yoda
> > > conditions here ? Please run checkpatch.pl on this too just to be sure.
> >
> > No fireball was visible when looking at the hardware.
>
> #define MT9M032_CHIP_VERSION_VALUE 0x1402
>
> is an option (not sure if it prevents fireballs though).
I don't think it will prevent any burns or fires.
But i'll add it anyway, it's clear enough.
>
> > > > + dev_info(&client->dev, "mt9m032: detected sensor.\n");
> > > > + } else {
> > > > + dev_warn(&client->dev, "mt9m032: error: detected unsupported
> > > chip version 0x%x\n",
> > > > + chip_version);
> > > > + ret = -ENODEV;
> > > > + goto free_sensor;
> > > > + }
>
> [snip]
>
> > > > +static int __init mt9m032_init(void)
> > > > +{
> > > > + int rval;
> > > > +
> > > > + rval = i2c_add_driver(&mt9m032_i2c_driver);
> > > > + if (rval)
> > > > + pr_err("%s: failed registering " MT9M032_NAME "\n", __func__);
> > > > +
> > > > + return rval;
> > > > +}
> > > > +
> > > > +static void mt9m032_exit(void)
> > > > +{
> > > > + i2c_del_driver(&mt9m032_i2c_driver);
> > > > +}
> > > > +
> > > > +module_init(mt9m032_init);
> > > > +module_exit(mt9m032_exit);
>
> v3.3 will have a very handy module_i2c_driver() macro. See
> https://lkml.org/lkml/2011/11/17/36
If you think i should do that i can. Looses a bit verboseness in case of
errors, but i don't think it matters.
For me both ways are ok.
>
> > > > +MODULE_AUTHOR("Martin Hostettler");
> > > > +MODULE_DESCRIPTION("MT9M032 camera sensor driver");
> > > > +MODULE_LICENSE("GPL v2");
>
> --
> Regards,
>
> Laurent Pinchart
next prev parent reply other threads:[~2011-12-14 18:58 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-12-14 1:20 [PATCH v3] v4l: Add driver for Micron MT9M032 camera sensor Martin Hostettler
2011-12-14 1:55 ` Marek Vasut
2011-12-14 7:14 ` martin
2011-12-14 13:49 ` Laurent Pinchart
2011-12-14 18:58 ` martin [this message]
2011-12-14 22:11 ` Sakari Ailus
2011-12-17 9:50 ` martin
2011-12-19 10:47 ` Laurent Pinchart
2011-12-19 21:43 ` Sakari Ailus
2011-12-19 10:43 ` Laurent Pinchart
2011-12-14 21:44 ` Guennadi Liakhovetski
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1323889126.283763.19222@localhost \
--to=martin@neutronstar.dyndns.org \
--cc=hverkuil@xs4all.nl \
--cc=laurent.pinchart@ideasonboard.com \
--cc=linux-media@vger.kernel.org \
--cc=marek.vasut@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox