2020-09-30 10:53:28

by Stefan Riedmüller

[permalink] [raw]
Subject: [PATCH v2 1/5] media: mt9p031: Add support for 8 bit and 10 bit formats

From: Christian Hemp <[email protected]>

Aside from 12 bit monochrome or color format the sensor implicitly
supports 10 and 8 bit formats as well by simply dropping the
corresponding LSBs.

Signed-off-by: Christian Hemp <[email protected]>
[[email protected]: simplified by dropping v4l2_colorspace handling]
Signed-off-by: Jan Luebbe <[email protected]>
Signed-off-by: Stefan Riedmueller <[email protected]>
---
Changes in v2:
- Use unsigned int for num_fmts and loop variable in find_datafmt
- Remove superfluous const qualifier from find_datafmt
---
drivers/media/i2c/mt9p031.c | 50 +++++++++++++++++++++++++++++--------
1 file changed, 40 insertions(+), 10 deletions(-)

diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
index dc23b9ed510a..2e6671ef877c 100644
--- a/drivers/media/i2c/mt9p031.c
+++ b/drivers/media/i2c/mt9p031.c
@@ -116,6 +116,18 @@ enum mt9p031_model {
MT9P031_MODEL_MONOCHROME,
};

+static const u32 mt9p031_color_fmts[] = {
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+};
+
+static const u32 mt9p031_monochrome_fmts[] = {
+ MEDIA_BUS_FMT_Y8_1X8,
+ MEDIA_BUS_FMT_Y10_1X10,
+ MEDIA_BUS_FMT_Y12_1X12,
+};
+
struct mt9p031 {
struct v4l2_subdev subdev;
struct media_pad pad;
@@ -138,6 +150,9 @@ struct mt9p031 {
struct v4l2_ctrl *blc_auto;
struct v4l2_ctrl *blc_offset;

+ const u32 *fmts;
+ unsigned int num_fmts;
+
/* Registers cache */
u16 output_control;
u16 mode2;
@@ -148,6 +163,17 @@ static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd)
return container_of(sd, struct mt9p031, subdev);
}

+static u32 mt9p031_find_datafmt(struct mt9p031 *mt9p031, u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < mt9p031->num_fmts; i++)
+ if (mt9p031->fmts[i] == code)
+ return mt9p031->fmts[i];
+
+ return mt9p031->fmts[mt9p031->num_fmts-1];
+}
+
static int mt9p031_read(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_word_swapped(client, reg);
@@ -476,10 +502,11 @@ static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev,
{
struct mt9p031 *mt9p031 = to_mt9p031(subdev);

- if (code->pad || code->index)
+ if (code->pad || code->index >= mt9p031->num_fmts)
return -EINVAL;

- code->code = mt9p031->format.code;
+ code->code = mt9p031->fmts[code->index];
+
return 0;
}

@@ -573,6 +600,8 @@ static int mt9p031_set_format(struct v4l2_subdev *subdev,
__format->width = __crop->width / hratio;
__format->height = __crop->height / vratio;

+ __format->code = mt9p031_find_datafmt(mt9p031, format->format.code);
+
format->format = *__format;

return 0;
@@ -951,10 +980,7 @@ static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)

format = v4l2_subdev_get_try_format(subdev, fh->pad, 0);

- if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
- format->code = MEDIA_BUS_FMT_Y12_1X12;
- else
- format->code = MEDIA_BUS_FMT_SGRBG12_1X12;
+ format->code = mt9p031_find_datafmt(mt9p031, 0);

format->width = MT9P031_WINDOW_WIDTH_DEF;
format->height = MT9P031_WINDOW_HEIGHT_DEF;
@@ -1121,10 +1147,14 @@ static int mt9p031_probe(struct i2c_client *client,
mt9p031->crop.left = MT9P031_COLUMN_START_DEF;
mt9p031->crop.top = MT9P031_ROW_START_DEF;

- if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
- mt9p031->format.code = MEDIA_BUS_FMT_Y12_1X12;
- else
- mt9p031->format.code = MEDIA_BUS_FMT_SGRBG12_1X12;
+ if (mt9p031->model == MT9P031_MODEL_MONOCHROME) {
+ mt9p031->fmts = mt9p031_monochrome_fmts;
+ mt9p031->num_fmts = ARRAY_SIZE(mt9p031_monochrome_fmts);
+ } else {
+ mt9p031->fmts = mt9p031_color_fmts;
+ mt9p031->num_fmts = ARRAY_SIZE(mt9p031_color_fmts);
+ }
+ mt9p031->format.code = mt9p031_find_datafmt(mt9p031, 0);

mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF;
mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF;
--
2.25.1


2020-09-30 10:53:59

by Stefan Riedmüller

[permalink] [raw]
Subject: [PATCH v2 2/5] media: mt9p031: Read back the real clock rate

From: Enrico Scholz <[email protected]>

The real and requested clock can differ and because it is used to
calculate PLL values, the real clock rate should be read.

Signed-off-by: Enrico Scholz <[email protected]>
Signed-off-by: Stefan Riedmueller <[email protected]>
---
No changes in v2
---
drivers/media/i2c/mt9p031.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
index 2e6671ef877c..b4c042f418c1 100644
--- a/drivers/media/i2c/mt9p031.c
+++ b/drivers/media/i2c/mt9p031.c
@@ -255,6 +255,7 @@ static int mt9p031_clk_setup(struct mt9p031 *mt9p031)

struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
struct mt9p031_platform_data *pdata = mt9p031->pdata;
+ unsigned long ext_freq;
int ret;

mt9p031->clk = devm_clk_get(&client->dev, NULL);
@@ -265,13 +266,15 @@ static int mt9p031_clk_setup(struct mt9p031 *mt9p031)
if (ret < 0)
return ret;

+ ext_freq = clk_get_rate(mt9p031->clk);
+
/* If the external clock frequency is out of bounds for the PLL use the
* pixel clock divider only and disable the PLL.
*/
- if (pdata->ext_freq > limits.ext_clock_max) {
+ if (ext_freq > limits.ext_clock_max) {
unsigned int div;

- div = DIV_ROUND_UP(pdata->ext_freq, pdata->target_freq);
+ div = DIV_ROUND_UP(ext_freq, pdata->target_freq);
div = roundup_pow_of_two(div) / 2;

mt9p031->clk_div = min_t(unsigned int, div, 64);
@@ -280,7 +283,7 @@ static int mt9p031_clk_setup(struct mt9p031 *mt9p031)
return 0;
}

- mt9p031->pll.ext_clock = pdata->ext_freq;
+ mt9p031->pll.ext_clock = ext_freq;
mt9p031->pll.pix_clock = pdata->target_freq;
mt9p031->use_pll = true;

--
2.25.1

2020-09-30 10:54:23

by Stefan Riedmüller

[permalink] [raw]
Subject: [PATCH v2 5/5] media: mt9p031: Fix corrupted frame after restarting stream

From: Dirk Bender <[email protected]>

To prevent corrupted frames after starting and stopping the sensor it's
datasheet specifies a specific pause sequence to follow:

Stopping:
Set Pause_Restart Bit -> Set Restart Bit -> Set Chip_Enable Off

Restarting:
Set Chip_Enable On -> Clear Pause_Restart Bit

The Restart Bit is cleared automatically and must not be cleared
manually as this would cause undefined behavior.

Signed-off-by: Dirk Bender <[email protected]>
Signed-off-by: Stefan Riedmueller <[email protected]>
---
No changes in v2
---
drivers/media/i2c/mt9p031.c | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)

diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
index d10457361e6c..d59f66e3dcf3 100644
--- a/drivers/media/i2c/mt9p031.c
+++ b/drivers/media/i2c/mt9p031.c
@@ -80,6 +80,8 @@
#define MT9P031_PIXEL_CLOCK_SHIFT(n) ((n) << 8)
#define MT9P031_PIXEL_CLOCK_DIVIDE(n) ((n) << 0)
#define MT9P031_FRAME_RESTART 0x0b
+#define MT9P031_FRAME_RESTART_SET (1 << 0)
+#define MT9P031_FRAME_PAUSE_RESTART_SET (1 << 1)
#define MT9P031_SHUTTER_DELAY 0x0c
#define MT9P031_RST 0x0d
#define MT9P031_RST_ENABLE 1
@@ -483,9 +485,25 @@ static int mt9p031_set_params(struct mt9p031 *mt9p031)
static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)
{
struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ int val;
int ret;

if (!enable) {
+ val = mt9p031_read(client, MT9P031_FRAME_RESTART);
+
+ /* enable pause restart */
+ val |= MT9P031_FRAME_PAUSE_RESTART_SET;
+ ret = mt9p031_write(client, MT9P031_FRAME_RESTART, val);
+ if (ret < 0)
+ return ret;
+
+ /* enable restart + keep pause restart set */
+ val |= MT9P031_FRAME_RESTART_SET;
+ ret = mt9p031_write(client, MT9P031_FRAME_RESTART, val);
+ if (ret < 0)
+ return ret;
+
/* Stop sensor readout */
ret = mt9p031_set_output_control(mt9p031,
MT9P031_OUTPUT_CONTROL_CEN, 0);
@@ -505,6 +523,13 @@ static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)
if (ret < 0)
return ret;

+ val = mt9p031_read(client, MT9P031_FRAME_RESTART);
+ /* disable reset + pause restart */
+ val &= ~MT9P031_FRAME_PAUSE_RESTART_SET;
+ ret = mt9p031_write(client, MT9P031_FRAME_RESTART, val);
+ if (ret < 0)
+ return ret;
+
return mt9p031_pll_enable(mt9p031);
}

--
2.25.1

2020-09-30 10:55:45

by Stefan Riedmüller

[permalink] [raw]
Subject: [PATCH v2 3/5] media: mt9p031: Implement [gs]_register debug calls

From: Enrico Scholz <[email protected]>

Implement g_register and s_register v4l2_subdev_core_ops to access
camera register directly from userspace for debug purposes.

Signed-off-by: Enrico Scholz <[email protected]>
Signed-off-by: Stefan Riedmueller <[email protected]>
---
No changes in v2
---
drivers/media/i2c/mt9p031.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)

diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
index b4c042f418c1..de36025260a8 100644
--- a/drivers/media/i2c/mt9p031.c
+++ b/drivers/media/i2c/mt9p031.c
@@ -703,6 +703,30 @@ static int mt9p031_restore_blc(struct mt9p031 *mt9p031)
return 0;
}

+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9p031_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ ret = mt9p031_read(client, reg->reg);
+ if (ret < 0)
+ return ret;
+
+ reg->val = ret;
+ return 0;
+}
+
+static int mt9p031_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register const *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return mt9p031_write(client, reg->reg, reg->val);
+}
+#endif
+
static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct mt9p031 *mt9p031 =
@@ -1000,6 +1024,10 @@ static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)

static const struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = {
.s_power = mt9p031_set_power,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .s_register = mt9p031_s_register,
+ .g_register = mt9p031_g_register,
+#endif
};

static const struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = {
--
2.25.1

2020-09-30 11:41:03

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v2 3/5] media: mt9p031: Implement [gs]_register debug calls

Hi Stefan,

Thank you for the patch.

On Wed, Sep 30, 2020 at 12:51:31PM +0200, Stefan Riedmueller wrote:
> From: Enrico Scholz <[email protected]>
>
> Implement g_register and s_register v4l2_subdev_core_ops to access
> camera register directly from userspace for debug purposes.

As the name of the operations imply, this is meant for debug purpose
only. They are however prone to be abused to configure the sensor from
userspace in production, which isn't a direction we want to take.
What's your use case for this ? I'd rather drop this patch and see the
driver extended with support for more controls if needed

> Signed-off-by: Enrico Scholz <[email protected]>
> Signed-off-by: Stefan Riedmueller <[email protected]>
> ---
> No changes in v2
> ---
> drivers/media/i2c/mt9p031.c | 28 ++++++++++++++++++++++++++++
> 1 file changed, 28 insertions(+)
>
> diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
> index b4c042f418c1..de36025260a8 100644
> --- a/drivers/media/i2c/mt9p031.c
> +++ b/drivers/media/i2c/mt9p031.c
> @@ -703,6 +703,30 @@ static int mt9p031_restore_blc(struct mt9p031 *mt9p031)
> return 0;
> }
>
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +static int mt9p031_g_register(struct v4l2_subdev *sd,
> + struct v4l2_dbg_register *reg)
> +{
> + struct i2c_client *client = v4l2_get_subdevdata(sd);
> + int ret;
> +
> + ret = mt9p031_read(client, reg->reg);
> + if (ret < 0)
> + return ret;
> +
> + reg->val = ret;
> + return 0;
> +}
> +
> +static int mt9p031_s_register(struct v4l2_subdev *sd,
> + struct v4l2_dbg_register const *reg)
> +{
> + struct i2c_client *client = v4l2_get_subdevdata(sd);
> +
> + return mt9p031_write(client, reg->reg, reg->val);
> +}
> +#endif
> +
> static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
> {
> struct mt9p031 *mt9p031 =
> @@ -1000,6 +1024,10 @@ static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
>
> static const struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = {
> .s_power = mt9p031_set_power,
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> + .s_register = mt9p031_s_register,
> + .g_register = mt9p031_g_register,
> +#endif
> };
>
> static const struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = {

--
Regards,

Laurent Pinchart

2020-09-30 11:45:56

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v2 1/5] media: mt9p031: Add support for 8 bit and 10 bit formats

Hi Stefan,

Thank you for the patch.

On Wed, Sep 30, 2020 at 12:51:29PM +0200, Stefan Riedmueller wrote:
> From: Christian Hemp <[email protected]>
>
> Aside from 12 bit monochrome or color format the sensor implicitly
> supports 10 and 8 bit formats as well by simply dropping the
> corresponding LSBs.

That's not how it should work though. If you set the format on
MEDIA_BUS_FMT_SGRBG8_1X8 through the pipeline for instance, you will end
up capturing the 8 LSB, not the 8 MSB.

What's your use case for this ?

> Signed-off-by: Christian Hemp <[email protected]>
> [[email protected]: simplified by dropping v4l2_colorspace handling]
> Signed-off-by: Jan Luebbe <[email protected]>
> Signed-off-by: Stefan Riedmueller <[email protected]>
> ---
> Changes in v2:
> - Use unsigned int for num_fmts and loop variable in find_datafmt
> - Remove superfluous const qualifier from find_datafmt
> ---
> drivers/media/i2c/mt9p031.c | 50 +++++++++++++++++++++++++++++--------
> 1 file changed, 40 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
> index dc23b9ed510a..2e6671ef877c 100644
> --- a/drivers/media/i2c/mt9p031.c
> +++ b/drivers/media/i2c/mt9p031.c
> @@ -116,6 +116,18 @@ enum mt9p031_model {
> MT9P031_MODEL_MONOCHROME,
> };
>
> +static const u32 mt9p031_color_fmts[] = {
> + MEDIA_BUS_FMT_SGRBG8_1X8,
> + MEDIA_BUS_FMT_SGRBG10_1X10,
> + MEDIA_BUS_FMT_SGRBG12_1X12,
> +};
> +
> +static const u32 mt9p031_monochrome_fmts[] = {
> + MEDIA_BUS_FMT_Y8_1X8,
> + MEDIA_BUS_FMT_Y10_1X10,
> + MEDIA_BUS_FMT_Y12_1X12,
> +};
> +
> struct mt9p031 {
> struct v4l2_subdev subdev;
> struct media_pad pad;
> @@ -138,6 +150,9 @@ struct mt9p031 {
> struct v4l2_ctrl *blc_auto;
> struct v4l2_ctrl *blc_offset;
>
> + const u32 *fmts;
> + unsigned int num_fmts;
> +
> /* Registers cache */
> u16 output_control;
> u16 mode2;
> @@ -148,6 +163,17 @@ static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd)
> return container_of(sd, struct mt9p031, subdev);
> }
>
> +static u32 mt9p031_find_datafmt(struct mt9p031 *mt9p031, u32 code)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < mt9p031->num_fmts; i++)
> + if (mt9p031->fmts[i] == code)
> + return mt9p031->fmts[i];
> +
> + return mt9p031->fmts[mt9p031->num_fmts-1];
> +}
> +
> static int mt9p031_read(struct i2c_client *client, u8 reg)
> {
> return i2c_smbus_read_word_swapped(client, reg);
> @@ -476,10 +502,11 @@ static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev,
> {
> struct mt9p031 *mt9p031 = to_mt9p031(subdev);
>
> - if (code->pad || code->index)
> + if (code->pad || code->index >= mt9p031->num_fmts)
> return -EINVAL;
>
> - code->code = mt9p031->format.code;
> + code->code = mt9p031->fmts[code->index];
> +
> return 0;
> }
>
> @@ -573,6 +600,8 @@ static int mt9p031_set_format(struct v4l2_subdev *subdev,
> __format->width = __crop->width / hratio;
> __format->height = __crop->height / vratio;
>
> + __format->code = mt9p031_find_datafmt(mt9p031, format->format.code);
> +
> format->format = *__format;
>
> return 0;
> @@ -951,10 +980,7 @@ static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
>
> format = v4l2_subdev_get_try_format(subdev, fh->pad, 0);
>
> - if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
> - format->code = MEDIA_BUS_FMT_Y12_1X12;
> - else
> - format->code = MEDIA_BUS_FMT_SGRBG12_1X12;
> + format->code = mt9p031_find_datafmt(mt9p031, 0);
>
> format->width = MT9P031_WINDOW_WIDTH_DEF;
> format->height = MT9P031_WINDOW_HEIGHT_DEF;
> @@ -1121,10 +1147,14 @@ static int mt9p031_probe(struct i2c_client *client,
> mt9p031->crop.left = MT9P031_COLUMN_START_DEF;
> mt9p031->crop.top = MT9P031_ROW_START_DEF;
>
> - if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
> - mt9p031->format.code = MEDIA_BUS_FMT_Y12_1X12;
> - else
> - mt9p031->format.code = MEDIA_BUS_FMT_SGRBG12_1X12;
> + if (mt9p031->model == MT9P031_MODEL_MONOCHROME) {
> + mt9p031->fmts = mt9p031_monochrome_fmts;
> + mt9p031->num_fmts = ARRAY_SIZE(mt9p031_monochrome_fmts);
> + } else {
> + mt9p031->fmts = mt9p031_color_fmts;
> + mt9p031->num_fmts = ARRAY_SIZE(mt9p031_color_fmts);
> + }
> + mt9p031->format.code = mt9p031_find_datafmt(mt9p031, 0);
>
> mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF;
> mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF;

--
Regards,

Laurent Pinchart

2020-10-01 09:01:06

by Stefan Riedmüller

[permalink] [raw]
Subject: Re: [PATCH v2 3/5] media: mt9p031: Implement [gs]_register debug calls

Hi Laurent,

On 30.09.20 13:38, Laurent Pinchart wrote:
> Hi Stefan,
>
> Thank you for the patch.
>
> On Wed, Sep 30, 2020 at 12:51:31PM +0200, Stefan Riedmueller wrote:
>> From: Enrico Scholz <[email protected]>
>>
>> Implement g_register and s_register v4l2_subdev_core_ops to access
>> camera register directly from userspace for debug purposes.
>
> As the name of the operations imply, this is meant for debug purpose
> only. They are however prone to be abused to configure the sensor from
> userspace in production, which isn't a direction we want to take.
> What's your use case for this ? I'd rather drop this patch and see the
> driver extended with support for more controls if needed

thanks for your feedback.

I get your point. I myself solely use these operations for debugging
purposes but I'm aware that others like to abuse them.

I thought I send it anyway since for me the DEBUG config is enough to
signalize that these operations are not to be used with a productive system.
But I'm OK with dropping this patch if you think it might send the wrong signal.

Regards,
Stefan

>
>> Signed-off-by: Enrico Scholz <[email protected]>
>> Signed-off-by: Stefan Riedmueller <[email protected]>
>> ---
>> No changes in v2
>> ---
>> drivers/media/i2c/mt9p031.c | 28 ++++++++++++++++++++++++++++
>> 1 file changed, 28 insertions(+)
>>
>> diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
>> index b4c042f418c1..de36025260a8 100644
>> --- a/drivers/media/i2c/mt9p031.c
>> +++ b/drivers/media/i2c/mt9p031.c
>> @@ -703,6 +703,30 @@ static int mt9p031_restore_blc(struct mt9p031 *mt9p031)
>> return 0;
>> }
>>
>> +#ifdef CONFIG_VIDEO_ADV_DEBUG
>> +static int mt9p031_g_register(struct v4l2_subdev *sd,
>> + struct v4l2_dbg_register *reg)
>> +{
>> + struct i2c_client *client = v4l2_get_subdevdata(sd);
>> + int ret;
>> +
>> + ret = mt9p031_read(client, reg->reg);
>> + if (ret < 0)
>> + return ret;
>> +
>> + reg->val = ret;
>> + return 0;
>> +}
>> +
>> +static int mt9p031_s_register(struct v4l2_subdev *sd,
>> + struct v4l2_dbg_register const *reg)
>> +{
>> + struct i2c_client *client = v4l2_get_subdevdata(sd);
>> +
>> + return mt9p031_write(client, reg->reg, reg->val);
>> +}
>> +#endif
>> +
>> static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
>> {
>> struct mt9p031 *mt9p031 =
>> @@ -1000,6 +1024,10 @@ static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
>>
>> static const struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = {
>> .s_power = mt9p031_set_power,
>> +#ifdef CONFIG_VIDEO_ADV_DEBUG
>> + .s_register = mt9p031_s_register,
>> + .g_register = mt9p031_g_register,
>> +#endif
>> };
>>
>> static const struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = {
>

2020-10-01 09:08:52

by Stefan Riedmüller

[permalink] [raw]
Subject: Re: [PATCH v2 1/5] media: mt9p031: Add support for 8 bit and 10 bit formats

Hi Laurent,

On 30.09.20 13:42, Laurent Pinchart wrote:
> Hi Stefan,
>
> Thank you for the patch.
>
> On Wed, Sep 30, 2020 at 12:51:29PM +0200, Stefan Riedmueller wrote:
>> From: Christian Hemp <[email protected]>
>>
>> Aside from 12 bit monochrome or color format the sensor implicitly
>> supports 10 and 8 bit formats as well by simply dropping the
>> corresponding LSBs.
>
> That's not how it should work though. If you set the format on
> MEDIA_BUS_FMT_SGRBG8_1X8 through the pipeline for instance, you will end
> up capturing the 8 LSB, not the 8 MSB.
>
> What's your use case for this ?

I use this sensor in combination with an i.MX 6 and i.MX 6UL. When the
sensor is connected with 12 bit (or 10 bit on the i.MX 6UL) and I set
MEDIA_BUS_FMT_SGRBG8_1X8 through the pipeline the CSI interface drops the
unused 4 LSB (or 2 LSB on the i.MX 6UL) so I get the 8 MSB from my 12 bit
sensor.

Does this clarify things? Maybe the description in the commit message is not
accurate enough or did I get something wrong?

Thanks,
Stefan

>
>> Signed-off-by: Christian Hemp <[email protected]>
>> [[email protected]: simplified by dropping v4l2_colorspace handling]
>> Signed-off-by: Jan Luebbe <[email protected]>
>> Signed-off-by: Stefan Riedmueller <[email protected]>
>> ---
>> Changes in v2:
>> - Use unsigned int for num_fmts and loop variable in find_datafmt
>> - Remove superfluous const qualifier from find_datafmt
>> ---
>> drivers/media/i2c/mt9p031.c | 50 +++++++++++++++++++++++++++++--------
>> 1 file changed, 40 insertions(+), 10 deletions(-)
>>
>> diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
>> index dc23b9ed510a..2e6671ef877c 100644
>> --- a/drivers/media/i2c/mt9p031.c
>> +++ b/drivers/media/i2c/mt9p031.c
>> @@ -116,6 +116,18 @@ enum mt9p031_model {
>> MT9P031_MODEL_MONOCHROME,
>> };
>>
>> +static const u32 mt9p031_color_fmts[] = {
>> + MEDIA_BUS_FMT_SGRBG8_1X8,
>> + MEDIA_BUS_FMT_SGRBG10_1X10,
>> + MEDIA_BUS_FMT_SGRBG12_1X12,
>> +};
>> +
>> +static const u32 mt9p031_monochrome_fmts[] = {
>> + MEDIA_BUS_FMT_Y8_1X8,
>> + MEDIA_BUS_FMT_Y10_1X10,
>> + MEDIA_BUS_FMT_Y12_1X12,
>> +};
>> +
>> struct mt9p031 {
>> struct v4l2_subdev subdev;
>> struct media_pad pad;
>> @@ -138,6 +150,9 @@ struct mt9p031 {
>> struct v4l2_ctrl *blc_auto;
>> struct v4l2_ctrl *blc_offset;
>>
>> + const u32 *fmts;
>> + unsigned int num_fmts;
>> +
>> /* Registers cache */
>> u16 output_control;
>> u16 mode2;
>> @@ -148,6 +163,17 @@ static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd)
>> return container_of(sd, struct mt9p031, subdev);
>> }
>>
>> +static u32 mt9p031_find_datafmt(struct mt9p031 *mt9p031, u32 code)
>> +{
>> + unsigned int i;
>> +
>> + for (i = 0; i < mt9p031->num_fmts; i++)
>> + if (mt9p031->fmts[i] == code)
>> + return mt9p031->fmts[i];
>> +
>> + return mt9p031->fmts[mt9p031->num_fmts-1];
>> +}
>> +
>> static int mt9p031_read(struct i2c_client *client, u8 reg)
>> {
>> return i2c_smbus_read_word_swapped(client, reg);
>> @@ -476,10 +502,11 @@ static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev,
>> {
>> struct mt9p031 *mt9p031 = to_mt9p031(subdev);
>>
>> - if (code->pad || code->index)
>> + if (code->pad || code->index >= mt9p031->num_fmts)
>> return -EINVAL;
>>
>> - code->code = mt9p031->format.code;
>> + code->code = mt9p031->fmts[code->index];
>> +
>> return 0;
>> }
>>
>> @@ -573,6 +600,8 @@ static int mt9p031_set_format(struct v4l2_subdev *subdev,
>> __format->width = __crop->width / hratio;
>> __format->height = __crop->height / vratio;
>>
>> + __format->code = mt9p031_find_datafmt(mt9p031, format->format.code);
>> +
>> format->format = *__format;
>>
>> return 0;
>> @@ -951,10 +980,7 @@ static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
>>
>> format = v4l2_subdev_get_try_format(subdev, fh->pad, 0);
>>
>> - if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
>> - format->code = MEDIA_BUS_FMT_Y12_1X12;
>> - else
>> - format->code = MEDIA_BUS_FMT_SGRBG12_1X12;
>> + format->code = mt9p031_find_datafmt(mt9p031, 0);
>>
>> format->width = MT9P031_WINDOW_WIDTH_DEF;
>> format->height = MT9P031_WINDOW_HEIGHT_DEF;
>> @@ -1121,10 +1147,14 @@ static int mt9p031_probe(struct i2c_client *client,
>> mt9p031->crop.left = MT9P031_COLUMN_START_DEF;
>> mt9p031->crop.top = MT9P031_ROW_START_DEF;
>>
>> - if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
>> - mt9p031->format.code = MEDIA_BUS_FMT_Y12_1X12;
>> - else
>> - mt9p031->format.code = MEDIA_BUS_FMT_SGRBG12_1X12;
>> + if (mt9p031->model == MT9P031_MODEL_MONOCHROME) {
>> + mt9p031->fmts = mt9p031_monochrome_fmts;
>> + mt9p031->num_fmts = ARRAY_SIZE(mt9p031_monochrome_fmts);
>> + } else {
>> + mt9p031->fmts = mt9p031_color_fmts;
>> + mt9p031->num_fmts = ARRAY_SIZE(mt9p031_color_fmts);
>> + }
>> + mt9p031->format.code = mt9p031_find_datafmt(mt9p031, 0);
>>
>> mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF;
>> mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF;
>

2020-10-01 23:57:51

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v2 1/5] media: mt9p031: Add support for 8 bit and 10 bit formats

Hi Stefan,

On Thu, Oct 01, 2020 at 11:07:00AM +0200, Stefan Riedmüller wrote:
> On 30.09.20 13:42, Laurent Pinchart wrote:
> > On Wed, Sep 30, 2020 at 12:51:29PM +0200, Stefan Riedmueller wrote:
> >> From: Christian Hemp <[email protected]>
> >>
> >> Aside from 12 bit monochrome or color format the sensor implicitly
> >> supports 10 and 8 bit formats as well by simply dropping the
> >> corresponding LSBs.
> >
> > That's not how it should work though. If you set the format on
> > MEDIA_BUS_FMT_SGRBG8_1X8 through the pipeline for instance, you will end
> > up capturing the 8 LSB, not the 8 MSB.
> >
> > What's your use case for this ?
>
> I use this sensor in combination with an i.MX 6 and i.MX 6UL. When the
> sensor is connected with 12 bit (or 10 bit on the i.MX 6UL) and I set
> MEDIA_BUS_FMT_SGRBG8_1X8 through the pipeline the CSI interface drops the
> unused 4 LSB (or 2 LSB on the i.MX 6UL) so I get the 8 MSB from my 12 bit
> sensor.

Is that the PIXEL_BIT bit in CSI_CSICR1 for the i.MX6UL ? If so I think
this should be handled in the imx7-media-csi driver. You could set the
format to MEDIA_BUS_FMT_SGRBG10_1X10 on the sink pad of the CSI and to
MEDIA_BUS_FMT_SGRBG8_1X8 on the source pad to configure this. I don't
think the sensor driver should be involved, otherwise we'd have to patch
all sensor drivers. From a sensor point of view, it outputs 12-bit
Bayer, not 8-bit.

Now there's a caveat. When used with the i.MX6UL, I assume you connected
D[11:2] of the sensor to D[9:0] of the i.MX6UL, right ? The i.MX6UL
doesn't support 12-bit inputs, so it should accept
MEDIA_BUS_FMT_SGRBG12_1X12 on its sink pad. In this case, as D[1:0] of
the sensor are left unconnected, I think you should set data-shift to 2
and bus-width to 10 in DT on the sensor side. The MT9P031 driver should
parse that, and output MEDIA_BUS_FMT_SGRBG10_1X10 instead of
MEDIA_BUS_FMT_SGRBG12_1X12 in that case.

> Does this clarify things? Maybe the description in the commit message is not
> accurate enough or did I get something wrong?
>
> >> Signed-off-by: Christian Hemp <[email protected]>
> >> [[email protected]: simplified by dropping v4l2_colorspace handling]
> >> Signed-off-by: Jan Luebbe <[email protected]>
> >> Signed-off-by: Stefan Riedmueller <[email protected]>
> >> ---
> >> Changes in v2:
> >> - Use unsigned int for num_fmts and loop variable in find_datafmt
> >> - Remove superfluous const qualifier from find_datafmt
> >> ---
> >> drivers/media/i2c/mt9p031.c | 50 +++++++++++++++++++++++++++++--------
> >> 1 file changed, 40 insertions(+), 10 deletions(-)
> >>
> >> diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
> >> index dc23b9ed510a..2e6671ef877c 100644
> >> --- a/drivers/media/i2c/mt9p031.c
> >> +++ b/drivers/media/i2c/mt9p031.c
> >> @@ -116,6 +116,18 @@ enum mt9p031_model {
> >> MT9P031_MODEL_MONOCHROME,
> >> };
> >>
> >> +static const u32 mt9p031_color_fmts[] = {
> >> + MEDIA_BUS_FMT_SGRBG8_1X8,
> >> + MEDIA_BUS_FMT_SGRBG10_1X10,
> >> + MEDIA_BUS_FMT_SGRBG12_1X12,
> >> +};
> >> +
> >> +static const u32 mt9p031_monochrome_fmts[] = {
> >> + MEDIA_BUS_FMT_Y8_1X8,
> >> + MEDIA_BUS_FMT_Y10_1X10,
> >> + MEDIA_BUS_FMT_Y12_1X12,
> >> +};
> >> +
> >> struct mt9p031 {
> >> struct v4l2_subdev subdev;
> >> struct media_pad pad;
> >> @@ -138,6 +150,9 @@ struct mt9p031 {
> >> struct v4l2_ctrl *blc_auto;
> >> struct v4l2_ctrl *blc_offset;
> >>
> >> + const u32 *fmts;
> >> + unsigned int num_fmts;
> >> +
> >> /* Registers cache */
> >> u16 output_control;
> >> u16 mode2;
> >> @@ -148,6 +163,17 @@ static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd)
> >> return container_of(sd, struct mt9p031, subdev);
> >> }
> >>
> >> +static u32 mt9p031_find_datafmt(struct mt9p031 *mt9p031, u32 code)
> >> +{
> >> + unsigned int i;
> >> +
> >> + for (i = 0; i < mt9p031->num_fmts; i++)
> >> + if (mt9p031->fmts[i] == code)
> >> + return mt9p031->fmts[i];
> >> +
> >> + return mt9p031->fmts[mt9p031->num_fmts-1];
> >> +}
> >> +
> >> static int mt9p031_read(struct i2c_client *client, u8 reg)
> >> {
> >> return i2c_smbus_read_word_swapped(client, reg);
> >> @@ -476,10 +502,11 @@ static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev,
> >> {
> >> struct mt9p031 *mt9p031 = to_mt9p031(subdev);
> >>
> >> - if (code->pad || code->index)
> >> + if (code->pad || code->index >= mt9p031->num_fmts)
> >> return -EINVAL;
> >>
> >> - code->code = mt9p031->format.code;
> >> + code->code = mt9p031->fmts[code->index];
> >> +
> >> return 0;
> >> }
> >>
> >> @@ -573,6 +600,8 @@ static int mt9p031_set_format(struct v4l2_subdev *subdev,
> >> __format->width = __crop->width / hratio;
> >> __format->height = __crop->height / vratio;
> >>
> >> + __format->code = mt9p031_find_datafmt(mt9p031, format->format.code);
> >> +
> >> format->format = *__format;
> >>
> >> return 0;
> >> @@ -951,10 +980,7 @@ static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
> >>
> >> format = v4l2_subdev_get_try_format(subdev, fh->pad, 0);
> >>
> >> - if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
> >> - format->code = MEDIA_BUS_FMT_Y12_1X12;
> >> - else
> >> - format->code = MEDIA_BUS_FMT_SGRBG12_1X12;
> >> + format->code = mt9p031_find_datafmt(mt9p031, 0);
> >>
> >> format->width = MT9P031_WINDOW_WIDTH_DEF;
> >> format->height = MT9P031_WINDOW_HEIGHT_DEF;
> >> @@ -1121,10 +1147,14 @@ static int mt9p031_probe(struct i2c_client *client,
> >> mt9p031->crop.left = MT9P031_COLUMN_START_DEF;
> >> mt9p031->crop.top = MT9P031_ROW_START_DEF;
> >>
> >> - if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
> >> - mt9p031->format.code = MEDIA_BUS_FMT_Y12_1X12;
> >> - else
> >> - mt9p031->format.code = MEDIA_BUS_FMT_SGRBG12_1X12;
> >> + if (mt9p031->model == MT9P031_MODEL_MONOCHROME) {
> >> + mt9p031->fmts = mt9p031_monochrome_fmts;
> >> + mt9p031->num_fmts = ARRAY_SIZE(mt9p031_monochrome_fmts);
> >> + } else {
> >> + mt9p031->fmts = mt9p031_color_fmts;
> >> + mt9p031->num_fmts = ARRAY_SIZE(mt9p031_color_fmts);
> >> + }
> >> + mt9p031->format.code = mt9p031_find_datafmt(mt9p031, 0);
> >>
> >> mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF;
> >> mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF;

--
Regards,

Laurent Pinchart

2020-10-02 00:01:04

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v2 2/5] media: mt9p031: Read back the real clock rate

Hi Stefan,

Thank you for the patch.

On Wed, Sep 30, 2020 at 12:51:30PM +0200, Stefan Riedmueller wrote:
> From: Enrico Scholz <[email protected]>
>
> The real and requested clock can differ and because it is used to
> calculate PLL values, the real clock rate should be read.
>
> Signed-off-by: Enrico Scholz <[email protected]>
> Signed-off-by: Stefan Riedmueller <[email protected]>

Reviewed-by: Laurent Pinchart <[email protected]>

> ---
> No changes in v2
> ---
> drivers/media/i2c/mt9p031.c | 9 ++++++---
> 1 file changed, 6 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
> index 2e6671ef877c..b4c042f418c1 100644
> --- a/drivers/media/i2c/mt9p031.c
> +++ b/drivers/media/i2c/mt9p031.c
> @@ -255,6 +255,7 @@ static int mt9p031_clk_setup(struct mt9p031 *mt9p031)
>
> struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
> struct mt9p031_platform_data *pdata = mt9p031->pdata;
> + unsigned long ext_freq;
> int ret;
>
> mt9p031->clk = devm_clk_get(&client->dev, NULL);
> @@ -265,13 +266,15 @@ static int mt9p031_clk_setup(struct mt9p031 *mt9p031)
> if (ret < 0)
> return ret;
>
> + ext_freq = clk_get_rate(mt9p031->clk);
> +
> /* If the external clock frequency is out of bounds for the PLL use the
> * pixel clock divider only and disable the PLL.
> */
> - if (pdata->ext_freq > limits.ext_clock_max) {
> + if (ext_freq > limits.ext_clock_max) {
> unsigned int div;
>
> - div = DIV_ROUND_UP(pdata->ext_freq, pdata->target_freq);
> + div = DIV_ROUND_UP(ext_freq, pdata->target_freq);
> div = roundup_pow_of_two(div) / 2;
>
> mt9p031->clk_div = min_t(unsigned int, div, 64);
> @@ -280,7 +283,7 @@ static int mt9p031_clk_setup(struct mt9p031 *mt9p031)
> return 0;
> }
>
> - mt9p031->pll.ext_clock = pdata->ext_freq;
> + mt9p031->pll.ext_clock = ext_freq;
> mt9p031->pll.pix_clock = pdata->target_freq;
> mt9p031->use_pll = true;
>

--
Regards,

Laurent Pinchart

2020-10-02 00:08:27

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v2 5/5] media: mt9p031: Fix corrupted frame after restarting stream

Hi Stefan,

Thank you for the patch.

On Wed, Sep 30, 2020 at 12:51:33PM +0200, Stefan Riedmueller wrote:
> From: Dirk Bender <[email protected]>
>
> To prevent corrupted frames after starting and stopping the sensor it's

s/it's/its/

> datasheet specifies a specific pause sequence to follow:
>
> Stopping:
> Set Pause_Restart Bit -> Set Restart Bit -> Set Chip_Enable Off
>
> Restarting:
> Set Chip_Enable On -> Clear Pause_Restart Bit
>
> The Restart Bit is cleared automatically and must not be cleared
> manually as this would cause undefined behavior.
>
> Signed-off-by: Dirk Bender <[email protected]>
> Signed-off-by: Stefan Riedmueller <[email protected]>
> ---
> No changes in v2
> ---
> drivers/media/i2c/mt9p031.c | 25 +++++++++++++++++++++++++
> 1 file changed, 25 insertions(+)
>
> diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
> index d10457361e6c..d59f66e3dcf3 100644
> --- a/drivers/media/i2c/mt9p031.c
> +++ b/drivers/media/i2c/mt9p031.c
> @@ -80,6 +80,8 @@
> #define MT9P031_PIXEL_CLOCK_SHIFT(n) ((n) << 8)
> #define MT9P031_PIXEL_CLOCK_DIVIDE(n) ((n) << 0)
> #define MT9P031_FRAME_RESTART 0x0b
> +#define MT9P031_FRAME_RESTART_SET (1 << 0)
> +#define MT9P031_FRAME_PAUSE_RESTART_SET (1 << 1)

The fields are named Restart and Pause_Restart, I would drop _SET. Could
you also sort them from MSB to LSB as for the other registers ? Using
BIT() would be good too, although this could be done as an additional
patch to convert all the existing macros.

> #define MT9P031_SHUTTER_DELAY 0x0c
> #define MT9P031_RST 0x0d
> #define MT9P031_RST_ENABLE 1
> @@ -483,9 +485,25 @@ static int mt9p031_set_params(struct mt9p031 *mt9p031)
> static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)
> {
> struct mt9p031 *mt9p031 = to_mt9p031(subdev);
> + struct i2c_client *client = v4l2_get_subdevdata(subdev);
> + int val;
> int ret;
>
> if (!enable) {
> + val = mt9p031_read(client, MT9P031_FRAME_RESTART);

Do you need to read the register ? Can't you write
MT9P031_FRAME_PAUSE_RESTART_SET and then MT9P031_FRAME_PAUSE_RESTART_SET
| MT9P031_FRAME_RESTART_SET ? And actually, can't we just write both
bits in one go, do we need two writes ?

> +
> + /* enable pause restart */
> + val |= MT9P031_FRAME_PAUSE_RESTART_SET;
> + ret = mt9p031_write(client, MT9P031_FRAME_RESTART, val);
> + if (ret < 0)
> + return ret;
> +
> + /* enable restart + keep pause restart set */
> + val |= MT9P031_FRAME_RESTART_SET;
> + ret = mt9p031_write(client, MT9P031_FRAME_RESTART, val);
> + if (ret < 0)
> + return ret;
> +
> /* Stop sensor readout */
> ret = mt9p031_set_output_control(mt9p031,
> MT9P031_OUTPUT_CONTROL_CEN, 0);
> @@ -505,6 +523,13 @@ static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)
> if (ret < 0)
> return ret;
>
> + val = mt9p031_read(client, MT9P031_FRAME_RESTART);
> + /* disable reset + pause restart */
> + val &= ~MT9P031_FRAME_PAUSE_RESTART_SET;

Same here, I think you can simply write MT9P031_FRAME_PAUSE_RESTART_SET.

> + ret = mt9p031_write(client, MT9P031_FRAME_RESTART, val);
> + if (ret < 0)
> + return ret;
> +
> return mt9p031_pll_enable(mt9p031);
> }
>

--
Regards,

Laurent Pinchart

2020-10-02 00:09:04

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v2 3/5] media: mt9p031: Implement [gs]_register debug calls

Hi Stefan,

On Thu, Oct 01, 2020 at 10:56:24AM +0200, Stefan Riedmüller wrote:
> On 30.09.20 13:38, Laurent Pinchart wrote:
> > On Wed, Sep 30, 2020 at 12:51:31PM +0200, Stefan Riedmueller wrote:
> >> From: Enrico Scholz <[email protected]>
> >>
> >> Implement g_register and s_register v4l2_subdev_core_ops to access
> >> camera register directly from userspace for debug purposes.
> >
> > As the name of the operations imply, this is meant for debug purpose
> > only. They are however prone to be abused to configure the sensor from
> > userspace in production, which isn't a direction we want to take.
> > What's your use case for this ? I'd rather drop this patch and see the
> > driver extended with support for more controls if needed
>
> thanks for your feedback.
>
> I get your point. I myself solely use these operations for debugging
> purposes but I'm aware that others like to abuse them.
>
> I thought I send it anyway since for me the DEBUG config is enough to
> signalize that these operations are not to be used with a productive system.
> But I'm OK with dropping this patch if you think it might send the wrong signal.

I'd rather avoid this patch due to the risk of abuse if it's OK with
you.

> >> Signed-off-by: Enrico Scholz <[email protected]>
> >> Signed-off-by: Stefan Riedmueller <[email protected]>
> >> ---
> >> No changes in v2
> >> ---
> >> drivers/media/i2c/mt9p031.c | 28 ++++++++++++++++++++++++++++
> >> 1 file changed, 28 insertions(+)
> >>
> >> diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
> >> index b4c042f418c1..de36025260a8 100644
> >> --- a/drivers/media/i2c/mt9p031.c
> >> +++ b/drivers/media/i2c/mt9p031.c
> >> @@ -703,6 +703,30 @@ static int mt9p031_restore_blc(struct mt9p031 *mt9p031)
> >> return 0;
> >> }
> >>
> >> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> >> +static int mt9p031_g_register(struct v4l2_subdev *sd,
> >> + struct v4l2_dbg_register *reg)
> >> +{
> >> + struct i2c_client *client = v4l2_get_subdevdata(sd);
> >> + int ret;
> >> +
> >> + ret = mt9p031_read(client, reg->reg);
> >> + if (ret < 0)
> >> + return ret;
> >> +
> >> + reg->val = ret;
> >> + return 0;
> >> +}
> >> +
> >> +static int mt9p031_s_register(struct v4l2_subdev *sd,
> >> + struct v4l2_dbg_register const *reg)
> >> +{
> >> + struct i2c_client *client = v4l2_get_subdevdata(sd);
> >> +
> >> + return mt9p031_write(client, reg->reg, reg->val);
> >> +}
> >> +#endif
> >> +
> >> static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
> >> {
> >> struct mt9p031 *mt9p031 =
> >> @@ -1000,6 +1024,10 @@ static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
> >>
> >> static const struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = {
> >> .s_power = mt9p031_set_power,
> >> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> >> + .s_register = mt9p031_s_register,
> >> + .g_register = mt9p031_g_register,
> >> +#endif
> >> };
> >>
> >> static const struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = {

--
Regards,

Laurent Pinchart

2020-10-05 09:32:23

by Stefan Riedmüller

[permalink] [raw]
Subject: Re: [PATCH v2 5/5] media: mt9p031: Fix corrupted frame after restarting stream

Hi Laurent,

On 02.10.20 02:05, Laurent Pinchart wrote:
> Hi Stefan,
>
> Thank you for the patch.
>
> On Wed, Sep 30, 2020 at 12:51:33PM +0200, Stefan Riedmueller wrote:
>> From: Dirk Bender <[email protected]>
>>
>> To prevent corrupted frames after starting and stopping the sensor it's
>
> s/it's/its/

thanks, I'll fix that.

>
>> datasheet specifies a specific pause sequence to follow:
>>
>> Stopping:
>> Set Pause_Restart Bit -> Set Restart Bit -> Set Chip_Enable Off
>>
>> Restarting:
>> Set Chip_Enable On -> Clear Pause_Restart Bit
>>
>> The Restart Bit is cleared automatically and must not be cleared
>> manually as this would cause undefined behavior.
>>
>> Signed-off-by: Dirk Bender <[email protected]>
>> Signed-off-by: Stefan Riedmueller <[email protected]>
>> ---
>> No changes in v2
>> ---
>> drivers/media/i2c/mt9p031.c | 25 +++++++++++++++++++++++++
>> 1 file changed, 25 insertions(+)
>>
>> diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
>> index d10457361e6c..d59f66e3dcf3 100644
>> --- a/drivers/media/i2c/mt9p031.c
>> +++ b/drivers/media/i2c/mt9p031.c
>> @@ -80,6 +80,8 @@
>> #define MT9P031_PIXEL_CLOCK_SHIFT(n) ((n) << 8)
>> #define MT9P031_PIXEL_CLOCK_DIVIDE(n) ((n) << 0)
>> #define MT9P031_FRAME_RESTART 0x0b
>> +#define MT9P031_FRAME_RESTART_SET (1 << 0)
>> +#define MT9P031_FRAME_PAUSE_RESTART_SET (1 << 1)
>
> The fields are named Restart and Pause_Restart, I would drop _SET. Could
> you also sort them from MSB to LSB as for the other registers ? Using
> BIT() would be good too, although this could be done as an additional
> patch to convert all the existing macros.

I'll do that. Also I will rename the register to MT9P031_RESTART and the
bits to MT9P031_FRAME_RESTART and MT9P031_FRAME_PAUSE_RESTART.

>
>> #define MT9P031_SHUTTER_DELAY 0x0c
>> #define MT9P031_RST 0x0d
>> #define MT9P031_RST_ENABLE 1
>> @@ -483,9 +485,25 @@ static int mt9p031_set_params(struct mt9p031 *mt9p031)
>> static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)
>> {
>> struct mt9p031 *mt9p031 = to_mt9p031(subdev);
>> + struct i2c_client *client = v4l2_get_subdevdata(subdev);
>> + int val;
>> int ret;
>>
>> if (!enable) {
>> + val = mt9p031_read(client, MT9P031_FRAME_RESTART);
>
> Do you need to read the register ? Can't you write
> MT9P031_FRAME_PAUSE_RESTART_SET and then MT9P031_FRAME_PAUSE_RESTART_SET
> | MT9P031_FRAME_RESTART_SET ? And actually, can't we just write both
> bits in one go, do we need two writes ?

I think you're right we don't necessarily need to read the registers. The
only other bit is not used by the driver.

But I think we do need two separate writes, at least that is what the
datasheet states.

So I would drop the read but keep both write, ok?

>
>> +
>> + /* enable pause restart */
>> + val |= MT9P031_FRAME_PAUSE_RESTART_SET;
>> + ret = mt9p031_write(client, MT9P031_FRAME_RESTART, val);
>> + if (ret < 0)
>> + return ret;
>> +
>> + /* enable restart + keep pause restart set */
>> + val |= MT9P031_FRAME_RESTART_SET;
>> + ret = mt9p031_write(client, MT9P031_FRAME_RESTART, val);
>> + if (ret < 0)
>> + return ret;
>> +
>> /* Stop sensor readout */
>> ret = mt9p031_set_output_control(mt9p031,
>> MT9P031_OUTPUT_CONTROL_CEN, 0);
>> @@ -505,6 +523,13 @@ static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)
>> if (ret < 0)
>> return ret;
>>
>> + val = mt9p031_read(client, MT9P031_FRAME_RESTART);
>> + /* disable reset + pause restart */
>> + val &= ~MT9P031_FRAME_PAUSE_RESTART_SET;
>
> Same here, I think you can simply write MT9P031_FRAME_PAUSE_RESTART_SET.

I'll drop the read here as well. But I need to make sure, that the Restart
Bit is not cleared manually here.

Regards,
Stefan

>
>> + ret = mt9p031_write(client, MT9P031_FRAME_RESTART, val);
>> + if (ret < 0)
>> + return ret;
>> +
>> return mt9p031_pll_enable(mt9p031);
>> }
>>
>

2020-10-05 09:33:35

by Stefan Riedmüller

[permalink] [raw]
Subject: Re: [PATCH v2 1/5] media: mt9p031: Add support for 8 bit and 10 bit formats

Hi Laurent,

On 02.10.20 01:53, Laurent Pinchart wrote:
> Hi Stefan,
>
> On Thu, Oct 01, 2020 at 11:07:00AM +0200, Stefan Riedmüller wrote:
>> On 30.09.20 13:42, Laurent Pinchart wrote:
>>> On Wed, Sep 30, 2020 at 12:51:29PM +0200, Stefan Riedmueller wrote:
>>>> From: Christian Hemp <[email protected]>
>>>>
>>>> Aside from 12 bit monochrome or color format the sensor implicitly
>>>> supports 10 and 8 bit formats as well by simply dropping the
>>>> corresponding LSBs.
>>>
>>> That's not how it should work though. If you set the format on
>>> MEDIA_BUS_FMT_SGRBG8_1X8 through the pipeline for instance, you will end
>>> up capturing the 8 LSB, not the 8 MSB.
>>>
>>> What's your use case for this ?
>>
>> I use this sensor in combination with an i.MX 6 and i.MX 6UL. When the
>> sensor is connected with 12 bit (or 10 bit on the i.MX 6UL) and I set
>> MEDIA_BUS_FMT_SGRBG8_1X8 through the pipeline the CSI interface drops the
>> unused 4 LSB (or 2 LSB on the i.MX 6UL) so I get the 8 MSB from my 12 bit
>> sensor.
>
> Is that the PIXEL_BIT bit in CSI_CSICR1 for the i.MX6UL ? If so I think
> this should be handled in the imx7-media-csi driver. You could set the
> format to MEDIA_BUS_FMT_SGRBG10_1X10 on the sink pad of the CSI and to
> MEDIA_BUS_FMT_SGRBG8_1X8 on the source pad to configure this. I don't
> think the sensor driver should be involved, otherwise we'd have to patch
> all sensor drivers. From a sensor point of view, it outputs 12-bit
> Bayer, not 8-bit.

Ah, I had it wrong. What you say makes total sense. I will take another look
at it and also try to work your suggestion from below in.

Thanks,
Stefan

>
> Now there's a caveat. When used with the i.MX6UL, I assume you connected
> D[11:2] of the sensor to D[9:0] of the i.MX6UL, right ? The i.MX6UL
> doesn't support 12-bit inputs, so it should accept
> MEDIA_BUS_FMT_SGRBG12_1X12 on its sink pad. In this case, as D[1:0] of
> the sensor are left unconnected, I think you should set data-shift to 2
> and bus-width to 10 in DT on the sensor side. The MT9P031 driver should
> parse that, and output MEDIA_BUS_FMT_SGRBG10_1X10 instead of
> MEDIA_BUS_FMT_SGRBG12_1X12 in that case.
>
>> Does this clarify things? Maybe the description in the commit message is not
>> accurate enough or did I get something wrong?
>>
>>>> Signed-off-by: Christian Hemp <[email protected]>
>>>> [[email protected]: simplified by dropping v4l2_colorspace handling]
>>>> Signed-off-by: Jan Luebbe <[email protected]>
>>>> Signed-off-by: Stefan Riedmueller <[email protected]>
>>>> ---
>>>> Changes in v2:
>>>> - Use unsigned int for num_fmts and loop variable in find_datafmt
>>>> - Remove superfluous const qualifier from find_datafmt
>>>> ---
>>>> drivers/media/i2c/mt9p031.c | 50 +++++++++++++++++++++++++++++--------
>>>> 1 file changed, 40 insertions(+), 10 deletions(-)
>>>>
>>>> diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
>>>> index dc23b9ed510a..2e6671ef877c 100644
>>>> --- a/drivers/media/i2c/mt9p031.c
>>>> +++ b/drivers/media/i2c/mt9p031.c
>>>> @@ -116,6 +116,18 @@ enum mt9p031_model {
>>>> MT9P031_MODEL_MONOCHROME,
>>>> };
>>>>
>>>> +static const u32 mt9p031_color_fmts[] = {
>>>> + MEDIA_BUS_FMT_SGRBG8_1X8,
>>>> + MEDIA_BUS_FMT_SGRBG10_1X10,
>>>> + MEDIA_BUS_FMT_SGRBG12_1X12,
>>>> +};
>>>> +
>>>> +static const u32 mt9p031_monochrome_fmts[] = {
>>>> + MEDIA_BUS_FMT_Y8_1X8,
>>>> + MEDIA_BUS_FMT_Y10_1X10,
>>>> + MEDIA_BUS_FMT_Y12_1X12,
>>>> +};
>>>> +
>>>> struct mt9p031 {
>>>> struct v4l2_subdev subdev;
>>>> struct media_pad pad;
>>>> @@ -138,6 +150,9 @@ struct mt9p031 {
>>>> struct v4l2_ctrl *blc_auto;
>>>> struct v4l2_ctrl *blc_offset;
>>>>
>>>> + const u32 *fmts;
>>>> + unsigned int num_fmts;
>>>> +
>>>> /* Registers cache */
>>>> u16 output_control;
>>>> u16 mode2;
>>>> @@ -148,6 +163,17 @@ static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd)
>>>> return container_of(sd, struct mt9p031, subdev);
>>>> }
>>>>
>>>> +static u32 mt9p031_find_datafmt(struct mt9p031 *mt9p031, u32 code)
>>>> +{
>>>> + unsigned int i;
>>>> +
>>>> + for (i = 0; i < mt9p031->num_fmts; i++)
>>>> + if (mt9p031->fmts[i] == code)
>>>> + return mt9p031->fmts[i];
>>>> +
>>>> + return mt9p031->fmts[mt9p031->num_fmts-1];
>>>> +}
>>>> +
>>>> static int mt9p031_read(struct i2c_client *client, u8 reg)
>>>> {
>>>> return i2c_smbus_read_word_swapped(client, reg);
>>>> @@ -476,10 +502,11 @@ static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev,
>>>> {
>>>> struct mt9p031 *mt9p031 = to_mt9p031(subdev);
>>>>
>>>> - if (code->pad || code->index)
>>>> + if (code->pad || code->index >= mt9p031->num_fmts)
>>>> return -EINVAL;
>>>>
>>>> - code->code = mt9p031->format.code;
>>>> + code->code = mt9p031->fmts[code->index];
>>>> +
>>>> return 0;
>>>> }
>>>>
>>>> @@ -573,6 +600,8 @@ static int mt9p031_set_format(struct v4l2_subdev *subdev,
>>>> __format->width = __crop->width / hratio;
>>>> __format->height = __crop->height / vratio;
>>>>
>>>> + __format->code = mt9p031_find_datafmt(mt9p031, format->format.code);
>>>> +
>>>> format->format = *__format;
>>>>
>>>> return 0;
>>>> @@ -951,10 +980,7 @@ static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
>>>>
>>>> format = v4l2_subdev_get_try_format(subdev, fh->pad, 0);
>>>>
>>>> - if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
>>>> - format->code = MEDIA_BUS_FMT_Y12_1X12;
>>>> - else
>>>> - format->code = MEDIA_BUS_FMT_SGRBG12_1X12;
>>>> + format->code = mt9p031_find_datafmt(mt9p031, 0);
>>>>
>>>> format->width = MT9P031_WINDOW_WIDTH_DEF;
>>>> format->height = MT9P031_WINDOW_HEIGHT_DEF;
>>>> @@ -1121,10 +1147,14 @@ static int mt9p031_probe(struct i2c_client *client,
>>>> mt9p031->crop.left = MT9P031_COLUMN_START_DEF;
>>>> mt9p031->crop.top = MT9P031_ROW_START_DEF;
>>>>
>>>> - if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
>>>> - mt9p031->format.code = MEDIA_BUS_FMT_Y12_1X12;
>>>> - else
>>>> - mt9p031->format.code = MEDIA_BUS_FMT_SGRBG12_1X12;
>>>> + if (mt9p031->model == MT9P031_MODEL_MONOCHROME) {
>>>> + mt9p031->fmts = mt9p031_monochrome_fmts;
>>>> + mt9p031->num_fmts = ARRAY_SIZE(mt9p031_monochrome_fmts);
>>>> + } else {
>>>> + mt9p031->fmts = mt9p031_color_fmts;
>>>> + mt9p031->num_fmts = ARRAY_SIZE(mt9p031_color_fmts);
>>>> + }
>>>> + mt9p031->format.code = mt9p031_find_datafmt(mt9p031, 0);
>>>>
>>>> mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF;
>>>> mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF;
>

2020-10-05 09:35:02

by Stefan Riedmüller

[permalink] [raw]
Subject: Re: [PATCH v2 3/5] media: mt9p031: Implement [gs]_register debug calls

Hi Laurent,

On 02.10.20 02:06, Laurent Pinchart wrote:
> Hi Stefan,
>
> On Thu, Oct 01, 2020 at 10:56:24AM +0200, Stefan Riedmüller wrote:
>> On 30.09.20 13:38, Laurent Pinchart wrote:
>>> On Wed, Sep 30, 2020 at 12:51:31PM +0200, Stefan Riedmueller wrote:
>>>> From: Enrico Scholz <[email protected]>
>>>>
>>>> Implement g_register and s_register v4l2_subdev_core_ops to access
>>>> camera register directly from userspace for debug purposes.
>>>
>>> As the name of the operations imply, this is meant for debug purpose
>>> only. They are however prone to be abused to configure the sensor from
>>> userspace in production, which isn't a direction we want to take.
>>> What's your use case for this ? I'd rather drop this patch and see the
>>> driver extended with support for more controls if needed
>>
>> thanks for your feedback.
>>
>> I get your point. I myself solely use these operations for debugging
>> purposes but I'm aware that others like to abuse them.
>>
>> I thought I send it anyway since for me the DEBUG config is enough to
>> signalize that these operations are not to be used with a productive system.
>> But I'm OK with dropping this patch if you think it might send the wrong signal.
>
> I'd rather avoid this patch due to the risk of abuse if it's OK with
> you.

Yes, that's fine. I will drop it in v3.

Regards,
Stefan

>
>>>> Signed-off-by: Enrico Scholz <[email protected]>
>>>> Signed-off-by: Stefan Riedmueller <[email protected]>
>>>> ---
>>>> No changes in v2
>>>> ---
>>>> drivers/media/i2c/mt9p031.c | 28 ++++++++++++++++++++++++++++
>>>> 1 file changed, 28 insertions(+)
>>>>
>>>> diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
>>>> index b4c042f418c1..de36025260a8 100644
>>>> --- a/drivers/media/i2c/mt9p031.c
>>>> +++ b/drivers/media/i2c/mt9p031.c
>>>> @@ -703,6 +703,30 @@ static int mt9p031_restore_blc(struct mt9p031 *mt9p031)
>>>> return 0;
>>>> }
>>>>
>>>> +#ifdef CONFIG_VIDEO_ADV_DEBUG
>>>> +static int mt9p031_g_register(struct v4l2_subdev *sd,
>>>> + struct v4l2_dbg_register *reg)
>>>> +{
>>>> + struct i2c_client *client = v4l2_get_subdevdata(sd);
>>>> + int ret;
>>>> +
>>>> + ret = mt9p031_read(client, reg->reg);
>>>> + if (ret < 0)
>>>> + return ret;
>>>> +
>>>> + reg->val = ret;
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static int mt9p031_s_register(struct v4l2_subdev *sd,
>>>> + struct v4l2_dbg_register const *reg)
>>>> +{
>>>> + struct i2c_client *client = v4l2_get_subdevdata(sd);
>>>> +
>>>> + return mt9p031_write(client, reg->reg, reg->val);
>>>> +}
>>>> +#endif
>>>> +
>>>> static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
>>>> {
>>>> struct mt9p031 *mt9p031 =
>>>> @@ -1000,6 +1024,10 @@ static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
>>>>
>>>> static const struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = {
>>>> .s_power = mt9p031_set_power,
>>>> +#ifdef CONFIG_VIDEO_ADV_DEBUG
>>>> + .s_register = mt9p031_s_register,
>>>> + .g_register = mt9p031_g_register,
>>>> +#endif
>>>> };
>>>>
>>>> static const struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = {
>

2020-10-05 13:12:49

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v2 5/5] media: mt9p031: Fix corrupted frame after restarting stream

Hi Stefan,

On Mon, Oct 05, 2020 at 11:28:21AM +0200, Stefan Riedmüller wrote:
> On 02.10.20 02:05, Laurent Pinchart wrote:
> > On Wed, Sep 30, 2020 at 12:51:33PM +0200, Stefan Riedmueller wrote:
> >> From: Dirk Bender <[email protected]>
> >>
> >> To prevent corrupted frames after starting and stopping the sensor it's
> >
> > s/it's/its/
>
> thanks, I'll fix that.
>
> >> datasheet specifies a specific pause sequence to follow:
> >>
> >> Stopping:
> >> Set Pause_Restart Bit -> Set Restart Bit -> Set Chip_Enable Off
> >>
> >> Restarting:
> >> Set Chip_Enable On -> Clear Pause_Restart Bit
> >>
> >> The Restart Bit is cleared automatically and must not be cleared
> >> manually as this would cause undefined behavior.
> >>
> >> Signed-off-by: Dirk Bender <[email protected]>
> >> Signed-off-by: Stefan Riedmueller <[email protected]>
> >> ---
> >> No changes in v2
> >> ---
> >> drivers/media/i2c/mt9p031.c | 25 +++++++++++++++++++++++++
> >> 1 file changed, 25 insertions(+)
> >>
> >> diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
> >> index d10457361e6c..d59f66e3dcf3 100644
> >> --- a/drivers/media/i2c/mt9p031.c
> >> +++ b/drivers/media/i2c/mt9p031.c
> >> @@ -80,6 +80,8 @@
> >> #define MT9P031_PIXEL_CLOCK_SHIFT(n) ((n) << 8)
> >> #define MT9P031_PIXEL_CLOCK_DIVIDE(n) ((n) << 0)
> >> #define MT9P031_FRAME_RESTART 0x0b
> >> +#define MT9P031_FRAME_RESTART_SET (1 << 0)
> >> +#define MT9P031_FRAME_PAUSE_RESTART_SET (1 << 1)
> >
> > The fields are named Restart and Pause_Restart, I would drop _SET. Could
> > you also sort them from MSB to LSB as for the other registers ? Using
> > BIT() would be good too, although this could be done as an additional
> > patch to convert all the existing macros.
>
> I'll do that. Also I will rename the register to MT9P031_RESTART and the
> bits to MT9P031_FRAME_RESTART and MT9P031_FRAME_PAUSE_RESTART.
>
> >> #define MT9P031_SHUTTER_DELAY 0x0c
> >> #define MT9P031_RST 0x0d
> >> #define MT9P031_RST_ENABLE 1
> >> @@ -483,9 +485,25 @@ static int mt9p031_set_params(struct mt9p031 *mt9p031)
> >> static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)
> >> {
> >> struct mt9p031 *mt9p031 = to_mt9p031(subdev);
> >> + struct i2c_client *client = v4l2_get_subdevdata(subdev);
> >> + int val;
> >> int ret;
> >>
> >> if (!enable) {
> >> + val = mt9p031_read(client, MT9P031_FRAME_RESTART);
> >
> > Do you need to read the register ? Can't you write
> > MT9P031_FRAME_PAUSE_RESTART_SET and then MT9P031_FRAME_PAUSE_RESTART_SET
> > | MT9P031_FRAME_RESTART_SET ? And actually, can't we just write both
> > bits in one go, do we need two writes ?
>
> I think you're right we don't necessarily need to read the registers. The
> only other bit is not used by the driver.
>
> But I think we do need two separate writes, at least that is what the
> datasheet states.
>
> So I would drop the read but keep both write, ok?

That's fine with me if required, although I don't see where this is
indicated in the datasheet, but I may have missed it.

> >> +
> >> + /* enable pause restart */
> >> + val |= MT9P031_FRAME_PAUSE_RESTART_SET;
> >> + ret = mt9p031_write(client, MT9P031_FRAME_RESTART, val);
> >> + if (ret < 0)
> >> + return ret;
> >> +
> >> + /* enable restart + keep pause restart set */
> >> + val |= MT9P031_FRAME_RESTART_SET;
> >> + ret = mt9p031_write(client, MT9P031_FRAME_RESTART, val);
> >> + if (ret < 0)
> >> + return ret;
> >> +
> >> /* Stop sensor readout */
> >> ret = mt9p031_set_output_control(mt9p031,
> >> MT9P031_OUTPUT_CONTROL_CEN, 0);
> >> @@ -505,6 +523,13 @@ static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)
> >> if (ret < 0)
> >> return ret;
> >>
> >> + val = mt9p031_read(client, MT9P031_FRAME_RESTART);
> >> + /* disable reset + pause restart */
> >> + val &= ~MT9P031_FRAME_PAUSE_RESTART_SET;
> >
> > Same here, I think you can simply write MT9P031_FRAME_PAUSE_RESTART_SET.
>
> I'll drop the read here as well. But I need to make sure, that the Restart
> Bit is not cleared manually here.
>
> >> + ret = mt9p031_write(client, MT9P031_FRAME_RESTART, val);
> >> + if (ret < 0)
> >> + return ret;
> >> +
> >> return mt9p031_pll_enable(mt9p031);
> >> }
> >>

--
Regards,

Laurent Pinchart

2020-10-14 13:02:53

by Stefan Riedmüller

[permalink] [raw]
Subject: Re: [PATCH v2 5/5] media: mt9p031: Fix corrupted frame after restarting stream

Hi Laurent,

On 05.10.20 15:08, Laurent Pinchart wrote:
> Hi Stefan,
>
> On Mon, Oct 05, 2020 at 11:28:21AM +0200, Stefan Riedmüller wrote:
>> On 02.10.20 02:05, Laurent Pinchart wrote:
>>> On Wed, Sep 30, 2020 at 12:51:33PM +0200, Stefan Riedmueller wrote:
>>>> From: Dirk Bender <[email protected]>
>>>>
>>>> To prevent corrupted frames after starting and stopping the sensor it's
>>>
>>> s/it's/its/
>>
>> thanks, I'll fix that.
>>
>>>> datasheet specifies a specific pause sequence to follow:
>>>>
>>>> Stopping:
>>>> Set Pause_Restart Bit -> Set Restart Bit -> Set Chip_Enable Off
>>>>
>>>> Restarting:
>>>> Set Chip_Enable On -> Clear Pause_Restart Bit
>>>>
>>>> The Restart Bit is cleared automatically and must not be cleared
>>>> manually as this would cause undefined behavior.
>>>>
>>>> Signed-off-by: Dirk Bender <[email protected]>
>>>> Signed-off-by: Stefan Riedmueller <[email protected]>
>>>> ---
>>>> No changes in v2
>>>> ---
>>>> drivers/media/i2c/mt9p031.c | 25 +++++++++++++++++++++++++
>>>> 1 file changed, 25 insertions(+)
>>>>
>>>> diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
>>>> index d10457361e6c..d59f66e3dcf3 100644
>>>> --- a/drivers/media/i2c/mt9p031.c
>>>> +++ b/drivers/media/i2c/mt9p031.c
>>>> @@ -80,6 +80,8 @@
>>>> #define MT9P031_PIXEL_CLOCK_SHIFT(n) ((n) << 8)
>>>> #define MT9P031_PIXEL_CLOCK_DIVIDE(n) ((n) << 0)
>>>> #define MT9P031_FRAME_RESTART 0x0b
>>>> +#define MT9P031_FRAME_RESTART_SET (1 << 0)
>>>> +#define MT9P031_FRAME_PAUSE_RESTART_SET (1 << 1)
>>>
>>> The fields are named Restart and Pause_Restart, I would drop _SET. Could
>>> you also sort them from MSB to LSB as for the other registers ? Using
>>> BIT() would be good too, although this could be done as an additional
>>> patch to convert all the existing macros.
>>
>> I'll do that. Also I will rename the register to MT9P031_RESTART and the
>> bits to MT9P031_FRAME_RESTART and MT9P031_FRAME_PAUSE_RESTART.
>>
>>>> #define MT9P031_SHUTTER_DELAY 0x0c
>>>> #define MT9P031_RST 0x0d
>>>> #define MT9P031_RST_ENABLE 1
>>>> @@ -483,9 +485,25 @@ static int mt9p031_set_params(struct mt9p031 *mt9p031)
>>>> static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)
>>>> {
>>>> struct mt9p031 *mt9p031 = to_mt9p031(subdev);
>>>> + struct i2c_client *client = v4l2_get_subdevdata(subdev);
>>>> + int val;
>>>> int ret;
>>>>
>>>> if (!enable) {
>>>> + val = mt9p031_read(client, MT9P031_FRAME_RESTART);
>>>
>>> Do you need to read the register ? Can't you write
>>> MT9P031_FRAME_PAUSE_RESTART_SET and then MT9P031_FRAME_PAUSE_RESTART_SET
>>> | MT9P031_FRAME_RESTART_SET ? And actually, can't we just write both
>>> bits in one go, do we need two writes ?
>>
>> I think you're right we don't necessarily need to read the registers. The
>> only other bit is not used by the driver.
>>
>> But I think we do need two separate writes, at least that is what the
>> datasheet states.
>>
>> So I would drop the read but keep both write, ok?
>
> That's fine with me if required, although I don't see where this is
> indicated in the datasheet, but I may have missed it.

It's in "Standby and Chip Enable". There is a Sequence for entering soft
standby with two separate writes:

REG = 0x0B, 0x0002
REG = 0x0B, 0x0003

Regards,
Stefan

>
>>>> +
>>>> + /* enable pause restart */
>>>> + val |= MT9P031_FRAME_PAUSE_RESTART_SET;
>>>> + ret = mt9p031_write(client, MT9P031_FRAME_RESTART, val);
>>>> + if (ret < 0)
>>>> + return ret;
>>>> +
>>>> + /* enable restart + keep pause restart set */
>>>> + val |= MT9P031_FRAME_RESTART_SET;
>>>> + ret = mt9p031_write(client, MT9P031_FRAME_RESTART, val);
>>>> + if (ret < 0)
>>>> + return ret;
>>>> +
>>>> /* Stop sensor readout */
>>>> ret = mt9p031_set_output_control(mt9p031,
>>>> MT9P031_OUTPUT_CONTROL_CEN, 0);
>>>> @@ -505,6 +523,13 @@ static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)
>>>> if (ret < 0)
>>>> return ret;
>>>>
>>>> + val = mt9p031_read(client, MT9P031_FRAME_RESTART);
>>>> + /* disable reset + pause restart */
>>>> + val &= ~MT9P031_FRAME_PAUSE_RESTART_SET;
>>>
>>> Same here, I think you can simply write MT9P031_FRAME_PAUSE_RESTART_SET.
>>
>> I'll drop the read here as well. But I need to make sure, that the Restart
>> Bit is not cleared manually here.
>>
>>>> + ret = mt9p031_write(client, MT9P031_FRAME_RESTART, val);
>>>> + if (ret < 0)
>>>> + return ret;
>>>> +
>>>> return mt9p031_pll_enable(mt9p031);
>>>> }
>>>>
>