Re: [PATCH 1/4] media: ov5640: fix resolution update

2018-10-16 Thread Hugues FRUCHET
Hi Jacopo,

On 10/15/2018 05:24 PM, jacopo mondi wrote:
> Hi Hugues,
> 
> On Mon, Oct 15, 2018 at 03:13:12PM +0000, Hugues FRUCHET wrote:
>> Hi Laurent, Jacopo, Sam,
>>
>> I'm also OK to change to a simpler alternative;
>> - drop the "restore" step
> 
> Do you mean the restore step at the end of 'ov5640_restore_mode()' ?

yes

> I agree, I've been carrying this one [1] in my tree for some time now.
> I just didn't send it because of the too many issues in flight on this
> driver.
> 
>> - send the whole init register sequence + mode changes + format changes
>> at streamon
>>
> 
> This might be a first step in my opinion too, yes.
> 
>> is this what you have in mind Laurent ?
> 
> I know Laurent does not fully agree with me on this, but I would like
> to have Maxime's series on clock tree handling merged and tested on
> CSI-2 first before adding more patches to the pile of pending items on
> ov5640. I hope to have time to test them on CSI-2 this week before
> ELC-E.
> 
> Steve, you're the driver maintainer do you have preferences here?
> 
> Also, if this might be useful, I would like to help co-maintaining the
> driver (with possibily other people, possibly with the sensor wired in
> DVP mode), and help establishing priorities, as this driver needs some
> love, but one item at the time to avoid getting lost in too many
> pending changes as it happened recently :)

It's a good problem having too many contributions ;) This means that 
there is some interest on it...
For DVP, me and Maxime have two different setup so we can cover the DVP 
tests.

> 
> Thanks
> j
> 
> [1]
>  media: ov5640: Do not restore format at power up
> 
>  Do not force restoring the last applied capture format during sensor 
> power up
>  as it will be re-set at s_stream time.
> 
>  Signed-off-by: Jacopo Mondi 
> 
> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> index b226946..17ee55b 100644
> --- a/drivers/media/i2c/ov5640.c
> +++ b/drivers/media/i2c/ov5640.c
> @@ -1737,12 +1737,10 @@ static int ov5640_restore_mode(struct ov5640_dev 
> *sensor)
>  if (ret)
>  return ret;
> 
> -   /* now restore the last capture mode */
> -   ret = ov5640_set_mode(sensor, _mode_init_data);
> -   if (ret < 0)
> -   return ret;
> +   sensor->pending_mode_change = true;
> +   sensor->pending_fmt_change = true;
> 
> -   return ov5640_set_framefmt(sensor, >fmt);
> +   return 0;
>   }
> 
>   static void ov5640_power(struct ov5640_dev *sensor, bool enable)
> 
>>
>> On 10/10/2018 02:41 PM, Laurent Pinchart wrote:
>>> Hi Jacopo,
>>>
>>> On Wednesday, 10 October 2018 13:58:04 EEST jacopo mondi wrote:
>>>> Hi Sam,
>>>>  thanks for the patch, I see the same issue you reported, but I
>>>> think this patch can be improved.
>>>>
>>>> (expanding the Cc list to all people involved in recent ov5640
>>>> developemts, not just for this patch, but for the whole series to look
>>>> at. Copying names from another series cover letter, hope it is
>>>> complete.)
>>>>
>>>> On Mon, Oct 08, 2018 at 11:47:59PM -0700, Sam Bobrowicz wrote:
>>>>> set_fmt was not properly triggering a mode change when
>>>>> a new mode was set that happened to have the same format
>>>>> as the previous mode (for example, when only changing the
>>>>> frame dimensions). Fix this.
>>>>>
>>>>> Signed-off-by: Sam Bobrowicz 
>>>>> ---
>>>>>
>>>>>drivers/media/i2c/ov5640.c | 8 
>>>>>1 file changed, 4 insertions(+), 4 deletions(-)
>>>>>
>>>>> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
>>>>> index eaefdb5..5031aab 100644
>>>>> --- a/drivers/media/i2c/ov5640.c
>>>>> +++ b/drivers/media/i2c/ov5640.c
>>>>> @@ -2045,12 +2045,12 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
>>>>>
>>>>>   goto out;
>>>>>
>>>>>   }
>>>>>
>>>>> - if (new_mode != sensor->current_mode) {
>>>>> +
>>>>> + if (new_mode != sensor->current_mode ||
>>>>> + mbus_fmt->code != sensor->fmt.code) {
>>>>> + sensor->fmt = *mbus_fmt;
>>>>>
>>>>>   sensor->current_mode = new_mode;
>>>>>  

Re: [PATCH v4 12/12] ov5640: Enforce a mode change when changing the framerate

2018-10-16 Thread Hugues FRUCHET
You're welcome ;)

On 10/16/2018 09:10 AM, Maxime Ripard wrote:
> Hi Hugues,
> 
> On Mon, Oct 15, 2018 at 01:57:40PM +0000, Hugues FRUCHET wrote:
>> This is already fixed in media tree:
>> 0929983e49c81c1d413702cd9b83bb06c4a2555c media: ov5640: fix framerate update
> 
> My bad then, I missed it, thanks!
> Maxime
> 

Re: [PATCH 1/4] media: ov5640: fix resolution update

2018-10-15 Thread Hugues FRUCHET
Hi Laurent, Jacopo, Sam,

I'm also OK to change to a simpler alternative;
- drop the "restore" step
- send the whole init register sequence + mode changes + format changes 
at streamon

is this what you have in mind Laurent ?

On 10/10/2018 02:41 PM, Laurent Pinchart wrote:
> Hi Jacopo,
> 
> On Wednesday, 10 October 2018 13:58:04 EEST jacopo mondi wrote:
>> Hi Sam,
>> thanks for the patch, I see the same issue you reported, but I
>> think this patch can be improved.
>>
>> (expanding the Cc list to all people involved in recent ov5640
>> developemts, not just for this patch, but for the whole series to look
>> at. Copying names from another series cover letter, hope it is
>> complete.)
>>
>> On Mon, Oct 08, 2018 at 11:47:59PM -0700, Sam Bobrowicz wrote:
>>> set_fmt was not properly triggering a mode change when
>>> a new mode was set that happened to have the same format
>>> as the previous mode (for example, when only changing the
>>> frame dimensions). Fix this.
>>>
>>> Signed-off-by: Sam Bobrowicz 
>>> ---
>>>
>>>   drivers/media/i2c/ov5640.c | 8 
>>>   1 file changed, 4 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
>>> index eaefdb5..5031aab 100644
>>> --- a/drivers/media/i2c/ov5640.c
>>> +++ b/drivers/media/i2c/ov5640.c
>>> @@ -2045,12 +2045,12 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
>>>
>>> goto out;
>>> 
>>> }
>>>
>>> -   if (new_mode != sensor->current_mode) {
>>> +
>>> +   if (new_mode != sensor->current_mode ||
>>> +   mbus_fmt->code != sensor->fmt.code) {
>>> +   sensor->fmt = *mbus_fmt;
>>>
>>> sensor->current_mode = new_mode;
>>> sensor->pending_mode_change = true;
>>>
>>> -   }
>>> -   if (mbus_fmt->code != sensor->fmt.code) {
>>> -   sensor->fmt = *mbus_fmt;
>>>
>>> sensor->pending_fmt_change = true;
>>> 
>>> }
>>
>> How I did reproduce the issue:
>>
>> # Set 1024x768 on ov5640 without changing the image format
>> # (default image size at startup is 640x480)
>> $ media-ctl --set-v4l2 "'ov5640 2-003c':0[fmt:UYVY2X8/1024x768 field:none]"
>>sensor->pending_mode_change = true; //verified this flag gets set
>>
>> # Start streaming, after having configured the whole pipeline to work
>> # with 1024x768
>> $  yavta -c10 -n4 -f UYVY -s 1024x768 /dev/video4
>> Unable to start streaming: Broken pipe (32).
>>
>> # Inspect which part of pipeline validation went wrong
>> # Turns out the sensor->fmt field is not updated, and when get_fmt()
>> # is called, the old one is returned.
>> $ media-ctl -e "ov5640 2-003c" -p
>>...
>>[fmt:UYVY8_2X8/640x480@1/30 field:none colorspace:srgb xfer:srgb ycbcr:601
>> quantization:full-range] ^^^ ^^^
>>
>> So yes, sensor->fmt is not udapted as it should be when only image
>> resolution is changed.
>>
>> Although I still see value in having two separate flags for the
>> 'mode_change' (which in ov5640 lingo is resolution) and 'fmt_change' (which
>> in ov5640 lingo is the image format), and write their configuration to
>> registers only when they get actually changed.
>>
>> For this reasons I would like to propse the following patch which I
>> have tested by:
>> 1) changing resolution only
>> 2) changing format only
>> 3) change both
>>
>> What do you and others think?
> 
> I think that the format setting code should be completely rewritten, it's
> pretty much unmaintainable as-is.
> 
>> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
>> index eaefdb5..e392b9d 100644
>> --- a/drivers/media/i2c/ov5640.c
>> +++ b/drivers/media/i2c/ov5640.c
>> @@ -2020,6 +2020,7 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
>>  struct ov5640_dev *sensor = to_ov5640_dev(sd);
>>  const struct ov5640_mode_info *new_mode;
>>  struct v4l2_mbus_framefmt *mbus_fmt = >format;
>> +   struct v4l2_mbus_framefmt *fmt;
>>  int ret;
>>
>>  if (format->pad != 0)
>> @@ -2037,22 +2038,19 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
>>  if (ret)
>>  goto out;
>>
>> -   if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
>> -   struct v4l2_mbus_framefmt *fmt =
>> -   v4l2_subdev_get_try_format(sd, cfg, 0);
>> +   if (format->which == V4L2_SUBDEV_FORMAT_TRY)
>> +   fmt = v4l2_subdev_get_try_format(sd, cfg, 0);
>> +   else
>> +   fmt = >fmt;
>>
>> -   *fmt = *mbus_fmt;
>> -   goto out;
>> -   }
>> +   *fmt = *mbus_fmt;
>>
>>  if (new_mode != sensor->current_mode) {
>>  sensor->current_mode = new_mode;
>>  sensor->pending_mode_change = true;
>>  }
>> -   if (mbus_fmt->code != sensor->fmt.code) {
>> -   sensor->fmt = *mbus_fmt;
>> +   if (mbus_fmt->code != sensor->fmt.code)
>>  sensor->pending_fmt_change = true;
>> -   }
>>   out:
>>  

Re: [PATCH v4 01/12] media: ov5640: Adjust the clock based on the expected rate

2018-10-15 Thread Hugues FRUCHET
Hi Maxime,

I've recently found a problem around JPEG framerate, see below:

On 10/11/2018 11:20 AM, Maxime Ripard wrote:
> The clock structure for the PCLK is quite obscure in the documentation, and
> was hardcoded through the bytes array of each and every mode.
> 
> This is troublesome, since we cannot adjust it at runtime based on other
> parameters (such as the number of bytes per pixel), and we can't support
> either framerates that have not been used by the various vendors, since we
> don't have the needed initialization sequence.
> 
> We can however understand how the clock tree works, and then implement some
> functions to derive the various parameters from a given rate. And now that
> those parameters are calculated at runtime, we can remove them from the
> initialization sequence.
> 
> The modes also gained a new parameter which is the clock that they are
> running at, from the register writes they were doing, so for now the switch
> to the new algorithm should be transparent.
> 
> Signed-off-by: Maxime Ripard 
> ---
>   drivers/media/i2c/ov5640.c | 289 -
>   1 file changed, 288 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> index 30b15e91d8be..88fb16341466 100644
> --- a/drivers/media/i2c/ov5640.c
> +++ b/drivers/media/i2c/ov5640.c
> @@ -175,6 +175,7 @@ struct ov5640_mode_info {
>   u32 htot;
>   u32 vact;
>   u32 vtot;
> + u32 pixel_clock;
>   const struct reg_value *reg_data;
>   u32 reg_data_size;
>   };
> @@ -700,6 +701,7 @@ static const struct reg_value 
> ov5640_setting_15fps_QSXGA_2592_1944[] = {
>   /* power-on sensor init reg table */
>   static const struct ov5640_mode_info ov5640_mode_init_data = {
>   0, SUBSAMPLING, 640, 1896, 480, 984,
> + 5600,
>   ov5640_init_setting_30fps_VGA,
>   ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
>   };
> @@ -709,74 +711,91 @@ 
> ov5640_mode_data[OV5640_NUM_FRAMERATES][OV5640_NUM_MODES] = {
>   {
>   {OV5640_MODE_QCIF_176_144, SUBSAMPLING,
>176, 1896, 144, 984,
> +  2800,
>ov5640_setting_15fps_QCIF_176_144,
>ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)},
>   {OV5640_MODE_QVGA_320_240, SUBSAMPLING,
>320, 1896, 240, 984,
> +  2800,
>ov5640_setting_15fps_QVGA_320_240,
>ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)},
>   {OV5640_MODE_VGA_640_480, SUBSAMPLING,
>640, 1896, 480, 1080,
> +  2800,
>ov5640_setting_15fps_VGA_640_480,
>ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)},
>   {OV5640_MODE_NTSC_720_480, SUBSAMPLING,
>720, 1896, 480, 984,
> +  2800,
>ov5640_setting_15fps_NTSC_720_480,
>ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)},
>   {OV5640_MODE_PAL_720_576, SUBSAMPLING,
>720, 1896, 576, 984,
> +  2800,
>ov5640_setting_15fps_PAL_720_576,
>ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)},
>   {OV5640_MODE_XGA_1024_768, SUBSAMPLING,
>1024, 1896, 768, 1080,
> +  2800,
>ov5640_setting_15fps_XGA_1024_768,
>ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)},
>   {OV5640_MODE_720P_1280_720, SUBSAMPLING,
>1280, 1892, 720, 740,
> +  2100,
>ov5640_setting_15fps_720P_1280_720,
>ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)},
>   {OV5640_MODE_1080P_1920_1080, SCALING,
>1920, 2500, 1080, 1120,
> +  4200,
>ov5640_setting_15fps_1080P_1920_1080,
>ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)},
>   {OV5640_MODE_QSXGA_2592_1944, SCALING,
>2592, 2844, 1944, 1968,
> +  8400,
>ov5640_setting_15fps_QSXGA_2592_1944,
>ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)},
>   }, {
>   {OV5640_MODE_QCIF_176_144, SUBSAMPLING,
>176, 1896, 144, 984,
> +  5600,
>ov5640_setting_30fps_QCIF_176_144,
>ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)},
>   {OV5640_MODE_QVGA_320_240, SUBSAMPLING,
>320, 1896, 240, 984,
> +  5600,
>ov5640_setting_30fps_QVGA_320_240,
>ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)},
>   {OV5640_MODE_VGA_640_480, SUBSAMPLING,
>640, 1896, 480, 1080,
> +  5600,
>ov5640_setting_30fps_VGA_640_480,
>ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)},
>   {OV5640_MODE_NTSC_720_480, SUBSAMPLING,
>720, 1896, 480, 

Re: [PATCH v4 12/12] ov5640: Enforce a mode change when changing the framerate

2018-10-15 Thread Hugues FRUCHET
Hi Maxime,

This is already fixed in media tree:
0929983e49c81c1d413702cd9b83bb06c4a2555c media: ov5640: fix framerate update


On 10/11/2018 11:21 AM, Maxime Ripard wrote:
> The current logic only requires to call ov5640_set_mode, which will in turn
> change the clock rates according to the mode and frame interval, when a new
> mode is set up.
> 
> However, when only the frame interval is changed but the mode isn't,
> ov5640_set_mode is never called and the resulting frame rate will be old or
> default one. Fix this by requiring that ov5640_set_mode is called when the
> frame interval is changed as well.
> 
> Signed-off-by: Maxime Ripard 
> ---
>   drivers/media/i2c/ov5640.c | 8 ++--
>   1 file changed, 6 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> index 818411400ef6..e01d2cb93c67 100644
> --- a/drivers/media/i2c/ov5640.c
> +++ b/drivers/media/i2c/ov5640.c
> @@ -2638,8 +2638,12 @@ static int ov5640_s_frame_interval(struct v4l2_subdev 
> *sd,
>   goto out;
>   }
>   
> - sensor->current_fr = frame_rate;
> - sensor->frame_interval = fi->interval;
> + if (frame_rate != sensor->current_fr) {
> + sensor->current_fr = frame_rate;
> + sensor->frame_interval = fi->interval;
> + sensor->pending_mode_change = true;
> + }
> +
>   mode = ov5640_find_mode(sensor, frame_rate, mode->hact,
>   mode->vact, true);
>   if (!mode) {
> 

BR,
Hugues.

Re: [PATCH v3 00/12] media: ov5640: Misc cleanup and improvements

2018-10-04 Thread Hugues FRUCHET
Hi Maxime,

On 10/04/2018 05:04 PM, Maxime Ripard wrote:
> Hi!
> 
> On Mon, Oct 01, 2018 at 02:12:31PM +0000, Hugues FRUCHET wrote:
>>>> This is working perfectly fine on my parallel setup and allows me to
>>>> well support VGA@30fps (instead 27) and also support XGA(1024x768)@15fps
>>>> that I never seen working before.
>>>> So at least for the parallel setup, this serie is working fine for all
>>>> the discrete resolutions and framerate exposed by the driver for the 
>>>> moment:
>>>> * QCIF 176x144 15/30fps
>>>> * QVGA 320x240 15/30fps
>>>> * VGA 640x480 15/30fps
>>>> * 480p 720x480 15/30fps
>>>> * XGA 1024x768 15/30fps
>>>> * 720p 1280x720 15/30fps
>>>> * 1080p 1920x1080 15/30fps
>>>> * 5Mp 2592x1944 15fps
>>>
>>> I'm glad this is working for you as well. I guess I'll resubmit these
>>> patches, but this time making sure someone with a CSI setup tests
>>> before merging. I crtainly don't want to repeat the previous disaster.
>>>
>>> Do you have those patches rebased somewhere? I'm not quite sure how to
>>> fix the conflict with the v4l2_find_nearest_size addition.
>>>
>>>> Moreover I'm not clear on relationship between rate and pixel clock
>>>> frequency.
>>>> I've understood that to DVP_PCLK_DIVIDER (0x3824) register
>>>> and VFIFO_CTRL0C (0x460c) affects the effective pixel clock frequency.
>>>> All the resolutions up to 720x576 are forcing a manual value of 2 for
>>>> divider (0x460c=0x22), but including 720p and more, the divider value is
>>>> controlled by "auto-mode" (0x460c=0x20)... from what I measured and
>>>> understood, for those resolutions, the divider must be set to 1 in order
>>>> that your rate computation match the effective pixel clock on output,
>>>> see [2].
>>>>
>>>> So I wonder if this PCLK divider register should be included
>>>> or not into your rate computation, what do you think ?
>>>
>>> Have you tried change the PCLK divider while in auto-mode? IIRC, I did
>>> that and it was affecting the PCLK rate on my scope, but I wouldn't be
>>> definitive about it.
>>
>> I have tested to change PCLK divider while in auto mode but no effect.
>>
>>> Can we always set the mode to auto and divider to 1, even for the
>>> lower resolutions?
>>
>> This is breaking 176x144@30fps on my side, because of pixel clock too
>> high (112MHz vs 70 MHz max).
> 
> Ok.
> 
>> Instead of using auto mode, my proposal was the inverse: use manual mode
>> with the proper divider to fit the max pixel clock constraint.
> 
> Oh. That would work for me too yeah. How do you want to deal with it?
> Should I send your rebased patches, and you add that change as a
> subsequent patch?

Yes, this is the best option, and we can then ask people having CSI 
setup to check for non-regression after having applied this important 
clock serie patch.
Hoping that this will also work on their setup so that we can move 
forward on next OV5640 improvements.

> 
> Thanks!
> Maxime
> 

BR,
Hugues.


[PATCH] media: ov5640: fix framerate update

2018-10-04 Thread Hugues Fruchet
Changing framerate right before streamon had no effect,
the new framerate value was taken into account only at
next streamon, fix this.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 7 ---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 664ffac..8599e17 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -2573,8 +2573,6 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
if (frame_rate < 0)
frame_rate = OV5640_15_FPS;
 
-   sensor->current_fr = frame_rate;
-   sensor->frame_interval = fi->interval;
mode = ov5640_find_mode(sensor, frame_rate, mode->hact,
mode->vact, true);
if (!mode) {
@@ -2582,7 +2580,10 @@ static int ov5640_s_frame_interval(struct v4l2_subdev 
*sd,
goto out;
}
 
-   if (mode != sensor->current_mode) {
+   if (mode != sensor->current_mode ||
+   frame_rate != sensor->current_fr) {
+   sensor->current_fr = frame_rate;
+   sensor->frame_interval = fi->interval;
sensor->current_mode = mode;
sensor->pending_mode_change = true;
}
-- 
2.7.4



Re: [PATCH 3/4] media: dt-bindings: media: Document pclk-max-frequency property

2018-10-01 Thread Hugues FRUCHET
Hi Sakari,

On 09/28/2018 09:03 AM, Sakari Ailus wrote:
> Hi Hugues,
> 
> On Thu, Sep 27, 2018 at 04:46:06PM +0200, Hugues Fruchet wrote:
>> This optional property aims to inform parallel video devices
>> of the maximum pixel clock frequency admissible by host video
>> interface. If bandwidth of data to be transferred requires a
>> pixel clock which is higher than this value, parallel video
>> device could then typically adapt framerate to reach
>> this constraint.
>>
>> Signed-off-by: Hugues Fruchet 
>> ---
>>   Documentation/devicetree/bindings/media/video-interfaces.txt | 2 ++
>>   1 file changed, 2 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/media/video-interfaces.txt 
>> b/Documentation/devicetree/bindings/media/video-interfaces.txt
>> index baf9d97..fa4c112 100644
>> --- a/Documentation/devicetree/bindings/media/video-interfaces.txt
>> +++ b/Documentation/devicetree/bindings/media/video-interfaces.txt
>> @@ -147,6 +147,8 @@ Optional endpoint properties
>> as 0 (normal). This property is valid for serial busses only.
>>   - strobe: Whether the clock signal is used as clock (0) or strobe (1). Used
>> with CCP2, for instance.
>> +- pclk-max-frequency: maximum pixel clock frequency admissible by video
>> +  host interface.
> 
> Is there a limit on the pixel clock or the link frequency?

The constraint is the frequency of the clock in input of the SoC (pixel 
clock line).

> 
> We do have a property for the link frequency and a control for the pixel
> lock as well as for the link frequency. Could these be used for the
> purpose?

As this was documented mainly for MIPI-CSI2 I was not clear if this 
could be used or not, but video-interface.txt binding let open the door
to parallel port usage...
I had also some hesitations to use this property because what I was 
searching for here was a maximum limit to not exceed while 
"link-frequencies" is described as frequencies to use: "Allowed data bus 
frequencies".
The fact that there was several entries for this property was also quite 
confusing.
What I can do is to use this property and add a comment explaining that 
this can also be used for parallel port as the frequency to not exceed 
on pixel clock signal, what do you think about it ?

Checking drivers which are implementing "link-frequencies", I've found 
OV2659 sensor which is doing almost what I want to, ie compute the clock 
rate depending on link-frequency, see ov2659_pll_calc_params().

> 
> The link frequency in general should be specified for the board, and that
> limits the pixel clock as well in the case the bus transfers a given number
> of pixels per clock.
> 
> The OMAP3ISP driver also address this by reading back the pixel clock from
> the sensor before starting streaming.
> 

BR,
Hugues.

Re: [PATCH v3 00/12] media: ov5640: Misc cleanup and improvements

2018-10-01 Thread Hugues FRUCHET
Hi Maxime,

On 09/28/2018 06:05 PM, Maxime Ripard wrote:
> Hi Hugues,
> 
> On Thu, Sep 27, 2018 at 03:59:04PM +0000, Hugues FRUCHET wrote:
>> Hi Maxime & all OV5640 stakeholders,
>>
>> I've just pushed a new patchset also related to rate/pixel clock
>> handling [1], based on your V3 great work:
>>   >media: ov5640: Adjust the clock based on the expected rate
>>   >media: ov5640: Remove the clocks registers initialization
>>   >media: ov5640: Remove redundant defines
>>   >media: ov5640: Remove redundant register setup
>>   >media: ov5640: Compute the clock rate at runtime
>>   >media: ov5640: Remove pixel clock rates
>>   >media: ov5640: Enhance FPS handling
>>
>> This is working perfectly fine on my parallel setup and allows me to
>> well support VGA@30fps (instead 27) and also support XGA(1024x768)@15fps
>> that I never seen working before.
>> So at least for the parallel setup, this serie is working fine for all
>> the discrete resolutions and framerate exposed by the driver for the moment:
>> * QCIF 176x144 15/30fps
>> * QVGA 320x240 15/30fps
>> * VGA 640x480 15/30fps
>> * 480p 720x480 15/30fps
>> * XGA 1024x768 15/30fps
>> * 720p 1280x720 15/30fps
>> * 1080p 1920x1080 15/30fps
>> * 5Mp 2592x1944 15fps
> 
> I'm glad this is working for you as well. I guess I'll resubmit these
> patches, but this time making sure someone with a CSI setup tests
> before merging. I crtainly don't want to repeat the previous disaster.
> 
> Do you have those patches rebased somewhere? I'm not quite sure how to
> fix the conflict with the v4l2_find_nearest_size addition.
> 
>> Moreover I'm not clear on relationship between rate and pixel clock
>> frequency.
>> I've understood that to DVP_PCLK_DIVIDER (0x3824) register
>> and VFIFO_CTRL0C (0x460c) affects the effective pixel clock frequency.
>> All the resolutions up to 720x576 are forcing a manual value of 2 for
>> divider (0x460c=0x22), but including 720p and more, the divider value is
>> controlled by "auto-mode" (0x460c=0x20)... from what I measured and
>> understood, for those resolutions, the divider must be set to 1 in order
>> that your rate computation match the effective pixel clock on output,
>> see [2].
>>
>> So I wonder if this PCLK divider register should be included
>> or not into your rate computation, what do you think ?
> 
> Have you tried change the PCLK divider while in auto-mode? IIRC, I did
> that and it was affecting the PCLK rate on my scope, but I wouldn't be
> definitive about it.

I have tested to change PCLK divider while in auto mode but no effect.

> 
> Can we always set the mode to auto and divider to 1, even for the
> lower resolutions?
This is breaking 176x144@30fps on my side, because of pixel clock too 
high (112MHz vs 70 MHz max).

Instead of using auto mode, my proposal was the inverse: use manual mode 
with the proper divider to fit the max pixel clock constraint.


BR,
Hugues.

Re: [PATCH v3 00/12] media: ov5640: Misc cleanup and improvements

2018-09-27 Thread Hugues FRUCHET
Hi Maxime & all OV5640 stakeholders,

I've just pushed a new patchset also related to rate/pixel clock 
handling [1], based on your V3 great work:
 >media: ov5640: Adjust the clock based on the expected rate
 >media: ov5640: Remove the clocks registers initialization
 >media: ov5640: Remove redundant defines
 >media: ov5640: Remove redundant register setup
 >media: ov5640: Compute the clock rate at runtime
 >media: ov5640: Remove pixel clock rates
 >media: ov5640: Enhance FPS handling

This is working perfectly fine on my parallel setup and allows me to 
well support VGA@30fps (instead 27) and also support XGA(1024x768)@15fps 
that I never seen working before.
So at least for the parallel setup, this serie is working fine for all 
the discrete resolutions and framerate exposed by the driver for the moment:
* QCIF 176x144 15/30fps
* QVGA 320x240 15/30fps
* VGA 640x480 15/30fps
* 480p 720x480 15/30fps
* XGA 1024x768 15/30fps
* 720p 1280x720 15/30fps
* 1080p 1920x1080 15/30fps
* 5Mp 2592x1944 15fps

Moreover I'm not clear on relationship between rate and pixel clock 
frequency.
I've understood that to DVP_PCLK_DIVIDER (0x3824) register
and VFIFO_CTRL0C (0x460c) affects the effective pixel clock frequency.
All the resolutions up to 720x576 are forcing a manual value of 2 for 
divider (0x460c=0x22), but including 720p and more, the divider value is 
controlled by "auto-mode" (0x460c=0x20)... from what I measured and
understood, for those resolutions, the divider must be set to 1 in order 
that your rate computation match the effective pixel clock on output, 
see [2].

So I wonder if this PCLK divider register should be included
or not into your rate computation, what do you think ?


[1] OV5640: reduce rate according to maximum pixel clock 
https://www.spinics.net/lists/linux-media/msg140958.html:
[2] media: ov5640: move parallel port pixel clock divider out of 
registers set https://www.spinics.net/lists/linux-media/msg140960.html

BR,
Hugues.

On 05/17/2018 10:53 AM, Maxime Ripard wrote:
> Hi,
> 
> Here is a "small" series that mostly cleans up the ov5640 driver code,
> slowly getting rid of the big data array for more understandable code
> (hopefully).
> 
> The biggest addition would be the clock rate computation at runtime,
> instead of relying on those arrays to setup the clock tree
> properly. As a side effect, it fixes the framerate that was off by
> around 10% on the smaller resolutions, and we now support 60fps.
> 
> This also introduces a bunch of new features.
> 
> Let me know what you think,
> Maxime
> 
> Changes from v2:
>- Rebased on latest Sakari PR
>- Fixed the issues reported by Hugues: improper FPS returned for
>  formats, improper rounding of the FPS, some with his suggestions,
>  some by simplifying the logic.
>- Expanded the clock tree comments based on the feedback from Samuel
>  Bobrowicz and Loic Poulain
>- Merged some of the changes made by Samuel Bobrowicz to fix the
>  MIPI rate computation, fix the call sites of the
>  ov5640_set_timings function, the auto-exposure calculation call,
>  etc.
>- Split the patches into smaller ones in order to make it more
>  readable (hopefully)
> 
> Changes from v1:
>- Integrated Hugues' suggestions to fix v4l2-compliance
>- Fixed the bus width with JPEG
>- Dropped the clock rate calculation loops for something simpler as
>  suggested by Sakari
>- Cache the exposure value instead of using the control value
>- Rebased on top of 4.17
> 
> Maxime Ripard (11):
>media: ov5640: Adjust the clock based on the expected rate
>media: ov5640: Remove the clocks registers initialization
>media: ov5640: Remove redundant defines
>media: ov5640: Remove redundant register setup
>media: ov5640: Compute the clock rate at runtime
>media: ov5640: Remove pixel clock rates
>media: ov5640: Enhance FPS handling
>media: ov5640: Make the return rate type more explicit
>media: ov5640: Make the FPS clamping / rounding more extendable
>media: ov5640: Add 60 fps support
>media: ov5640: Remove duplicate auto-exposure setup
> 
> Samuel Bobrowicz (1):
>media: ov5640: Fix timings setup code
> 
>   drivers/media/i2c/ov5640.c | 701 +
>   1 file changed, 392 insertions(+), 309 deletions(-)
> 

[PATCH 2/4] media: v4l2-core: add pixel clock max frequency parallel port property

2018-09-27 Thread Hugues Fruchet
Add pclk-max-frequency property in parallel port endpoint in order
to inform sensor of the maximum pixel clock frequency admissible
by camera interface that is connected on.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/v4l2-core/v4l2-fwnode.c | 3 +++
 include/media/v4l2-fwnode.h   | 2 ++
 2 files changed, 5 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c 
b/drivers/media/v4l2-core/v4l2-fwnode.c
index 169bdbb..505338e 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -158,6 +158,9 @@ static void v4l2_fwnode_endpoint_parse_parallel_bus(
flags |= v ? V4L2_MBUS_DATA_ENABLE_HIGH :
V4L2_MBUS_DATA_ENABLE_LOW;
 
+   if (!fwnode_property_read_u32(fwnode, "pclk-max-frequency", ))
+   bus->pclk_max_frequency = v;
+
bus->flags = flags;
 
 }
diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
index 9cccab6..946b48d 100644
--- a/include/media/v4l2-fwnode.h
+++ b/include/media/v4l2-fwnode.h
@@ -52,11 +52,13 @@ struct v4l2_fwnode_bus_mipi_csi2 {
  * @flags: media bus (V4L2_MBUS_*) flags
  * @bus_width: bus width in bits
  * @data_shift: data shift in bits
+ * @max_pclk_frequency: maximum pixel clock in hertz
  */
 struct v4l2_fwnode_bus_parallel {
unsigned int flags;
unsigned char bus_width;
unsigned char data_shift;
+   unsigned int pclk_max_frequency;
 };
 
 /**
-- 
2.7.4



[PATCH 1/4] media: ov5640: move parallel port pixel clock divider out of registers set

2018-09-27 Thread Hugues Fruchet
Set DVP_PCLK_DIVIDER (0x3824) and VFIFO_CTRL0C (0x460c) registers
in ov5640_set_dvp_pclk() according to mode selected.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 66 --
 1 file changed, 46 insertions(+), 20 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 1a64bb6..da4d754 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -66,6 +66,7 @@
 #define OV5640_REG_TIMING_VTS  0x380e
 #define OV5640_REG_TIMING_TC_REG20 0x3820
 #define OV5640_REG_TIMING_TC_REG21 0x3821
+#define OV5640_REG_DVP_PCLK_DIVIDER0x3824
 #define OV5640_REG_AEC_CTRL00  0x3a00
 #define OV5640_REG_AEC_B50_STEP0x3a08
 #define OV5640_REG_AEC_B60_STEP0x3a0a
@@ -82,6 +83,7 @@
 #define OV5640_REG_SIGMADELTA_CTRL0C   0x3c0c
 #define OV5640_REG_FRAME_CTRL010x4202
 #define OV5640_REG_FORMAT_CONTROL000x4300
+#define OV5640_REG_VFIFO_CTRL0C0x460C
 #define OV5640_REG_POLARITY_CTRL00 0x4740
 #define OV5640_REG_MIPI_CTRL00 0x4800
 #define OV5640_REG_DEBUG_MODE  0x4814
@@ -355,8 +357,8 @@ static const struct reg_value ov5640_setting_VGA_640_480[] 
= {
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
-   {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-   {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+   {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
+   {0x5001, 0xa3, 0, 0},
 };
 
 static const struct reg_value ov5640_setting_XGA_1024_768[] = {
@@ -374,8 +376,8 @@ static const struct reg_value ov5640_setting_XGA_1024_768[] 
= {
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
-   {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-   {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+   {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
+   {0x5001, 0xa3, 0, 0},
 };
 
 static const struct reg_value ov5640_setting_QVGA_320_240[] = {
@@ -393,8 +395,8 @@ static const struct reg_value ov5640_setting_QVGA_320_240[] 
= {
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
-   {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-   {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+   {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
+   {0x5001, 0xa3, 0, 0},
 };
 
 static const struct reg_value ov5640_setting_QCIF_176_144[] = {
@@ -412,8 +414,8 @@ static const struct reg_value ov5640_setting_QCIF_176_144[] 
= {
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
-   {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-   {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+   {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
+   {0x5001, 0xa3, 0, 0},
 };
 
 static const struct reg_value ov5640_setting_NTSC_720_480[] = {
@@ -431,8 +433,8 @@ static const struct reg_value ov5640_setting_NTSC_720_480[] 
= {
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
-   {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-   {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+   {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
+   {0x5001, 0xa3, 0, 0},
 };
 
 static const struct reg_value ov5640_setting_PAL_720_576[] = {
@@ -450,8 +452,8 @@ static const struct reg_value ov5640_setting_PAL_720_576[] 
= {
{0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
{0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
-   {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
-   {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+   {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0},
+   {0x5001, 0xa3, 0, 0},
 };
 
 static const struct reg_value ov5640_setting_720P_1280_720[] = {
@@ -469,8 +471,8 @@ static const struct reg_value 
ov5640_setting_720P_1280_720[] = {
{0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
{0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0

[PATCH 0/4] OV5640: reduce rate according to maximum pixel clock

2018-09-27 Thread Hugues Fruchet
This patch serie aims to reduce parallel port rate according to maximum pixel
clock frequency admissible by camera interface in front of the sensor.
This allows to support any resolutions/framerate requests by decreasing
the framerate according to maximum camera interface capabilities.
This allows typically to enable 5Mp YUV/RGB frame capture even if 15fps
framerate could not be reached by platform.

This work is based on OV5640 Maxime Ripard's runtime clock computing serie [1]
which allows to adapt the clock tree registers according to maximum pixel
clock.

Then the first patch adds handling of pclk divider registers
DVP_PCLK_DIVIDER (0x3824) and VFIFO_CTRL0C (0x460c) in order to
correlate the rate to the effective pixel clock output on parallel interface.

A new devicetree property "pclk-max-frequency" is introduced in order
to inform sensor of the camera interface maximum admissible pixel clock.
This new devicetree property handling is added to V4L2 core.

Then OV5640 ov5640_set_dvp_pclk() is modified to clip rate according
to optional maximum pixel clock property.

References:
  [1] [PATCH v3 00/12] media: ov5640: Misc cleanup and improvements 
https://www.mail-archive.com/linux-media@vger.kernel.org/msg131655.html

Hugues Fruchet (4):
  media: ov5640: move parallel port pixel clock divider out of registers
set
  media: v4l2-core: add pixel clock max frequency parallel port property
  media: dt-bindings: media: Document pclk-max-frequency property
  media: ov5640: reduce rate according to maximum pixel clock frequency

 .../devicetree/bindings/media/video-interfaces.txt |  2 +
 drivers/media/i2c/ov5640.c | 78 --
 drivers/media/v4l2-core/v4l2-fwnode.c  |  3 +
 include/media/v4l2-fwnode.h|  2 +
 4 files changed, 65 insertions(+), 20 deletions(-)

-- 
2.7.4



[PATCH 4/4] media: ov5640: reduce rate according to maximum pixel clock frequency

2018-09-27 Thread Hugues Fruchet
Reduce parallel port rate according to maximum pixel clock frequency
admissible by camera interface.
This allows to support any resolutions/framerate requests by decreasing
the framerate according to maximum camera interface capabilities.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 12 
 1 file changed, 12 insertions(+)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index da4d754..9f3c32e 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -918,6 +918,8 @@ static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor,
 {
u8 prediv, mult, sysdiv, pll_rdiv, bit_div, pclk_div;
int ret;
+   struct i2c_client *client = sensor->i2c_client;
+   unsigned int pclk_freq, max_pclk_freq;
/*
 * FIXME, value of PCLK divider deduced from
 * mode registers hardcoded sequence and tests
@@ -941,6 +943,16 @@ static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor,
if (ret)
return ret;
 
+   pclk_freq = rate / dvp_pclk_divider;
+   max_pclk_freq = sensor->ep.bus.parallel.pclk_max_frequency;
+
+   /* clip rate according to optional maximum pixel clock limit */
+   if (max_pclk_freq && pclk_freq > max_pclk_freq) {
+   rate = max_pclk_freq * dvp_pclk_divider;
+   dev_dbg(>dev, "DVP pixel clock too high (%d > %d Hz), 
reducing rate...\n",
+   pclk_freq, max_pclk_freq);
+   }
+
ov5640_calc_pclk(sensor, rate, , , , _rdiv,
 _div, _div);
 
-- 
2.7.4



[PATCH 3/4] media: dt-bindings: media: Document pclk-max-frequency property

2018-09-27 Thread Hugues Fruchet
This optional property aims to inform parallel video devices
of the maximum pixel clock frequency admissible by host video
interface. If bandwidth of data to be transferred requires a
pixel clock which is higher than this value, parallel video
device could then typically adapt framerate to reach
this constraint.

Signed-off-by: Hugues Fruchet 
---
 Documentation/devicetree/bindings/media/video-interfaces.txt | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/media/video-interfaces.txt 
b/Documentation/devicetree/bindings/media/video-interfaces.txt
index baf9d97..fa4c112 100644
--- a/Documentation/devicetree/bindings/media/video-interfaces.txt
+++ b/Documentation/devicetree/bindings/media/video-interfaces.txt
@@ -147,6 +147,8 @@ Optional endpoint properties
   as 0 (normal). This property is valid for serial busses only.
 - strobe: Whether the clock signal is used as clock (0) or strobe (1). Used
   with CCP2, for instance.
+- pclk-max-frequency: maximum pixel clock frequency admissible by video
+  host interface.
 
 Example
 ---
-- 
2.7.4



[PATCH] media: ov5640: use JPEG mode 3 for 720p

2018-09-24 Thread Hugues Fruchet
Change 720p JPEG mode to mode 3 as per other resolutions.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 30b15e9..664ffac 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -608,7 +608,7 @@ static const struct reg_value 
ov5640_setting_15fps_720P_1280_720[] = {
{0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0},
{0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0},
{0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0},
-   {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0},
+   {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
{0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
{0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0},
 };
-- 
2.7.4



Re: [PATCH v3 0/5] Fix OV5640 exposure & gain

2018-09-24 Thread Hugues FRUCHET
Many thanks for this work Jacopo and Sakari,

On my side I've made some other improvements/fixes on OV5640 that I will 
push in the coming days.

BR,
Hugues.

On 09/17/2018 01:40 PM, Sakari Ailus wrote:
> On Mon, Sep 17, 2018 at 09:47:19AM +0200, jacopo mondi wrote:
>> Hi Sakari,
>>  thanks for handling this
>>
>> On Sun, Sep 16, 2018 at 02:02:30AM +0300, Sakari Ailus wrote:
>>> Hi Jacopo, Hugues,
>>>
>>> On Fri, Sep 14, 2018 at 06:07:12PM +0200, jacopo mondi wrote:
>>>> Hi Sakari,
>>>>
>>>> On Tue, Sep 11, 2018 at 03:48:16PM +0200, Hugues Fruchet wrote:
>>>>> This patch serie fixes some problems around exposure & gain in OV5640 
>>>>> driver.
>>>>
>>>> As you offered to collect this series and my CSI-2 fixes I have just
>>>> re-sent, you might be interested in this branch:
>>>>
>>>> git://jmondi.org/linux
>>>> engicam-imx6q/media-master/ov5640/csi2_init_v4_exposure_v3
>>>>
>>>> I have there re-based this series on top of mine, which is in turn
>>>> based on latest media master, where this series do not apply as-is
>>>> afaict.
>>>>
>>>> I have added to Hugues' patches my reviewed-by and tested-by tags.
>>>> If you prefer to I can send you a pull request, or if you want to have
>>>> a chance to review the whole patch list please refer to the above
>>>> branch.
>>>>
>>>> Let me know if I can help speeding up the inclusion of these two
>>>> series as they fix two real issues of MIPI CSI-2 capture operations
>>>> for this sensor.
>>>
>>> I've pushed the patches here:
>>>
>>> https://git.linuxtv.org/sailus/media_tree.git/log/?h=for-4.20-5>
>>>
>>> There was a merge commit and a few extra patches in your tree; I threw them
>>> out. :-)
>>
>> Yeah, those are a few patches I need for my testing platform... Forgot to
>> remove them, hope you didn't spend too much time on this.
> 
> No, it was rather easy to remove them. I've sent a pull request on these:
> 
> https://patchwork.linuxtv.org/patch/52091/>
> 

[PATCH v3 4/5] media: ov5640: fix auto controls values when switching to manual mode

2018-09-11 Thread Hugues Fruchet
When switching from auto to manual mode, V4L2 core is calling
g_volatile_ctrl() in manual mode in order to get the manual initial value.
Remove the manual mode check/return to not break this behaviour.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 4 
 1 file changed, 4 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 9fb17b5..c110a6a 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -2277,16 +2277,12 @@ static int ov5640_g_volatile_ctrl(struct v4l2_ctrl 
*ctrl)
 
switch (ctrl->id) {
case V4L2_CID_AUTOGAIN:
-   if (!ctrl->val)
-   return 0;
val = ov5640_get_gain(sensor);
if (val < 0)
return val;
sensor->ctrls.gain->val = val;
break;
case V4L2_CID_EXPOSURE_AUTO:
-   if (ctrl->val == V4L2_EXPOSURE_MANUAL)
-   return 0;
val = ov5640_get_exposure(sensor);
if (val < 0)
return val;
-- 
2.7.4



[PATCH v3 0/5] Fix OV5640 exposure & gain

2018-09-11 Thread Hugues Fruchet
This patch serie fixes some problems around exposure & gain in OV5640 driver.

The 4th patch about autocontrols requires also a fix in v4l2-ctrls.c:
https://www.mail-archive.com/linux-media@vger.kernel.org/msg133164.html

Here is the test procedure used for exposure & gain controls check:
1) Preview in background
$> gst-launch-1.0 v4l2src ! "video/x-raw, width=640, Height=480" ! queue ! 
waylandsink -e &
2) Check gain & exposure values
$> v4l2-ctl --all | grep -e exposure -e gain | grep "(int)"
   exposure (int): min=0 max=65535 step=1 default=0 
value=330 flags=inactive, volatile
   gain (int): min=0 max=1023 step=1 default=0 
value=19 flags=inactive, volatile
3) Put finger in front of camera and check that gain/exposure values are 
changing:
$> v4l2-ctl --all | grep -e exposure -e gain | grep "(int)"
   exposure (int): min=0 max=65535 step=1 default=0 
value=660 flags=inactive, volatile
   gain (int): min=0 max=1023 step=1 default=0 
value=37 flags=inactive, volatile
4) switch to manual mode, image exposition must not change
$> v4l2-ctl --set-ctrl=gain_automatic=0
$> v4l2-ctl --set-ctrl=auto_exposure=1
Note the "1" for manual exposure.

5) Check current gain/exposure values:
$> v4l2-ctl --all | grep -e exposure -e gain | grep "(int)"
   exposure (int): min=0 max=65535 step=1 default=0 
value=330
   gain (int): min=0 max=1023 step=1 default=0 
value=20

6) Put finger behind camera and check that gain/exposure values are NOT 
changing:
$> v4l2-ctl --all | grep -e exposure -e gain | grep "(int)"
   exposure (int): min=0 max=65535 step=1 default=0 
value=330
   gain (int): min=0 max=1023 step=1 default=0 
value=20
7) Update exposure, check that it is well changed on display and that same 
value is returned:
$> v4l2-ctl --set-ctrl=exposure=100
$> v4l2-ctl --get-ctrl=exposure
exposure: 100

9) Update gain, check that it is well changed on display and that same value is 
returned:
$> v4l2-ctl --set-ctrl=gain=10
$> v4l2-ctl --get-ctrl=gain
gain: 10

10) Switch back to auto gain/exposure, verify that image is correct and values 
returned are correct:
$> v4l2-ctl --set-ctrl=gain_automatic=1
$> v4l2-ctl --set-ctrl=auto_exposure=0
$> v4l2-ctl --all | grep -e exposure -e gain | grep "(int)"
   exposure (int): min=0 max=65535 step=1 default=0 
value=330 flags=inactive, volatile
   gain (int): min=0 max=1023 step=1 default=0 
value=22 flags=inactive, volatile
Note the "0" for auto exposure.

===
= history =
===
version 3:
  - Change patch 5/5 by removing set_mode() orig_mode parameter as per jacopo' 
suggestion:
https://www.spinics.net/lists/linux-media/msg139457.html

version 2:
  - Fix patch 3/5 commit comment and rename binning function as per jacopo' 
suggestion:
https://www.mail-archive.com/linux-media@vger.kernel.org/msg133272.html

Hugues Fruchet (5):
  media: ov5640: fix exposure regression
  media: ov5640: fix auto gain & exposure when changing mode
  media: ov5640: fix wrong binning value in exposure calculation
  media: ov5640: fix auto controls values when switching to manual mode
  media: ov5640: fix restore of last mode set

 drivers/media/i2c/ov5640.c | 128 ++---
 1 file changed, 73 insertions(+), 55 deletions(-)

-- 
2.7.4



[PATCH v3 2/5] media: ov5640: fix auto gain & exposure when changing mode

2018-09-11 Thread Hugues Fruchet
Ensure that auto gain and auto exposure are well restored
when changing mode.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 96 ++
 1 file changed, 54 insertions(+), 42 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 4b9da8b..7c569de 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1000,6 +1000,18 @@ static int ov5640_get_gain(struct ov5640_dev *sensor)
return gain & 0x3ff;
 }
 
+static int ov5640_set_gain(struct ov5640_dev *sensor, int gain)
+{
+   return ov5640_write_reg16(sensor, OV5640_REG_AEC_PK_REAL_GAIN,
+ (u16)gain & 0x3ff);
+}
+
+static int ov5640_set_autogain(struct ov5640_dev *sensor, bool on)
+{
+   return ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL,
+ BIT(1), on ? 0 : BIT(1));
+}
+
 static int ov5640_set_stream_dvp(struct ov5640_dev *sensor, bool on)
 {
int ret;
@@ -1577,7 +1589,7 @@ static int ov5640_set_mode_exposure_calc(struct 
ov5640_dev *sensor,
}
 
/* set capture gain */
-   ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.gain, cap_gain16);
+   ret = ov5640_set_gain(sensor, cap_gain16);
if (ret)
return ret;
 
@@ -1590,7 +1602,7 @@ static int ov5640_set_mode_exposure_calc(struct 
ov5640_dev *sensor,
}
 
/* set exposure */
-   return __v4l2_ctrl_s_ctrl(sensor->ctrls.exposure, cap_shutter);
+   return ov5640_set_exposure(sensor, cap_shutter);
 }
 
 /*
@@ -1598,26 +1610,13 @@ static int ov5640_set_mode_exposure_calc(struct 
ov5640_dev *sensor,
  * change mode directly
  */
 static int ov5640_set_mode_direct(struct ov5640_dev *sensor,
- const struct ov5640_mode_info *mode,
- bool auto_exp)
+ const struct ov5640_mode_info *mode)
 {
-   int ret;
-
if (!mode->reg_data)
return -EINVAL;
 
/* Write capture setting */
-   ret = ov5640_load_regs(sensor, mode);
-   if (ret < 0)
-   return ret;
-
-   /* turn auto gain/exposure back on for direct mode */
-   ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_gain, 1);
-   if (ret)
-   return ret;
-
-   return __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, auto_exp ?
- V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL);
+   return ov5640_load_regs(sensor, mode);
 }
 
 static int ov5640_set_mode(struct ov5640_dev *sensor,
@@ -1625,6 +1624,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
 {
const struct ov5640_mode_info *mode = sensor->current_mode;
enum ov5640_downsize_mode dn_mode, orig_dn_mode;
+   bool auto_gain = sensor->ctrls.auto_gain->val == 1;
bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
int ret;
 
@@ -1632,19 +1632,23 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
orig_dn_mode = orig_mode->dn_mode;
 
/* auto gain and exposure must be turned off when changing modes */
-   ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_gain, 0);
-   if (ret)
-   return ret;
+   if (auto_gain) {
+   ret = ov5640_set_autogain(sensor, false);
+   if (ret)
+   return ret;
+   }
 
-   ret = ov5640_set_autoexposure(sensor, false);
-   if (ret)
-   return ret;
+   if (auto_exp) {
+   ret = ov5640_set_autoexposure(sensor, false);
+   if (ret)
+   goto restore_auto_gain;
+   }
 
if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
(dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
/*
 * change between subsampling and scaling
-* go through exposure calucation
+* go through exposure calculation
 */
ret = ov5640_set_mode_exposure_calc(sensor, mode);
} else {
@@ -1652,11 +1656,16 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
 * change inside subsampling or scaling
 * download firmware directly
 */
-   ret = ov5640_set_mode_direct(sensor, mode, auto_exp);
+   ret = ov5640_set_mode_direct(sensor, mode);
}
-
if (ret < 0)
-   return ret;
+   goto restore_auto_exp_gain;
+
+   /* restore auto gain and exposure */
+   if (auto_gain)
+   ov5640_set_autogain(sensor, true);
+   if (auto_exp)
+   ov5640_set_autoexposure(sensor, true);
 
ret = ov5640_set_timings(sensor, mode);
if (ret < 0)
@@ -1681,6 +1690,15 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
sensor->pending_mode_change = f

[PATCH v3 3/5] media: ov5640: fix wrong binning value in exposure calculation

2018-09-11 Thread Hugues Fruchet
ov5640_set_mode_exposure_calc() is checking binning value but
binning value read is buggy, fix this.
Rename ov5640_binning_on() to ov5640_get_binning() as per other
similar functions.

Signed-off-by: Hugues Fruchet 
Reviewed-by: Laurent Pinchart 
---
 drivers/media/i2c/ov5640.c | 8 
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 7c569de..9fb17b5 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1349,7 +1349,7 @@ static int ov5640_set_ae_target(struct ov5640_dev 
*sensor, int target)
return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1F, fast_low);
 }
 
-static int ov5640_binning_on(struct ov5640_dev *sensor)
+static int ov5640_get_binning(struct ov5640_dev *sensor)
 {
u8 temp;
int ret;
@@ -1357,8 +1357,8 @@ static int ov5640_binning_on(struct ov5640_dev *sensor)
ret = ov5640_read_reg(sensor, OV5640_REG_TIMING_TC_REG21, );
if (ret)
return ret;
-   temp &= 0xfe;
-   return temp ? 1 : 0;
+
+   return temp & BIT(0);
 }
 
 static int ov5640_set_binning(struct ov5640_dev *sensor, bool enable)
@@ -1468,7 +1468,7 @@ static int ov5640_set_mode_exposure_calc(struct 
ov5640_dev *sensor,
if (ret < 0)
return ret;
prev_shutter = ret;
-   ret = ov5640_binning_on(sensor);
+   ret = ov5640_get_binning(sensor);
if (ret < 0)
return ret;
if (ret && mode->id != OV5640_MODE_720P_1280_720 &&
-- 
2.7.4



[PATCH v3 1/5] media: ov5640: fix exposure regression

2018-09-11 Thread Hugues Fruchet
fixes: bf4a4b518c20 ("media: ov5640: Don't force the auto exposure state at 
start time").

Symptom was black image when capturing HD or 5Mp picture
due to manual exposure set to 1 while it was intended to
set autoexposure to "manual", fix this.

Signed-off-by: Hugues Fruchet 
Reviewed-by: Laurent Pinchart 
---
 drivers/media/i2c/ov5640.c | 18 --
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 1ecbb7a..4b9da8b 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -938,6 +938,12 @@ static int ov5640_load_regs(struct ov5640_dev *sensor,
return ret;
 }
 
+static int ov5640_set_autoexposure(struct ov5640_dev *sensor, bool on)
+{
+   return ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL,
+ BIT(0), on ? 0 : BIT(0));
+}
+
 /* read exposure, in number of line periods */
 static int ov5640_get_exposure(struct ov5640_dev *sensor)
 {
@@ -1593,7 +1599,7 @@ static int ov5640_set_mode_exposure_calc(struct 
ov5640_dev *sensor,
  */
 static int ov5640_set_mode_direct(struct ov5640_dev *sensor,
  const struct ov5640_mode_info *mode,
- s32 exposure)
+ bool auto_exp)
 {
int ret;
 
@@ -1610,7 +1616,8 @@ static int ov5640_set_mode_direct(struct ov5640_dev 
*sensor,
if (ret)
return ret;
 
-   return __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, exposure);
+   return __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, auto_exp ?
+ V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL);
 }
 
 static int ov5640_set_mode(struct ov5640_dev *sensor,
@@ -1618,7 +1625,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
 {
const struct ov5640_mode_info *mode = sensor->current_mode;
enum ov5640_downsize_mode dn_mode, orig_dn_mode;
-   s32 exposure;
+   bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
int ret;
 
dn_mode = mode->dn_mode;
@@ -1629,8 +1636,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
if (ret)
return ret;
 
-   exposure = sensor->ctrls.auto_exp->val;
-   ret = ov5640_set_exposure(sensor, V4L2_EXPOSURE_MANUAL);
+   ret = ov5640_set_autoexposure(sensor, false);
if (ret)
return ret;
 
@@ -1646,7 +1652,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
 * change inside subsampling or scaling
 * download firmware directly
 */
-   ret = ov5640_set_mode_direct(sensor, mode, exposure);
+   ret = ov5640_set_mode_direct(sensor, mode, auto_exp);
}
 
if (ret < 0)
-- 
2.7.4



[PATCH v3 5/5] media: ov5640: fix restore of last mode set

2018-09-11 Thread Hugues Fruchet
Mode setting depends on last mode set, in particular
because of exposure calculation when downscale mode
change between subsampling and scaling.
At stream on the last mode was wrongly set to current mode,
so no change was detected and exposure calculation
was not made, fix this.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 12 
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index c110a6a..427c2e7 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -225,6 +225,7 @@ struct ov5640_dev {
struct v4l2_mbus_framefmt fmt;
 
const struct ov5640_mode_info *current_mode;
+   const struct ov5640_mode_info *last_mode;
enum ov5640_frame_rate current_fr;
struct v4l2_fract frame_interval;
 
@@ -1619,10 +1620,10 @@ static int ov5640_set_mode_direct(struct ov5640_dev 
*sensor,
return ov5640_load_regs(sensor, mode);
 }
 
-static int ov5640_set_mode(struct ov5640_dev *sensor,
-  const struct ov5640_mode_info *orig_mode)
+static int ov5640_set_mode(struct ov5640_dev *sensor)
 {
const struct ov5640_mode_info *mode = sensor->current_mode;
+   const struct ov5640_mode_info *orig_mode = sensor->last_mode;
enum ov5640_downsize_mode dn_mode, orig_dn_mode;
bool auto_gain = sensor->ctrls.auto_gain->val == 1;
bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
@@ -1688,6 +1689,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
return ret;
 
sensor->pending_mode_change = false;
+   sensor->last_mode = mode;
 
return 0;
 
@@ -1713,6 +1715,7 @@ static int ov5640_restore_mode(struct ov5640_dev *sensor)
ret = ov5640_load_regs(sensor, _mode_init_data);
if (ret < 0)
return ret;
+   sensor->last_mode = _mode_init_data;
 
ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f,
 (ilog2(OV5640_SCLK2X_ROOT_DIVIDER_DEFAULT) << 2) |
@@ -1721,7 +1724,7 @@ static int ov5640_restore_mode(struct ov5640_dev *sensor)
return ret;
 
/* now restore the last capture mode */
-   ret = ov5640_set_mode(sensor, _mode_init_data);
+   ret = ov5640_set_mode(sensor);
if (ret < 0)
return ret;
 
@@ -2551,7 +2554,7 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int 
enable)
 
if (sensor->streaming == !enable) {
if (enable && sensor->pending_mode_change) {
-   ret = ov5640_set_mode(sensor, sensor->current_mode);
+   ret = ov5640_set_mode(sensor);
if (ret)
goto out;
 
@@ -2667,6 +2670,7 @@ static int ov5640_probe(struct i2c_client *client,
sensor->current_mode =
_mode_data[OV5640_30_FPS][OV5640_MODE_VGA_640_480];
sensor->pending_mode_change = true;
+   sensor->last_mode = sensor->current_mode;
 
sensor->ae_target = 52;
 
-- 
2.7.4



Re: [PATCH v6 0/3] Add support for MPEG-2 in DELTA video decoder

2018-09-11 Thread Hugues FRUCHET
Hi Hans,

On 09/09/2018 11:38 AM, Hans Verkuil wrote:
> Hi Hugues,
> 
> On 04/28/2017 03:25 PM, Hugues Fruchet wrote:
>> The patchset implements the MPEG-2 part of V4L2 unified low-level decoder
>> API RFC [0] needed by stateless video decoders, ie decoders which requires
>> specific parsing metadata in addition to video bitstream chunk in order
>> to complete decoding.
>> A reference implementation using STMicroelectronics DELTA video decoder
>> is provided as initial support in this patchset.
>> In addition to this patchset, a libv4l plugin is also provided which convert
>> MPEG-2 video bitstream to "parsed MPEG-2" by parsing the user video bitstream
>> and filling accordingly the dedicated controls, doing so user code remains
>> unchanged whatever decoder is: stateless or not.
>>
>> The first patch implements the MPEG-2 part of V4L2 unified low-level decoder
>> API RFC [0]. A dedicated "parsed MPEG-2" pixel format has been introduced 
>> with
>> its related extended controls in order that user provides both video 
>> bitstream
>> chunk and the associated extra data resulting from this video bitstream chunk
>> parsing.
>>
>> The second patch adds the support of "parsed" pixel format inside DELTA video
>> decoder including handling of the dedicated controls and setting of parsing
>> metadata required by decoder layer.
>> Please note that the current implementation has a restriction regarding
>> the atomicity of S_EXT_CTRL/QBUF that must be guaranteed by user.
>> This restriction will be removed when V4L2 request API will be implemented 
>> [1].
>> Please also note the failure in v4l2-compliance in controls section, related
>> to complex compound controls handling, to be discussed to find the right way
>> to fix it in v4l2-compliance.
>>
>> The third patch adds the support of DELTA MPEG-2 stateless video decoder 
>> back-end.
> 
> I've marked this (old) series as obsoleted in patchwork. The Request API 
> together
> with the cedrus stateless MPEG decoder is about to be merged for 4.20, so it 
> would
> be very nice indeed if you can resurrect this driver and base it on the 
> Request API.

Many thanks to you and all contributors for this long and hard work on 
request API, no doubt that this is a major step towards video codecs 
support standardisation in Linux.

Now I have switched on other activities related to camera on STM32 
platform as you can see regularly on mailing list and unfortunately it 
will be difficult to me to resurrect this now.
Anyway I hope that this implementation in kernel associated to the 
userspace part provided in libv4l will serve its example purpose for any 
other MPEG2 implementation (idea was that different soc vendor kernel 
driver but a single userspace implementation...).


Best regards,
Hugues.
> 
> Thanks!
> 
>   Hans
> 
>>
>>
>> This driver depends on:
>>[PATCH v7 00/10] Add support for DELTA video decoder of 
>> STMicroelectronics STiH4xx SoC series 
>> https://patchwork.linuxtv.org/patch/39186/
>>
>> References:
>>[0] [RFC] V4L2 unified low-level decoder API 
>> https://www.spinics.net/lists/linux-media/msg107150.html
>>[1] [ANN] Report of the V4L2 Request API brainstorm meeting 
>> https://www.spinics.net/lists/linux-media/msg106699.html
>>
>> ===
>> = history =
>> ===
>> version 6:
>>- patchset 5 review from Hans:
>>  - revisit 32/64 bit compat in mpeg2 controls struct (using pahole 
>> utility)
>>to avoid padding fields introduction
>>- pass latest v4l2-compliance with compound controls fixes
>>  - fix delta_subscribe_event() adding missing control event
>>- fix warnings at documentation generation (add exceptions)
>>
>> version 5:
>>- patchset 4 review from Hans:
>>  - fix 32/64 bit compat in mpeg2 controls struct (using pahole utility)
>>  - fix upper case at begining of words in v4l2_ctrl_get_name()
>>
>> version 4:
>>- patchset 3 review from Nicolas Dufresne
>>  - one attribute per line in structure
>>- fix some multilines comments
>>
>> version 3:
>>- fix warning on parisc architecture
>>
>> version 2:
>>- rebase on top of DELTA v7, refer to [0]
>>- change VIDEO_STI_DELTA_DRIVER to default=y as per Mauro recommendations
>>
>> version 1:
>>- Initial submission
>>
>> ===
>> = v4l2-compliance =
>> ===
>> Below is the v4l2-compliance report, v4l2-compliance

Re: [PATCH v2 5/5] media: ov5640: fix restore of last mode set

2018-09-11 Thread Hugues FRUCHET
Hi Laurent,

On 09/10/2018 10:56 PM, Laurent Pinchart wrote:
> Hi Hugues,
> 
> On Monday, 10 September 2018 18:14:45 EEST Hugues FRUCHET wrote:
>> On 09/07/2018 04:18 PM, Laurent Pinchart wrote:
>>> On Thursday, 16 August 2018 18:07:54 EEST Hugues FRUCHET wrote:
>>>> On 08/16/2018 12:10 PM, jacopo mondi wrote:
>>>>> On Mon, Aug 13, 2018 at 12:19:46PM +0200, Hugues Fruchet wrote:
>>>>>
>>>>>> Mode setting depends on last mode set, in particular
>>>>>> because of exposure calculation when downscale mode
>>>>>> change between subsampling and scaling.
>>>>>> At stream on the last mode was wrongly set to current mode,
>>>>>> so no change was detected and exposure calculation
>>>>>> was not made, fix this.
>>>>>
>>>>> I actually see a different issue here...
>>>>
>>>> Which problem do you have exactly, you got a VGA JPEG instead of a QVGA
>>>> YUYV ?
>>>>
>>>>> The issue I see here depends on the format programmed through
>>>>> set_fmt() never being applied when using the sensor with a media
>>>>> controller equipped device (in this case an i.MX6 board) through
>>>>> capture sessions, and the not properly calculated exposure you see may
>>>>> be a consequence of this.
>>>>>
>>>>> I'll try to write down what I see, with the help of some debug output.
>>>>>
>>>>> - At probe time mode 640x460@30 is programmed:
>>>>>
>>>>>  [1.651216] ov5640_probe: Initial mode with id: 2
>>>>>
>>>>> - I set the format on the sensor's pad and it gets not applied but
>>>>>  marked as pending as the sensor is powered off:
>   >>>
>>>>>  #media-ctl --set-v4l2 "'ov5640 2-003c':0[fmt:UYVY2X8/320x240
>>>>>  field:none]"
>>>>>   [   65.611983] ov5640_set_fmt: NEW mode with id: 1 - PENDING
>>>>
>>>> So here sensor->current_mode is set to <1>;//QVGA
>>>> and sensor->pending_mode_change is set to true;
>>>>
>>>>> - I start streaming with yavta, and the sensor receives a power on;
>>>>>  this causes the 'initial' format to be re-programmed and the
>>>>>  pending change to be ignored:
>>>>>
>>>>>  #yavta -c10 -n4 -f YUYV -s $320x240  -F"../frame-#.yuv" /dev/video4
>>>>>  
>>>>>   [   69.395018] ov5640_set_power:1805 - on
>>>>>   [   69.431342] ov5640_restore_mode:1711
>>>>>   [   69.996882] ov5640_set_mode: Apply mode with id: 0
>>>>>
>>>>>  The 'ov5640_set_mode()' call from 'ov5640_restore_mode()' clears
>>>>>  the sensor->pending flag, discarding the newly requested format, for
>>>>>  this reason, at s_stream() time, the pending flag is not set
>>>>>  anymore.
>>>>
>>>> OK but before clearing sensor->pending_mode_change, set_mode() is
>>>> loading registers corresponding to sensor->current_mode:
>>>>
>>>> static int ov5640_set_mode(struct ov5640_dev *sensor,
>>>>   const struct ov5640_mode_info *orig_mode)
>>>> {
>>>> ==>const struct ov5640_mode_info *mode = sensor->current_mode;
>>>> ...
>>>>ret = ov5640_set_mode_direct(sensor, mode, exposure);
>>>>
>>>> => so mode <1> is expected to be set now, so I don't understand your
>>>> trace:
>>>> "> [   69.996882] ov5640_set_mode: Apply mode with id: 0"
>>>> Which variable do you trace that shows "0" ?
>>>>
>>>>> Are you using a media-controller system? I suspect in non-mc cases,
>>>>> the set_fmt is applied through a single power_on/power_off session, not
>>>>> causing the 'restore_mode()' issue. Is this the case for you or your
>>>>> issue is differnt?
>>>>>
>>>>> Edit:
>>>>> Mita-san tried to address the issue of the output pixel format not
>>>>> being restored when the image format was restored in
>>>>> 19ad26f9e6e1 ("media: ov5640: add missing output pixel format setting")
>>>>>
>>>>> I understand the issue he tried to fix, but shouldn't the pending
>>>>> format (if a

Re: [PATCH v2 5/5] media: ov5640: fix restore of last mode set

2018-09-11 Thread Hugues FRUCHET
Hi Jacopo,

On 08/25/2018 04:53 PM, jacopo mondi wrote:
> Hi Hugues,
>   one more comment on this patch..
> 
> On Mon, Aug 13, 2018 at 12:19:46PM +0200, Hugues Fruchet wrote:
>> Mode setting depends on last mode set, in particular
>> because of exposure calculation when downscale mode
>> change between subsampling and scaling.
>> At stream on the last mode was wrongly set to current mode,
>> so no change was detected and exposure calculation
>> was not made, fix this.
>>
>> Signed-off-by: Hugues Fruchet 
>> ---
>>   drivers/media/i2c/ov5640.c | 8 +++-
>>   1 file changed, 7 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
>> index c110a6a..923cc30 100644
>> --- a/drivers/media/i2c/ov5640.c
>> +++ b/drivers/media/i2c/ov5640.c
>> @@ -225,6 +225,7 @@ struct ov5640_dev {
>>  struct v4l2_mbus_framefmt fmt;
>>
>>  const struct ov5640_mode_info *current_mode;
>> +const struct ov5640_mode_info *last_mode;
>>  enum ov5640_frame_rate current_fr;
>>  struct v4l2_fract frame_interval;
>>
>> @@ -1628,6 +1629,9 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
>>  bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
>>  int ret;
>>
>> +if (!orig_mode)
>> +orig_mode = mode;
>> +
> 
> Am I wrong or with the introduction of last_mode we could drop the
> 'orig_mode' parameter (which has confused me already :/ ) from the
> set_mode() function?
> 
> Just set here 'orig_mode = sensor->last_mode' and make sure last_mode
> is intialized properly at probe time...
> 
> Or is there some other value in keeping the orig_mode parameter here?
> 
> Thanks
> j

I'm fine with that change, will push it in v3.


> 
>>  dn_mode = mode->dn_mode;
>>  orig_dn_mode = orig_mode->dn_mode;
>>
>> @@ -1688,6 +1692,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
>>  return ret;
>>
>>  sensor->pending_mode_change = false;
>> +sensor->last_mode = mode;
>>
>>  return 0;
>>
>> @@ -2551,7 +2556,8 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int 
>> enable)
>>
>>  if (sensor->streaming == !enable) {
>>  if (enable && sensor->pending_mode_change) {
>> -ret = ov5640_set_mode(sensor, sensor->current_mode);
>> +ret = ov5640_set_mode(sensor, sensor->last_mode);
>> +
>>  if (ret)
>>  goto out;
>>
>> --
>> 2.7.4
>>

BR Hugues.

Re: [PATCH v2 5/5] media: ov5640: fix restore of last mode set

2018-09-10 Thread Hugues FRUCHET
Hi Laurent, Steve,

On 09/07/2018 04:18 PM, Laurent Pinchart wrote:
> Hello Hugues,
> 
> On Thursday, 16 August 2018 18:07:54 EEST Hugues FRUCHET wrote:
>> On 08/16/2018 12:10 PM, jacopo mondi wrote:
>>> On Mon, Aug 13, 2018 at 12:19:46PM +0200, Hugues Fruchet wrote:
>>>
>>>> Mode setting depends on last mode set, in particular
>>>> because of exposure calculation when downscale mode
>>>> change between subsampling and scaling.
>>>> At stream on the last mode was wrongly set to current mode,
>>>> so no change was detected and exposure calculation
>>>> was not made, fix this.
>>>
>>> I actually see a different issue here...
>>
>> Which problem do you have exactly, you got a VGA JPEG instead of a QVGA
>> YUYV ?
>>
>>> The issue I see here depends on the format programmed through
>>> set_fmt() never being applied when using the sensor with a media
>>> controller equipped device (in this case an i.MX6 board) through
>>> capture sessions, and the not properly calculated exposure you see may
>>> be a consequence of this.
>>>
>>> I'll try to write down what I see, with the help of some debug output.
>>>
>>> - At probe time mode 640x460@30 is programmed:
>>>
>>> [1.651216] ov5640_probe: Initial mode with id: 2
>>>
>>> - I set the format on the sensor's pad and it gets not applied but
>>> marked as pending as the sensor is powered off:
>>>
>>> #media-ctl --set-v4l2 "'ov5640 2-003c':0[fmt:UYVY2X8/320x240
>>> field:none]"
>>>  [   65.611983] ov5640_set_fmt: NEW mode with id: 1 - PENDING
>>
>> So here sensor->current_mode is set to <1>;//QVGA
>> and sensor->pending_mode_change is set to true;
>>
>>> - I start streaming with yavta, and the sensor receives a power on;
>>> this causes the 'initial' format to be re-programmed and the pending
>>> change to be ignored:
>>>
>>> #yavta -c10 -n4 -f YUYV -s $320x240  -F"../frame-#.yuv" /dev/video4
>>> 
>>>  [   69.395018] ov5640_set_power:1805 - on
>>>  [   69.431342] ov5640_restore_mode:1711
>>>  [   69.996882] ov5640_set_mode: Apply mode with id: 0
>>>
>>> The 'ov5640_set_mode()' call from 'ov5640_restore_mode()' clears the
>>> sensor->pending flag, discarding the newly requested format, for
>>> this reason, at s_stream() time, the pending flag is not set
>>> anymore.
>>
>> OK but before clearing sensor->pending_mode_change, set_mode() is
>> loading registers corresponding to sensor->current_mode:
>> static int ov5640_set_mode(struct ov5640_dev *sensor,
>> const struct ov5640_mode_info *orig_mode)
>> {
>> ==>  const struct ov5640_mode_info *mode = sensor->current_mode;
>> ...
>>  ret = ov5640_set_mode_direct(sensor, mode, exposure);
>>
>> => so mode <1> is expected to be set now, so I don't understand your trace:
>> "> [   69.996882] ov5640_set_mode: Apply mode with id: 0"
>> Which variable do you trace that shows "0" ?
>>
>>> Are you using a media-controller system? I suspect in non-mc cases,
>>> the set_fmt is applied through a single power_on/power_off session, not
>>> causing the 'restore_mode()' issue. Is this the case for you or your
>>> issue is differnt?
>>>
>>> Edit:
>>> Mita-san tried to address the issue of the output pixel format not
>>> being restored when the image format was restored in
>>> 19ad26f9e6e1 ("media: ov5640: add missing output pixel format setting")
>>>
>>> I understand the issue he tried to fix, but shouldn't the pending
>>> format (if any) be applied instead of the initial one unconditionally?
>>
>> This is what does the ov5640_restore_mode(), set the current mode
>> (sensor->current_mode), that is done through this line:
>>  /* now restore the last capture mode */
>>  ret = ov5640_set_mode(sensor, _mode_init_data);
>> => note that the comment above is weird, in fact it is the "current"
>> mode that is set.
>> => note also that the 2nd parameter is not the mode to be set but the
>> previously applied mode ! (ie loaded in ov5640 registers). This is used
>> to decide if we have to go to the "set_mode_exposure_calc" or
>> "set_mode_direct".
>>
>> the ov5640_restore_mode() also set the current pix

Re: [PATCH v2 4/5] media: ov5640: fix auto controls values when switching to manual mode

2018-09-10 Thread Hugues FRUCHET
Hi Laurent,

On 09/10/2018 12:46 PM, Laurent Pinchart wrote:
> Hi Hugues,
> 
> On Monday, 10 September 2018 13:23:41 EEST Hugues FRUCHET wrote:
>> On 09/06/2018 03:31 PM, Laurent Pinchart wrote:
>>> On Monday, 13 August 2018 13:19:45 EEST Hugues Fruchet wrote:
>>>
>>>> When switching from auto to manual mode, V4L2 core is calling
>>>> g_volatile_ctrl() in manual mode in order to get the manual initial
>>>> value. Remove the manual mode check/return to not break this behaviour.
>>>>
>>>> Signed-off-by: Hugues Fruchet 
>>>> ---
>>>>drivers/media/i2c/ov5640.c | 4 
>>>>1 file changed, 4 deletions(-)
>>>>
>>>> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
>>>> index 9fb17b5..c110a6a 100644
>>>> --- a/drivers/media/i2c/ov5640.c
>>>> +++ b/drivers/media/i2c/ov5640.c
>>>> @@ -2277,16 +2277,12 @@ static int ov5640_g_volatile_ctrl(struct
>>>> v4l2_ctrl *ctrl)
>>>>
>>>>switch (ctrl->id) {
>>>>case V4L2_CID_AUTOGAIN:
>>>> -  if (!ctrl->val)
>>>> -  return 0;
>>>>
>>>>val = ov5640_get_gain(sensor);
>>>>if (val < 0)
>>>>return val;
>>>>
>>>>sensor->ctrls.gain->val = val;
>>>>break;
>>>
>>> What is this even supposed to do ? Only the V4L2_CID_GAIN and
>>> V4L2_CID_EXPOSURE have the volatile flag set. Why can't this code be
>>> replaced with
>>
>> This is because V4L2_CID_AUTOGAIN & V4L2_CID_GAIN are declared as
>> auto-cluster:
>>static int ov5640_init_controls(struct ov5640_dev *sensor)
>>  /* Auto/manual gain */
>>  ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
>>   0, 1, 1, 1);
>>  ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
>>  0, 1023, 1, 0);
>> [...]
>>  v4l2_ctrl_auto_cluster(2, >auto_gain, 0, true);
>>
>> By checking many other drivers that are using clustered auto controls,
>> they are all doing that way:
>>
>> ctrls->auto_x = v4l2_ctrl_new_std(CID_X_AUTO..
>> ctrls->x = v4l2_ctrl_new_std(CID_X..
>> v4l2_ctrl_auto_cluster(2, >auto, 0, true);
>>
>> g_volatile_ctrl(ctrl)
>> switch (ctrl->id) {
>>  case CID_X_AUTO:
>>ctrls->x->val = READ_REG()
> 
> Seems like cargo-cult to me. Why is this better than the construct below ?
> 

I have done the changes as per your suggestion, but behaviour is broken: 
when autogain control is on and I read gain value, gain is not refreshed 
with current gain value from sensor, but stick to last manual value set.

Moreover I've checked in vivid how it is done and still we have the 
structure of code I've already mentionned:

static int vivid_user_vid_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, 
ctrl_hdl_user_vid);

switch (ctrl->id) {
case V4L2_CID_AUTOGAIN:
dev->gain->val = dev->jiffies_vid_cap & 0xff;
break;
}
return 0;
}


>>> case V4L2_CID_GAIN:
>>> val = ov5640_get_gain(sensor);
>>> if (val < 0)
>>> return val;
>>> ctrl->val = val;
>>> break;
>>>
>>>>case V4L2_CID_EXPOSURE_AUTO:
>>>> -  if (ctrl->val == V4L2_EXPOSURE_MANUAL)
>>>> -  return 0;
>>>>val = ov5640_get_exposure(sensor);
>>>>if (val < 0)
>>>>return val;
>>>
>>> And same here.
> 

BR Hugues.

Re: [PATCH v2 4/5] media: ov5640: fix auto controls values when switching to manual mode

2018-09-10 Thread Hugues FRUCHET
Hi Laurent,

On 09/06/2018 03:31 PM, Laurent Pinchart wrote:
> Hi Hugues,
> 
> Thank you for the patch.
> 
> On Monday, 13 August 2018 13:19:45 EEST Hugues Fruchet wrote:
>> When switching from auto to manual mode, V4L2 core is calling
>> g_volatile_ctrl() in manual mode in order to get the manual initial value.
>> Remove the manual mode check/return to not break this behaviour.
>>
>> Signed-off-by: Hugues Fruchet 
>> ---
>>   drivers/media/i2c/ov5640.c | 4 
>>   1 file changed, 4 deletions(-)
>>
>> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
>> index 9fb17b5..c110a6a 100644
>> --- a/drivers/media/i2c/ov5640.c
>> +++ b/drivers/media/i2c/ov5640.c
>> @@ -2277,16 +2277,12 @@ static int ov5640_g_volatile_ctrl(struct v4l2_ctrl
>> *ctrl)
>>
>>  switch (ctrl->id) {
>>  case V4L2_CID_AUTOGAIN:
>> -if (!ctrl->val)
>> -return 0;
>>  val = ov5640_get_gain(sensor);
>>  if (val < 0)
>>  return val;
>>  sensor->ctrls.gain->val = val;
>>  break;
> 
> What is this even supposed to do ? Only the V4L2_CID_GAIN and
> V4L2_CID_EXPOSURE have the volatile flag set. Why can't this code be replaced
> with

This is because V4L2_CID_AUTOGAIN & V4L2_CID_GAIN are declared as 
auto-cluster:
  static int ov5640_init_controls(struct ov5640_dev *sensor)
/* Auto/manual gain */
ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN,
 0, 1, 1, 1);
ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
0, 1023, 1, 0);
[...]
v4l2_ctrl_auto_cluster(2, >auto_gain, 0, true);

By checking many other drivers that are using clustered auto controls, 
they are all doing that way:

ctrls->auto_x = v4l2_ctrl_new_std(CID_X_AUTO..
ctrls->x = v4l2_ctrl_new_std(CID_X..
v4l2_ctrl_auto_cluster(2, >auto, 0, true);

g_volatile_ctrl(ctrl)
   switch (ctrl->id) {
case CID_X_AUTO:
  ctrls->x->val = READ_REG()

> 
>   case V4L2_CID_GAIN:
>   val = ov5640_get_gain(sensor);
>   if (val < 0)
>   return val;
>   ctrl->val = val;
>   break;
> 
> 
>>  case V4L2_CID_EXPOSURE_AUTO:
>> -if (ctrl->val == V4L2_EXPOSURE_MANUAL)
>> -return 0;
>>  val = ov5640_get_exposure(sensor);
>>  if (val < 0)
>>  return val;
> 
> And same here.
> 

Best regards,
Hugues.

Re: [PATCH v2 5/5] media: ov5640: fix restore of last mode set

2018-08-16 Thread Hugues FRUCHET
Hi Jacopo,

On 08/16/2018 12:10 PM, jacopo mondi wrote:
> Hi Hugues,
>  thanks for the patch
> 
> On Mon, Aug 13, 2018 at 12:19:46PM +0200, Hugues Fruchet wrote:
>> Mode setting depends on last mode set, in particular
>> because of exposure calculation when downscale mode
>> change between subsampling and scaling.
>> At stream on the last mode was wrongly set to current mode,
>> so no change was detected and exposure calculation
>> was not made, fix this.
> 
> I actually see a different issue here...

Which problem do you have exactly, you got a VGA JPEG instead of a QVGA 
YUYV ?

> 
> The issue I see here depends on the format programmed through
> set_fmt() never being applied when using the sensor with a media
> controller equipped device (in this case an i.MX6 board) through
> capture sessions, and the not properly calculated exposure you see may
> be a consequence of this.
> 
> I'll try to write down what I see, with the help of some debug output.
> 
> - At probe time mode 640x460@30 is programmed:
>[1.651216] ov5640_probe: Initial mode with id: 2
> 
> - I set the format on the sensor's pad and it gets not applied but
>marked as pending as the sensor is powered off:
> 
>#media-ctl --set-v4l2 "'ov5640 2-003c':0[fmt:UYVY2X8/320x240 field:none]"
> [   65.611983] ov5640_set_fmt: NEW mode with id: 1 - PENDING

So here sensor->current_mode is set to <1>;//QVGA
and sensor->pending_mode_change is set to true;

> 
> - I start streaming with yavta, and the sensor receives a power on;
>this causes the 'initial' format to be re-programmed and the pending
>change to be ignored:
> 
>#yavta -c10 -n4 -f YUYV -s $320x240  -F"../frame-#.yuv" /dev/video4
> [   69.395018] ov5640_set_power:1805 - on
> [   69.431342] ov5640_restore_mode:1711
> [   69.996882] ov5640_set_mode: Apply mode with id: 0
> 
>The 'ov5640_set_mode()' call from 'ov5640_restore_mode()' clears the
>sensor->pending flag, discarding the newly requested format, for
>this reason, at s_stream() time, the pending flag is not set
>anymore.

OK but before clearing sensor->pending_mode_change, set_mode() is
loading registers corresponding to sensor->current_mode:
static int ov5640_set_mode(struct ov5640_dev *sensor,
   const struct ov5640_mode_info *orig_mode)
{
==> const struct ov5640_mode_info *mode = sensor->current_mode;
...
ret = ov5640_set_mode_direct(sensor, mode, exposure);

=> so mode <1> is expected to be set now, so I don't understand your trace:
"> [   69.996882] ov5640_set_mode: Apply mode with id: 0"
Which variable do you trace that shows "0" ?


> 
> Are you using a media-controller system? I suspect in non-mc cases,
> the set_fmt is applied through a single power_on/power_off session, not
> causing the 'restore_mode()' issue. Is this the case for you or your
> issue is differnt?
> 
> Edit:
> Mita-san tried to address the issue of the output pixel format not
> being restored when the image format was restored in
> 19ad26f9e6e1 ("media: ov5640: add missing output pixel format setting")
> 
> I understand the issue he tried to fix, but shouldn't the pending
> format (if any) be applied instead of the initial one unconditionally?

This is what does the ov5640_restore_mode(), set the current mode 
(sensor->current_mode), that is done through this line:
/* now restore the last capture mode */
ret = ov5640_set_mode(sensor, _mode_init_data);
=> note that the comment above is weird, in fact it is the "current" 
mode that is set.
=> note also that the 2nd parameter is not the mode to be set but the 
previously applied mode ! (ie loaded in ov5640 registers). This is used
to decide if we have to go to the "set_mode_exposure_calc" or 
"set_mode_direct".

the ov5640_restore_mode() also set the current pixel format 
(sensor->fmt), that is done through this line:
return ov5640_set_framefmt(sensor, >fmt);
==> This is what have fixed Mita-san, this line was missing previously, 
leading to "mode registers" being loaded but not the "pixel format 
registers".


PS: There are two other "set mode" related changes that are related to this:
1) 6949d864776e ("media: ov5640: do not change mode if format or frame 
interval is unchanged")
=> this is merged in media master, unfortunately I've introduced a 
regression on "pixel format" side that I've fixed in this patchset :
2) https://www.mail-archive.com/linux-media@vger.kernel.org/msg134413.html
Symptom was a noisy image when capturing QVGA YUV (in fact captured as 
JPEG data).


Best regards,
Hugues.

> 
> Thanks
>   

Re: [PATCH v2] media: ov5640: do not change mode if format or frame interval is unchanged

2018-08-16 Thread Hugues FRUCHET
Hi all,

Please ignore this v2, the v1 was merged.
I've just pushed a new patch which fixes the regression observed, see:
https://www.mail-archive.com/linux-media@vger.kernel.org/msg134413.html

Sorry for inconvenience.

Best regards,
Hugues.

On 08/13/2018 11:21 AM, Hugues Fruchet wrote:
> Save load of mode registers array when V4L2 client sets a format or a
> frame interval which selects the same mode than the current one.
> 
> Signed-off-by: Hugues Fruchet 
> ---
> Version 2:
>- Fix fuzzy image because of JPEG default format not being
>  changed according to new format selected, fix this.
>- Init sequence initialises format to YUV422 UYVY but
>  sensor->fmt initial value was set to JPEG, fix this.
> 
> 
>   drivers/media/i2c/ov5640.c | 33 -
>   1 file changed, 24 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> index 1ecbb7a..2ddd86d 100644
> --- a/drivers/media/i2c/ov5640.c
> +++ b/drivers/media/i2c/ov5640.c
> @@ -223,6 +223,7 @@ struct ov5640_dev {
>   int power_count;
>   
>   struct v4l2_mbus_framefmt fmt;
> + bool pending_fmt_change;
>   
>   const struct ov5640_mode_info *current_mode;
>   enum ov5640_frame_rate current_fr;
> @@ -255,7 +256,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct 
> v4l2_ctrl *ctrl)
>* should be identified and removed to speed register load time
>* over i2c.
>*/
> -
> +/* YUV422 UYVY VGA@30fps */
>   static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
>   {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
>   {0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0},
> @@ -1966,9 +1967,14 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
>   goto out;
>   }
>   
> - sensor->current_mode = new_mode;
> - sensor->fmt = *mbus_fmt;
> - sensor->pending_mode_change = true;
> + if (new_mode != sensor->current_mode) {
> + sensor->current_mode = new_mode;
> + sensor->pending_mode_change = true;
> + }
> + if (mbus_fmt->code != sensor->fmt.code) {
> + sensor->fmt = *mbus_fmt;
> + sensor->pending_fmt_change = true;
> + }
>   out:
>   mutex_unlock(>lock);
>   return ret;
> @@ -2508,8 +2514,10 @@ static int ov5640_s_frame_interval(struct v4l2_subdev 
> *sd,
>   goto out;
>   }
>   
> - sensor->current_mode = mode;
> - sensor->pending_mode_change = true;
> + if (mode != sensor->current_mode) {
> + sensor->current_mode = mode;
> + sensor->pending_mode_change = true;
> + }
>   out:
>   mutex_unlock(>lock);
>   return ret;
> @@ -2540,10 +2548,13 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, 
> int enable)
>   ret = ov5640_set_mode(sensor, sensor->current_mode);
>   if (ret)
>   goto out;
> + }
>   
> + if (enable && sensor->pending_fmt_change) {
>   ret = ov5640_set_framefmt(sensor, >fmt);
>   if (ret)
>   goto out;
> + sensor->pending_fmt_change = false;
>   }
>   
>   if (sensor->ep.bus_type == V4L2_MBUS_CSI2)
> @@ -2638,9 +2649,14 @@ static int ov5640_probe(struct i2c_client *client,
>   return -ENOMEM;
>   
>   sensor->i2c_client = client;
> +
> + /*
> +  * default init sequence initialize sensor to
> +  * YUV422 UYVY VGA@30fps
> +  */
>   fmt = >fmt;
> - fmt->code = ov5640_formats[0].code;
> - fmt->colorspace = ov5640_formats[0].colorspace;
> + fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
> + fmt->colorspace = V4L2_COLORSPACE_SRGB;
>   fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
>   fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
>   fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
> @@ -2652,7 +2668,6 @@ static int ov5640_probe(struct i2c_client *client,
>   sensor->current_fr = OV5640_30_FPS;
>   sensor->current_mode =
>   _mode_data[OV5640_30_FPS][OV5640_MODE_VGA_640_480];
> - sensor->pending_mode_change = true;
>   
>   sensor->ae_target = 52;
>   
> 

[PATCH] media: ov5640: fix mode change regression

2018-08-16 Thread Hugues Fruchet
fixes: 6949d864776e ("media: ov5640: do not change mode if format or frame 
interval is unchanged").

Symptom was fuzzy image because of JPEG default format
not being changed according to new format selected, fix this.
Init sequence initialises format to YUV422 UYVY but
sensor->fmt initial value was set to JPEG, fix this.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 21 -
 1 file changed, 16 insertions(+), 5 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 071f4bc..2ddd86d 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -223,6 +223,7 @@ struct ov5640_dev {
int power_count;
 
struct v4l2_mbus_framefmt fmt;
+   bool pending_fmt_change;
 
const struct ov5640_mode_info *current_mode;
enum ov5640_frame_rate current_fr;
@@ -255,7 +256,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct 
v4l2_ctrl *ctrl)
  * should be identified and removed to speed register load time
  * over i2c.
  */
-
+/* YUV422 UYVY VGA@30fps */
 static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
{0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
{0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0},
@@ -1968,9 +1969,12 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
 
if (new_mode != sensor->current_mode) {
sensor->current_mode = new_mode;
-   sensor->fmt = *mbus_fmt;
sensor->pending_mode_change = true;
}
+   if (mbus_fmt->code != sensor->fmt.code) {
+   sensor->fmt = *mbus_fmt;
+   sensor->pending_fmt_change = true;
+   }
 out:
mutex_unlock(>lock);
return ret;
@@ -2544,10 +2548,13 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int 
enable)
ret = ov5640_set_mode(sensor, sensor->current_mode);
if (ret)
goto out;
+   }
 
+   if (enable && sensor->pending_fmt_change) {
ret = ov5640_set_framefmt(sensor, >fmt);
if (ret)
goto out;
+   sensor->pending_fmt_change = false;
}
 
if (sensor->ep.bus_type == V4L2_MBUS_CSI2)
@@ -2642,9 +2649,14 @@ static int ov5640_probe(struct i2c_client *client,
return -ENOMEM;
 
sensor->i2c_client = client;
+
+   /*
+* default init sequence initialize sensor to
+* YUV422 UYVY VGA@30fps
+*/
fmt = >fmt;
-   fmt->code = ov5640_formats[0].code;
-   fmt->colorspace = ov5640_formats[0].colorspace;
+   fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
+   fmt->colorspace = V4L2_COLORSPACE_SRGB;
fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
@@ -2656,7 +2668,6 @@ static int ov5640_probe(struct i2c_client *client,
sensor->current_fr = OV5640_30_FPS;
sensor->current_mode =
_mode_data[OV5640_30_FPS][OV5640_MODE_VGA_640_480];
-   sensor->pending_mode_change = true;
 
sensor->ae_target = 52;
 
-- 
2.7.4



[PATCH v2 1/5] media: ov5640: fix exposure regression

2018-08-13 Thread Hugues Fruchet
fixes: bf4a4b518c20 ("media: ov5640: Don't force the auto exposure state at 
start time").

Symptom was black image when capturing HD or 5Mp picture
due to manual exposure set to 1 while it was intended to
set autoexposure to "manual", fix this.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 18 --
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 1ecbb7a..4b9da8b 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -938,6 +938,12 @@ static int ov5640_load_regs(struct ov5640_dev *sensor,
return ret;
 }
 
+static int ov5640_set_autoexposure(struct ov5640_dev *sensor, bool on)
+{
+   return ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL,
+ BIT(0), on ? 0 : BIT(0));
+}
+
 /* read exposure, in number of line periods */
 static int ov5640_get_exposure(struct ov5640_dev *sensor)
 {
@@ -1593,7 +1599,7 @@ static int ov5640_set_mode_exposure_calc(struct 
ov5640_dev *sensor,
  */
 static int ov5640_set_mode_direct(struct ov5640_dev *sensor,
  const struct ov5640_mode_info *mode,
- s32 exposure)
+ bool auto_exp)
 {
int ret;
 
@@ -1610,7 +1616,8 @@ static int ov5640_set_mode_direct(struct ov5640_dev 
*sensor,
if (ret)
return ret;
 
-   return __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, exposure);
+   return __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, auto_exp ?
+ V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL);
 }
 
 static int ov5640_set_mode(struct ov5640_dev *sensor,
@@ -1618,7 +1625,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
 {
const struct ov5640_mode_info *mode = sensor->current_mode;
enum ov5640_downsize_mode dn_mode, orig_dn_mode;
-   s32 exposure;
+   bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
int ret;
 
dn_mode = mode->dn_mode;
@@ -1629,8 +1636,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
if (ret)
return ret;
 
-   exposure = sensor->ctrls.auto_exp->val;
-   ret = ov5640_set_exposure(sensor, V4L2_EXPOSURE_MANUAL);
+   ret = ov5640_set_autoexposure(sensor, false);
if (ret)
return ret;
 
@@ -1646,7 +1652,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
 * change inside subsampling or scaling
 * download firmware directly
 */
-   ret = ov5640_set_mode_direct(sensor, mode, exposure);
+   ret = ov5640_set_mode_direct(sensor, mode, auto_exp);
}
 
if (ret < 0)
-- 
2.7.4



[PATCH v2 0/5] Fix OV5640 exposure & gain

2018-08-13 Thread Hugues Fruchet
This patch serie fixes some problems around exposure & gain in OV5640 driver.

The 4th patch about autocontrols requires also a fix in v4l2-ctrls.c:
https://www.mail-archive.com/linux-media@vger.kernel.org/msg133164.html

Here is the test procedure used for exposure & gain controls check:
1) Preview in background
$> gst-launch-1.0 v4l2src ! "video/x-raw, width=640, Height=480" ! queue ! 
waylandsink -e &
2) Check gain & exposure values
$> v4l2-ctl --all | grep -e exposure -e gain | grep "(int)"
   exposure (int): min=0 max=65535 step=1 default=0 
value=330 flags=inactive, volatile
   gain (int): min=0 max=1023 step=1 default=0 
value=19 flags=inactive, volatile
3) Put finger in front of camera and check that gain/exposure values are 
changing:
$> v4l2-ctl --all | grep -e exposure -e gain | grep "(int)"
   exposure (int): min=0 max=65535 step=1 default=0 
value=660 flags=inactive, volatile
   gain (int): min=0 max=1023 step=1 default=0 
value=37 flags=inactive, volatile
4) switch to manual mode, image exposition must not change
$> v4l2-ctl --set-ctrl=gain_automatic=0
$> v4l2-ctl --set-ctrl=auto_exposure=1
Note the "1" for manual exposure.

5) Check current gain/exposure values:
$> v4l2-ctl --all | grep -e exposure -e gain | grep "(int)"
   exposure (int): min=0 max=65535 step=1 default=0 
value=330
   gain (int): min=0 max=1023 step=1 default=0 
value=20

6) Put finger behind camera and check that gain/exposure values are NOT 
changing:
$> v4l2-ctl --all | grep -e exposure -e gain | grep "(int)"
   exposure (int): min=0 max=65535 step=1 default=0 
value=330
   gain (int): min=0 max=1023 step=1 default=0 
value=20
7) Update exposure, check that it is well changed on display and that same 
value is returned:
$> v4l2-ctl --set-ctrl=exposure=100
$> v4l2-ctl --get-ctrl=exposure
exposure: 100

9) Update gain, check that it is well changed on display and that same value is 
returned:
$> v4l2-ctl --set-ctrl=gain=10
$> v4l2-ctl --get-ctrl=gain
gain: 10

10) Switch back to auto gain/exposure, verify that image is correct and values 
returned are correct:
$> v4l2-ctl --set-ctrl=gain_automatic=1
$> v4l2-ctl --set-ctrl=auto_exposure=0
$> v4l2-ctl --all | grep -e exposure -e gain | grep "(int)"
   exposure (int): min=0 max=65535 step=1 default=0 
value=330 flags=inactive, volatile
   gain (int): min=0 max=1023 step=1 default=0 
value=22 flags=inactive, volatile
Note the "0" for auto exposure.

===
= history =
===
version 2:
  - Fix patch 3/5 commit comment and rename binning function as per jacopo' 
suggestion:
https://www.mail-archive.com/linux-media@vger.kernel.org/msg133272.html

Hugues Fruchet (5):
  media: ov5640: fix exposure regression
  media: ov5640: fix auto gain & exposure when changing mode
  media: ov5640: fix wrong binning value in exposure calculation
  media: ov5640: fix auto controls values when switching to manual mode
  media: ov5640: fix restore of last mode set

 drivers/media/i2c/ov5640.c | 124 ++---
 1 file changed, 72 insertions(+), 52 deletions(-)

-- 
2.7.4



[PATCH v2 3/5] media: ov5640: fix wrong binning value in exposure calculation

2018-08-13 Thread Hugues Fruchet
ov5640_set_mode_exposure_calc() is checking binning value but
binning value read is buggy, fix this.
Rename ov5640_binning_on() to ov5640_get_binning() as per other
similar functions.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 8 
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 7c569de..9fb17b5 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1349,7 +1349,7 @@ static int ov5640_set_ae_target(struct ov5640_dev 
*sensor, int target)
return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1F, fast_low);
 }
 
-static int ov5640_binning_on(struct ov5640_dev *sensor)
+static int ov5640_get_binning(struct ov5640_dev *sensor)
 {
u8 temp;
int ret;
@@ -1357,8 +1357,8 @@ static int ov5640_binning_on(struct ov5640_dev *sensor)
ret = ov5640_read_reg(sensor, OV5640_REG_TIMING_TC_REG21, );
if (ret)
return ret;
-   temp &= 0xfe;
-   return temp ? 1 : 0;
+
+   return temp & BIT(0);
 }
 
 static int ov5640_set_binning(struct ov5640_dev *sensor, bool enable)
@@ -1468,7 +1468,7 @@ static int ov5640_set_mode_exposure_calc(struct 
ov5640_dev *sensor,
if (ret < 0)
return ret;
prev_shutter = ret;
-   ret = ov5640_binning_on(sensor);
+   ret = ov5640_get_binning(sensor);
if (ret < 0)
return ret;
if (ret && mode->id != OV5640_MODE_720P_1280_720 &&
-- 
2.7.4



[PATCH v2 4/5] media: ov5640: fix auto controls values when switching to manual mode

2018-08-13 Thread Hugues Fruchet
When switching from auto to manual mode, V4L2 core is calling
g_volatile_ctrl() in manual mode in order to get the manual initial value.
Remove the manual mode check/return to not break this behaviour.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 4 
 1 file changed, 4 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 9fb17b5..c110a6a 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -2277,16 +2277,12 @@ static int ov5640_g_volatile_ctrl(struct v4l2_ctrl 
*ctrl)
 
switch (ctrl->id) {
case V4L2_CID_AUTOGAIN:
-   if (!ctrl->val)
-   return 0;
val = ov5640_get_gain(sensor);
if (val < 0)
return val;
sensor->ctrls.gain->val = val;
break;
case V4L2_CID_EXPOSURE_AUTO:
-   if (ctrl->val == V4L2_EXPOSURE_MANUAL)
-   return 0;
val = ov5640_get_exposure(sensor);
if (val < 0)
return val;
-- 
2.7.4



[PATCH v2 2/5] media: ov5640: fix auto gain & exposure when changing mode

2018-08-13 Thread Hugues Fruchet
Ensure that auto gain and auto exposure are well restored
when changing mode.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 96 ++
 1 file changed, 54 insertions(+), 42 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 4b9da8b..7c569de 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1000,6 +1000,18 @@ static int ov5640_get_gain(struct ov5640_dev *sensor)
return gain & 0x3ff;
 }
 
+static int ov5640_set_gain(struct ov5640_dev *sensor, int gain)
+{
+   return ov5640_write_reg16(sensor, OV5640_REG_AEC_PK_REAL_GAIN,
+ (u16)gain & 0x3ff);
+}
+
+static int ov5640_set_autogain(struct ov5640_dev *sensor, bool on)
+{
+   return ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL,
+ BIT(1), on ? 0 : BIT(1));
+}
+
 static int ov5640_set_stream_dvp(struct ov5640_dev *sensor, bool on)
 {
int ret;
@@ -1577,7 +1589,7 @@ static int ov5640_set_mode_exposure_calc(struct 
ov5640_dev *sensor,
}
 
/* set capture gain */
-   ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.gain, cap_gain16);
+   ret = ov5640_set_gain(sensor, cap_gain16);
if (ret)
return ret;
 
@@ -1590,7 +1602,7 @@ static int ov5640_set_mode_exposure_calc(struct 
ov5640_dev *sensor,
}
 
/* set exposure */
-   return __v4l2_ctrl_s_ctrl(sensor->ctrls.exposure, cap_shutter);
+   return ov5640_set_exposure(sensor, cap_shutter);
 }
 
 /*
@@ -1598,26 +1610,13 @@ static int ov5640_set_mode_exposure_calc(struct 
ov5640_dev *sensor,
  * change mode directly
  */
 static int ov5640_set_mode_direct(struct ov5640_dev *sensor,
- const struct ov5640_mode_info *mode,
- bool auto_exp)
+ const struct ov5640_mode_info *mode)
 {
-   int ret;
-
if (!mode->reg_data)
return -EINVAL;
 
/* Write capture setting */
-   ret = ov5640_load_regs(sensor, mode);
-   if (ret < 0)
-   return ret;
-
-   /* turn auto gain/exposure back on for direct mode */
-   ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_gain, 1);
-   if (ret)
-   return ret;
-
-   return __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, auto_exp ?
- V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL);
+   return ov5640_load_regs(sensor, mode);
 }
 
 static int ov5640_set_mode(struct ov5640_dev *sensor,
@@ -1625,6 +1624,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
 {
const struct ov5640_mode_info *mode = sensor->current_mode;
enum ov5640_downsize_mode dn_mode, orig_dn_mode;
+   bool auto_gain = sensor->ctrls.auto_gain->val == 1;
bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
int ret;
 
@@ -1632,19 +1632,23 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
orig_dn_mode = orig_mode->dn_mode;
 
/* auto gain and exposure must be turned off when changing modes */
-   ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_gain, 0);
-   if (ret)
-   return ret;
+   if (auto_gain) {
+   ret = ov5640_set_autogain(sensor, false);
+   if (ret)
+   return ret;
+   }
 
-   ret = ov5640_set_autoexposure(sensor, false);
-   if (ret)
-   return ret;
+   if (auto_exp) {
+   ret = ov5640_set_autoexposure(sensor, false);
+   if (ret)
+   goto restore_auto_gain;
+   }
 
if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
(dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
/*
 * change between subsampling and scaling
-* go through exposure calucation
+* go through exposure calculation
 */
ret = ov5640_set_mode_exposure_calc(sensor, mode);
} else {
@@ -1652,11 +1656,16 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
 * change inside subsampling or scaling
 * download firmware directly
 */
-   ret = ov5640_set_mode_direct(sensor, mode, auto_exp);
+   ret = ov5640_set_mode_direct(sensor, mode);
}
-
if (ret < 0)
-   return ret;
+   goto restore_auto_exp_gain;
+
+   /* restore auto gain and exposure */
+   if (auto_gain)
+   ov5640_set_autogain(sensor, true);
+   if (auto_exp)
+   ov5640_set_autoexposure(sensor, true);
 
ret = ov5640_set_timings(sensor, mode);
if (ret < 0)
@@ -1681,6 +1690,15 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
sensor->pending_mode_change = f

[PATCH v2 5/5] media: ov5640: fix restore of last mode set

2018-08-13 Thread Hugues Fruchet
Mode setting depends on last mode set, in particular
because of exposure calculation when downscale mode
change between subsampling and scaling.
At stream on the last mode was wrongly set to current mode,
so no change was detected and exposure calculation
was not made, fix this.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 8 +++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index c110a6a..923cc30 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -225,6 +225,7 @@ struct ov5640_dev {
struct v4l2_mbus_framefmt fmt;
 
const struct ov5640_mode_info *current_mode;
+   const struct ov5640_mode_info *last_mode;
enum ov5640_frame_rate current_fr;
struct v4l2_fract frame_interval;
 
@@ -1628,6 +1629,9 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
int ret;
 
+   if (!orig_mode)
+   orig_mode = mode;
+
dn_mode = mode->dn_mode;
orig_dn_mode = orig_mode->dn_mode;
 
@@ -1688,6 +1692,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
return ret;
 
sensor->pending_mode_change = false;
+   sensor->last_mode = mode;
 
return 0;
 
@@ -2551,7 +2556,8 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int 
enable)
 
if (sensor->streaming == !enable) {
if (enable && sensor->pending_mode_change) {
-   ret = ov5640_set_mode(sensor, sensor->current_mode);
+   ret = ov5640_set_mode(sensor, sensor->last_mode);
+
if (ret)
goto out;
 
-- 
2.7.4



[PATCH v2] media: ov5640: do not change mode if format or frame interval is unchanged

2018-08-13 Thread Hugues Fruchet
Save load of mode registers array when V4L2 client sets a format or a
frame interval which selects the same mode than the current one.

Signed-off-by: Hugues Fruchet 
---
Version 2:
  - Fix fuzzy image because of JPEG default format not being
changed according to new format selected, fix this.
  - Init sequence initialises format to YUV422 UYVY but
sensor->fmt initial value was set to JPEG, fix this.


 drivers/media/i2c/ov5640.c | 33 -
 1 file changed, 24 insertions(+), 9 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 1ecbb7a..2ddd86d 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -223,6 +223,7 @@ struct ov5640_dev {
int power_count;
 
struct v4l2_mbus_framefmt fmt;
+   bool pending_fmt_change;
 
const struct ov5640_mode_info *current_mode;
enum ov5640_frame_rate current_fr;
@@ -255,7 +256,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct 
v4l2_ctrl *ctrl)
  * should be identified and removed to speed register load time
  * over i2c.
  */
-
+/* YUV422 UYVY VGA@30fps */
 static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
{0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},
{0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0},
@@ -1966,9 +1967,14 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
goto out;
}
 
-   sensor->current_mode = new_mode;
-   sensor->fmt = *mbus_fmt;
-   sensor->pending_mode_change = true;
+   if (new_mode != sensor->current_mode) {
+   sensor->current_mode = new_mode;
+   sensor->pending_mode_change = true;
+   }
+   if (mbus_fmt->code != sensor->fmt.code) {
+   sensor->fmt = *mbus_fmt;
+   sensor->pending_fmt_change = true;
+   }
 out:
mutex_unlock(>lock);
return ret;
@@ -2508,8 +2514,10 @@ static int ov5640_s_frame_interval(struct v4l2_subdev 
*sd,
goto out;
}
 
-   sensor->current_mode = mode;
-   sensor->pending_mode_change = true;
+   if (mode != sensor->current_mode) {
+   sensor->current_mode = mode;
+   sensor->pending_mode_change = true;
+   }
 out:
mutex_unlock(>lock);
return ret;
@@ -2540,10 +2548,13 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int 
enable)
ret = ov5640_set_mode(sensor, sensor->current_mode);
if (ret)
goto out;
+   }
 
+   if (enable && sensor->pending_fmt_change) {
ret = ov5640_set_framefmt(sensor, >fmt);
if (ret)
goto out;
+   sensor->pending_fmt_change = false;
}
 
if (sensor->ep.bus_type == V4L2_MBUS_CSI2)
@@ -2638,9 +2649,14 @@ static int ov5640_probe(struct i2c_client *client,
return -ENOMEM;
 
sensor->i2c_client = client;
+
+   /*
+* default init sequence initialize sensor to
+* YUV422 UYVY VGA@30fps
+*/
fmt = >fmt;
-   fmt->code = ov5640_formats[0].code;
-   fmt->colorspace = ov5640_formats[0].colorspace;
+   fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
+   fmt->colorspace = V4L2_COLORSPACE_SRGB;
fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
@@ -2652,7 +2668,6 @@ static int ov5640_probe(struct i2c_client *client,
sensor->current_fr = OV5640_30_FPS;
sensor->current_mode =
_mode_data[OV5640_30_FPS][OV5640_MODE_VGA_640_480];
-   sensor->pending_mode_change = true;
 
sensor->ae_target = 52;
 
-- 
2.7.4



Re: [PATCH 1/2] media: ov5640: Fix timings setup code

2018-08-07 Thread Hugues FRUCHET
Hi Jacopo,

Thanks for this patch, when testing on my side I don't see special 
regression or enhancement with that fix, particularly on image quality 
(exposure, ...),
do you have a test procedure that underline the issue ?
For example on my side when I do 5Mp then QVGA capture, pictures are 
overexposed/greenish:
v4l2-ctl --set-parm=15; v4l2-ctl 
--set-fmt-video=width=2592,height=1944,pixelformat=JPEG --stream-mmap 
--stream-count=1 --stream-to=pic-5Mp.jpeg; v4l2-ctl 
--set-parm=30;weston-image pic-5Mp.jpeg &
v4l2-ctl --set-parm=15; v4l2-ctl 
--set-fmt-video=width=320,height=240,pixelformat=JPEG --stream-mmap 
--stream-count=1 --stream-to=pic-QVGA.jpeg; v4l2-ctl 
--set-parm=30;weston-image pic-QVGA.jpeg &

If I skip the first frames, images captured are OK:

v4l2-ctl --set-parm=15; v4l2-ctl 
--set-fmt-video=width=2592,height=1944,pixelformat=JPEG --stream-mmap 
--stream-count=1 --stream-skip=1 --stream-to=pic-5Mp.jpeg; v4l2-ctl 
--set-parm=30;weston-image pic-5Mp.jpeg &
v4l2-ctl --set-parm=15; v4l2-ctl 
--set-fmt-video=width=320,height=240,pixelformat=JPEG --stream-mmap 
--stream-count=1 --stream-skip=1 --stream-to=pic-QVGA.jpeg; v4l2-ctl 
--set-parm=30;weston-image pic-QVGA.jpeg &


Best regards,
Hugues.

On 07/18/2018 01:19 PM, Jacopo Mondi wrote:
> As of:
> commit 476dec012f4c ("media: ov5640: Add horizontal and vertical totals")
> the timings parameters gets programmed separately from the static register
> values array.
> 
> When changing capture mode, the vertical and horizontal totals gets inspected
> by the set_mode_exposure_calc() functions, and only later programmed with the
> new values. This means exposure, light banding filter and shutter gain are
> calculated using the previous timings, and are thus not correct.
> 
> Fix this by programming timings right after the static register value table
> has been sent to the sensor in the ov5640_load_regs() function.
> 
> Fixes: 476dec012f4c ("media: ov5640: Add horizontal and vertical totals")
> Signed-off-by: Samuel Bobrowicz 
> Signed-off-by: Maxime Ripard 
> Signed-off-by: Jacopo Mondi 
> 
> ---
> This fix has been circulating around for quite some time now, in Maxime clock
> tree patches, in Sam dropbox patches and in my latest MIPI fixes patches.
> While the rest of the series have not yet been accepted, there is general
> consensus this is an actual fix that has to be collected.
> 
> I've slightly modified Sam's and Maxime's version I previously sent,
> programming timings directly in ov5640_load_regs() function.
> You can find Sam's previous version here:
> https://www.mail-archive.com/linux-media@vger.kernel.org/msg131654.html
> and mine here, with an additional change that aimed to fix MIPI mode, which
> I've left out in this version:
> https://www.mail-archive.com/linux-media@vger.kernel.org/msg133422.html
> 
> Sam, Maxime, I took the liberty of taking your Signed-off-by from the previous
> patch, as this was spotted by you first. Is this ok with you?
> 
> Thanks
> j
> ---
> ---
>   drivers/media/i2c/ov5640.c | 50 
> +++---
>   1 file changed, 21 insertions(+), 29 deletions(-)
> 
> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> index 1ecbb7a..12b3496 100644
> --- a/drivers/media/i2c/ov5640.c
> +++ b/drivers/media/i2c/ov5640.c
> @@ -908,6 +908,26 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 
> reg,
>   }
>   
>   /* download ov5640 settings to sensor through i2c */
> +static int ov5640_set_timings(struct ov5640_dev *sensor,
> +   const struct ov5640_mode_info *mode)
> +{
> + int ret;
> +
> + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact);
> + if (ret < 0)
> + return ret;
> +
> + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->vact);
> + if (ret < 0)
> + return ret;
> +
> + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, mode->htot);
> + if (ret < 0)
> + return ret;
> +
> + return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, mode->vtot);
> +}
> +
>   static int ov5640_load_regs(struct ov5640_dev *sensor,
>   const struct ov5640_mode_info *mode)
>   {
> @@ -935,7 +955,7 @@ static int ov5640_load_regs(struct ov5640_dev *sensor,
>   usleep_range(1000 * delay_ms, 1000 * delay_ms + 100);
>   }
>   
> - return ret;
> + return ov5640_set_timings(sensor, mode);
>   }
>   
>   /* read exposure, in number of line periods */
> @@ -1385,30 +1405,6 @@ static int ov5640_set_virtual_channel(struct 
> ov5640_dev *sensor)
>   return ov5640_write_reg(sensor, OV5640_REG_DEBUG_MODE, temp);
>   }
>   
> -static int ov5640_set_timings(struct ov5640_dev *sensor,
> -   const struct ov5640_mode_info *mode)
> -{
> - int ret;
> -
> - ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact);
> - if (ret < 0)
> - return ret;
> -

Re: [PATCH 2/2] media: ov5640: Fix auto-exposure disabling

2018-08-07 Thread Hugues FRUCHET
Hi Jacopo,

In serie "[PATCH 0/5] Fix OV5640 exposure & gain"
https://www.mail-archive.com/linux-media@vger.kernel.org/msg133269.html
I've tried to collect fixes around exposure/gain, not only the exposure 
regression and I would prefer to keep it consistent with the associated 
procedure test.
Moreover I dislike the internal use of control framework functions to 
disable/enable exposure/gain, on my opinion this has to be kept simpler
by just disabling/enabling the right registers.
Would it be possible that you test my 5 patches serie on your side ?

Best regards,
Hugues.

On 07/18/2018 03:04 PM, jacopo mondi wrote:
> Hi again,
> 
> On Wed, Jul 18, 2018 at 01:19:03PM +0200, Jacopo Mondi wrote:
>> As of:
>> commit bf4a4b518c20 ("media: ov5640: Don't force the auto exposure state at
>> start time") auto-exposure got disabled before programming new capture modes 
>> to
>> the sensor. Unfortunately the function used to do that 
>> (ov5640_set_exposure())
>> does not enable/disable auto-exposure engine through register 0x3503[0] bit, 
>> but
>> programs registers [0x3500 - 0x3502] which represent the desired exposure 
>> time
>> when running with manual exposure. As a result, auto-exposure was not 
>> actually
>> disabled at all.
>>
>> To actually disable auto-exposure, go through the control framework instead 
>> of
>> calling ov5640_set_exposure() function directly.
>>
>> Also, as auto-gain and auto-exposure are disabled un-conditionally but only
>> restored to their previous values in ov5640_set_mode_direct() function, move
>> controls restoring so that their value is re-programmed opportunely after
>> either ov5640_set_mode_direct() or ov5640_set_mode_exposure_calc() have been
>> executed.
>>
>> Fixes: bf4a4b518c20 ("media: ov5640: Don't force the auto exposure state at 
>> start time")
>> Signed-off-by: Jacopo Mondi 
>>
>> ---
>> Is it worth doing with auto-gain what we're doing with auto-exposure? Cache 
>> the
>> value and then re-program it instead of unconditionally disable/enable it?
> 
> I have missed this patch from Hugues that address almost the same
> issue
> https://www.mail-archive.com/linux-media@vger.kernel.org/msg133264.html
> 
> I feel this new one is simpler, and unless we want to avoid going
> through the control framework, it is not worth adding new functions to
> handle auto-exposure as Hugues' patch is doing.
> 
> Hugues, do you have comments? Feel free to add your sob or rb tags if
> you like to.
> 
> Thanks
> j
> 
>>
>> Thanks
>>j
>> ---
>> ---
>>   drivers/media/i2c/ov5640.c | 29 +
>>   1 file changed, 13 insertions(+), 16 deletions(-)
>>
>> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
>> index 12b3496..bc75cb7 100644
>> --- a/drivers/media/i2c/ov5640.c
>> +++ b/drivers/media/i2c/ov5640.c
>> @@ -1588,25 +1588,13 @@ static int ov5640_set_mode_exposure_calc(struct 
>> ov5640_dev *sensor,
>>* change mode directly
>>*/
>>   static int ov5640_set_mode_direct(struct ov5640_dev *sensor,
>> -  const struct ov5640_mode_info *mode,
>> -  s32 exposure)
>> +  const struct ov5640_mode_info *mode)
>>   {
>> -int ret;
>> -
>>  if (!mode->reg_data)
>>  return -EINVAL;
>>
>>  /* Write capture setting */
>> -ret = ov5640_load_regs(sensor, mode);
>> -if (ret < 0)
>> -return ret;
>> -
>> -/* turn auto gain/exposure back on for direct mode */
>> -ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_gain, 1);
>> -if (ret)
>> -return ret;
>> -
>> -return __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, exposure);
>> +return  ov5640_load_regs(sensor, mode);
>>   }
>>
>>   static int ov5640_set_mode(struct ov5640_dev *sensor,
>> @@ -1626,7 +1614,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
>>  return ret;
>>
>>  exposure = sensor->ctrls.auto_exp->val;
>> -ret = ov5640_set_exposure(sensor, V4L2_EXPOSURE_MANUAL);
>> +ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, V4L2_EXPOSURE_MANUAL);
>>  if (ret)
>>  return ret;
>>
>> @@ -1642,12 +1630,21 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
>>   * change inside subsampling or scaling
>>   * download firmware directly
>>   */
>> -ret = ov5640_set_mode_direct(sensor, mode, exposure);
>> +ret = ov5640_set_mode_direct(sensor, mode);
>>  }
>>
>>  if (ret < 0)
>>  return ret;
>>
>> +/* Restore auto-gain and auto-exposure after mode has changed. */
>> +ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_gain, 1);
>> +if (ret)
>> +return ret;
>> +
>> +ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, exposure)
>> +if (ret)
>> +return ret;
>> +
>>  ret = ov5640_set_binning(sensor, dn_mode != SCALING);
>>  if (ret < 0)
>>  return ret;
>> --
>> 2.7.4
>>

Re: [PATCH 3/5] media: ov5640: fix wrong binning value in exposure calculation

2018-07-04 Thread Hugues FRUCHET
Hi Jacopo,

Many thanks for you valuable comments, I hardly understand this exposure 
code, and still some wrongly exposed images are observed switching from 
subsampling to scaling modes.
Steve, do you have more insight to share with us on this code ?

On 07/04/2018 04:38 PM, jacopo mondi wrote:
> Hi Hugues,
> 
> On Wed, Jul 04, 2018 at 02:58:41PM +0200, Hugues Fruchet wrote:
>> ov5640_set_mode_exposure_calc() is checking binning value but
>> binning value read is buggy and binning value set is done
>> after calling ov5640_set_mode_exposure_calc(), fix all of this.
> 
> The ov5640_binning_on() function was indeed wrong (side note: that
> name is confusing, it should be 0v5640_get_binning() to comply with
> others..) and always returned 0, but I don't see a fix here for the
> second part of the issue.
Mistake from me here, I should have removed "and binning value set is 
done after calling ov5640_set_mode_exposure_calc()" in commit message.

> In facts, during the lenghty exposure
> calculation process, binning is checked to decide if the preview
> shutter time should be doubled or not
> 
> static int ov5640_set_mode_exposure_calc(struct ov5640_dev *sensor,
>const struct ov5640_mode_info *mode)
> {
>  ...
> 
>   /* read preview shutter */
>   ret = ov5640_get_exposure(sensor);
>   if (ret < 0)
>   return ret;
>   prev_shutter = ret;
>   ret = ov5640_binning_on(sensor);
>   if (ret < 0)
>   return ret;
>   if (ret && mode->id != OV5640_MODE_720P_1280_720 &&
>   mode->id != OV5640_MODE_1080P_1920_1080)
>   prev_shutter *= 2;
>  ...
> }
> 
> My understanding is that reading the value from the register returns
> the binning settings for the previously configured mode, while the > binning 
> value is later updated for the current mode in
> ov5640_set_mode(), after 'ov5640_set_mode_exposure_calc()' has already
> been called. Is this ok?

This is also my understanding.

> 
> Also, I assume the code checks for mode->id to figure out if the mode
> uses subsampling or scaling. Be aware that for 1280x720 mode, the
> selected scaling mode depends on the FPS, not only on the mode id as
> it is assumed here.

This is not what I understand from this array:
static const struct ov5640_mode_info
ov5640_mode_data[OV5640_NUM_FRAMERATES][OV5640_NUM_MODES] = {
[15fps]
{OV5640_MODE_720P_1280_720, SUBSAMPLING,
 1280, 1892, 720, 740,
 ov5640_setting_15fps_720P_1280_720,
 ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)},
[30fps]
{OV5640_MODE_720P_1280_720, SUBSAMPLING,
 1280, 1892, 720, 740,
 ov5640_setting_30fps_720P_1280_720,
 ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)},

=> both modes uses subsampling here

> 
> A final note, the 'ov5640_set_mode_exposure_calc()' also writes VTS to
> update the shutter time to the newly calculated value.
> 
>   /* write capture shutter */
>   if (cap_shutter > (cap_vts - 4)) {
>   cap_vts = cap_shutter + 4;
>   ret = ov5640_set_vts(sensor, cap_vts);
>   if (ret < 0)
>   return ret;
>   }
> 
> Be aware again that VTS is later restored to the mode->vtot value by
> the 'ov5640_set_timings()' functions, which again, is called later
> than 'ov5640_set_mode_exposure_calc()'.
> 
> Wouldn't it be better to postpone exposure calculation after timings
> and binnings have been set ?

As said, I'm new on all of this but I can give it a try.

> 
> Thanks
> j
> 
>>
>> Signed-off-by: Hugues Fruchet 
>> ---
>>   drivers/media/i2c/ov5640.c | 4 ++--
>>   1 file changed, 2 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
>> index 7c569de..f9b256e 100644
>> --- a/drivers/media/i2c/ov5640.c
>> +++ b/drivers/media/i2c/ov5640.c
>> @@ -1357,8 +1357,8 @@ static int ov5640_binning_on(struct ov5640_dev *sensor)
>>  ret = ov5640_read_reg(sensor, OV5640_REG_TIMING_TC_REG21, );
>>  if (ret)
>>  return ret;
>> -temp &= 0xfe;
>> -return temp ? 1 : 0;
>> +
>> +return temp & BIT(0);
>>   }
>>
>>   static int ov5640_set_binning(struct ov5640_dev *sensor, bool enable)
>> --
>> 1.9.1
>>

Best regards,
Hugues.

[PATCH] media: ov5640: do not change mode if format or frame interval is unchanged

2018-07-04 Thread Hugues Fruchet
Save load of mode registers array when V4L2 client sets a format or a
frame interval which selects the same mode than the current one.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 14 +-
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 1ecbb7a..071f4bc 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1966,9 +1966,11 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
goto out;
}
 
-   sensor->current_mode = new_mode;
-   sensor->fmt = *mbus_fmt;
-   sensor->pending_mode_change = true;
+   if (new_mode != sensor->current_mode) {
+   sensor->current_mode = new_mode;
+   sensor->fmt = *mbus_fmt;
+   sensor->pending_mode_change = true;
+   }
 out:
mutex_unlock(>lock);
return ret;
@@ -2508,8 +2510,10 @@ static int ov5640_s_frame_interval(struct v4l2_subdev 
*sd,
goto out;
}
 
-   sensor->current_mode = mode;
-   sensor->pending_mode_change = true;
+   if (mode != sensor->current_mode) {
+   sensor->current_mode = mode;
+   sensor->pending_mode_change = true;
+   }
 out:
mutex_unlock(>lock);
return ret;
-- 
1.9.1



[PATCH 4/5] media: ov5640: fix auto controls values when switching to manual mode

2018-07-04 Thread Hugues Fruchet
When switching from auto to manual mode, V4L2 core is calling
g_volatile_ctrl() in manual mode in order to get the manual initial value.
Remove the manual mode check/return to not break this behaviour.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 4 
 1 file changed, 4 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index f9b256e..a307f1e 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -2277,16 +2277,12 @@ static int ov5640_g_volatile_ctrl(struct v4l2_ctrl 
*ctrl)
 
switch (ctrl->id) {
case V4L2_CID_AUTOGAIN:
-   if (!ctrl->val)
-   return 0;
val = ov5640_get_gain(sensor);
if (val < 0)
return val;
sensor->ctrls.gain->val = val;
break;
case V4L2_CID_EXPOSURE_AUTO:
-   if (ctrl->val == V4L2_EXPOSURE_MANUAL)
-   return 0;
val = ov5640_get_exposure(sensor);
if (val < 0)
return val;
-- 
1.9.1



[PATCH 3/5] media: ov5640: fix wrong binning value in exposure calculation

2018-07-04 Thread Hugues Fruchet
ov5640_set_mode_exposure_calc() is checking binning value but
binning value read is buggy and binning value set is done
after calling ov5640_set_mode_exposure_calc(), fix all of this.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 7c569de..f9b256e 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1357,8 +1357,8 @@ static int ov5640_binning_on(struct ov5640_dev *sensor)
ret = ov5640_read_reg(sensor, OV5640_REG_TIMING_TC_REG21, );
if (ret)
return ret;
-   temp &= 0xfe;
-   return temp ? 1 : 0;
+
+   return temp & BIT(0);
 }
 
 static int ov5640_set_binning(struct ov5640_dev *sensor, bool enable)
-- 
1.9.1



[PATCH 0/5] Fix OV5640 exposure & gain

2018-07-04 Thread Hugues Fruchet
This patch serie fixes some problems around exposure & gain in OV5640 driver.

The 4th patch about autocontrols requires also a fix in v4l2-ctrls.c:
https://www.mail-archive.com/linux-media@vger.kernel.org/msg133164.html

Here is the test procedure used for exposure & gain controls check:
1) Preview in background
$> gst-launch-1.0 v4l2src ! "video/x-raw, width=640, Height=480" ! queue ! 
waylandsink -e &
2) Check gain & exposure values
$> v4l2-ctl --all | grep -e exposure -e gain | grep "(int)"
   exposure (int): min=0 max=65535 step=1 default=0 
value=330 flags=inactive, volatile
   gain (int): min=0 max=1023 step=1 default=0 
value=19 flags=inactive, volatile
3) Put finger in front of camera and check that gain/exposure values are 
changing:
$> v4l2-ctl --all | grep -e exposure -e gain | grep "(int)"
   exposure (int): min=0 max=65535 step=1 default=0 
value=660 flags=inactive, volatile
   gain (int): min=0 max=1023 step=1 default=0 
value=37 flags=inactive, volatile
4) switch to manual mode, image exposition must not change
$> v4l2-ctl --set-ctrl=gain_automatic=0
$> v4l2-ctl --set-ctrl=auto_exposure=1
Note the "1" for manual exposure.

5) Check current gain/exposure values:
$> v4l2-ctl --all | grep -e exposure -e gain | grep "(int)"
   exposure (int): min=0 max=65535 step=1 default=0 
value=330
   gain (int): min=0 max=1023 step=1 default=0 
value=20

6) Put finger behind camera and check that gain/exposure values are NOT 
changing:
$> v4l2-ctl --all | grep -e exposure -e gain | grep "(int)"
   exposure (int): min=0 max=65535 step=1 default=0 
value=330
   gain (int): min=0 max=1023 step=1 default=0 
value=20
7) Update exposure, check that it is well changed on display and that same 
value is returned:
$> v4l2-ctl --set-ctrl=exposure=100
$> v4l2-ctl --get-ctrl=exposure
exposure: 100

9) Update gain, check that it is well changed on display and that same value is 
returned:
$> v4l2-ctl --set-ctrl=gain=10
$> v4l2-ctl --get-ctrl=gain
gain: 10

10) Switch back to auto gain/exposure, verify that image is correct and values 
returned are correct:
$> v4l2-ctl --set-ctrl=gain_automatic=1
$> v4l2-ctl --set-ctrl=auto_exposure=0
$> v4l2-ctl --all | grep -e exposure -e gain | grep "(int)"
   exposure (int): min=0 max=65535 step=1 default=0 
value=330 flags=inactive, volatile
       gain (int): min=0 max=1023 step=1 default=0 
value=22 flags=inactive, volatile
Note the "0" for auto exposure.



Hugues Fruchet (5):
  media: ov5640: fix exposure regression
  media: ov5640: fix auto gain & exposure when changing mode
  media: ov5640: fix wrong binning value in exposure calculation
  media: ov5640: fix auto controls values when switching to manual mode
  media: ov5640: fix restore of last mode set

 drivers/media/i2c/ov5640.c | 120 ++---
 1 file changed, 70 insertions(+), 50 deletions(-)

-- 
1.9.1



[PATCH 5/5] media: ov5640: fix restore of last mode set

2018-07-04 Thread Hugues Fruchet
Mode setting depends on last mode set, in particular
because of exposure calculation when downscale mode
change between subsampling and scaling.
At stream on the last mode was wrongly set to current mode,
so no change was detected and exposure calculation
was not made, fix this.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 8 +++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index a307f1e..f3fb140 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -225,6 +225,7 @@ struct ov5640_dev {
struct v4l2_mbus_framefmt fmt;
 
const struct ov5640_mode_info *current_mode;
+   const struct ov5640_mode_info *last_mode;
enum ov5640_frame_rate current_fr;
struct v4l2_fract frame_interval;
 
@@ -1628,6 +1629,9 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
int ret;
 
+   if (!orig_mode)
+   orig_mode = mode;
+
dn_mode = mode->dn_mode;
orig_dn_mode = orig_mode->dn_mode;
 
@@ -1688,6 +1692,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
return ret;
 
sensor->pending_mode_change = false;
+   sensor->last_mode = mode;
 
return 0;
 
@@ -2551,7 +2556,8 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int 
enable)
 
if (sensor->streaming == !enable) {
if (enable && sensor->pending_mode_change) {
-   ret = ov5640_set_mode(sensor, sensor->current_mode);
+   ret = ov5640_set_mode(sensor, sensor->last_mode);
+
if (ret)
goto out;
 
-- 
1.9.1



[PATCH 2/5] media: ov5640: fix auto gain & exposure when changing mode

2018-07-04 Thread Hugues Fruchet
Ensure that auto gain and auto exposure are well restored
when changing mode.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 96 ++
 1 file changed, 54 insertions(+), 42 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 4b9da8b..7c569de 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1000,6 +1000,18 @@ static int ov5640_get_gain(struct ov5640_dev *sensor)
return gain & 0x3ff;
 }
 
+static int ov5640_set_gain(struct ov5640_dev *sensor, int gain)
+{
+   return ov5640_write_reg16(sensor, OV5640_REG_AEC_PK_REAL_GAIN,
+ (u16)gain & 0x3ff);
+}
+
+static int ov5640_set_autogain(struct ov5640_dev *sensor, bool on)
+{
+   return ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL,
+ BIT(1), on ? 0 : BIT(1));
+}
+
 static int ov5640_set_stream_dvp(struct ov5640_dev *sensor, bool on)
 {
int ret;
@@ -1577,7 +1589,7 @@ static int ov5640_set_mode_exposure_calc(struct 
ov5640_dev *sensor,
}
 
/* set capture gain */
-   ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.gain, cap_gain16);
+   ret = ov5640_set_gain(sensor, cap_gain16);
if (ret)
return ret;
 
@@ -1590,7 +1602,7 @@ static int ov5640_set_mode_exposure_calc(struct 
ov5640_dev *sensor,
}
 
/* set exposure */
-   return __v4l2_ctrl_s_ctrl(sensor->ctrls.exposure, cap_shutter);
+   return ov5640_set_exposure(sensor, cap_shutter);
 }
 
 /*
@@ -1598,26 +1610,13 @@ static int ov5640_set_mode_exposure_calc(struct 
ov5640_dev *sensor,
  * change mode directly
  */
 static int ov5640_set_mode_direct(struct ov5640_dev *sensor,
- const struct ov5640_mode_info *mode,
- bool auto_exp)
+ const struct ov5640_mode_info *mode)
 {
-   int ret;
-
if (!mode->reg_data)
return -EINVAL;
 
/* Write capture setting */
-   ret = ov5640_load_regs(sensor, mode);
-   if (ret < 0)
-   return ret;
-
-   /* turn auto gain/exposure back on for direct mode */
-   ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_gain, 1);
-   if (ret)
-   return ret;
-
-   return __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, auto_exp ?
- V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL);
+   return ov5640_load_regs(sensor, mode);
 }
 
 static int ov5640_set_mode(struct ov5640_dev *sensor,
@@ -1625,6 +1624,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
 {
const struct ov5640_mode_info *mode = sensor->current_mode;
enum ov5640_downsize_mode dn_mode, orig_dn_mode;
+   bool auto_gain = sensor->ctrls.auto_gain->val == 1;
bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
int ret;
 
@@ -1632,19 +1632,23 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
orig_dn_mode = orig_mode->dn_mode;
 
/* auto gain and exposure must be turned off when changing modes */
-   ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_gain, 0);
-   if (ret)
-   return ret;
+   if (auto_gain) {
+   ret = ov5640_set_autogain(sensor, false);
+   if (ret)
+   return ret;
+   }
 
-   ret = ov5640_set_autoexposure(sensor, false);
-   if (ret)
-   return ret;
+   if (auto_exp) {
+   ret = ov5640_set_autoexposure(sensor, false);
+   if (ret)
+   goto restore_auto_gain;
+   }
 
if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
(dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
/*
 * change between subsampling and scaling
-* go through exposure calucation
+* go through exposure calculation
 */
ret = ov5640_set_mode_exposure_calc(sensor, mode);
} else {
@@ -1652,11 +1656,16 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
 * change inside subsampling or scaling
 * download firmware directly
 */
-   ret = ov5640_set_mode_direct(sensor, mode, auto_exp);
+   ret = ov5640_set_mode_direct(sensor, mode);
}
-
if (ret < 0)
-   return ret;
+   goto restore_auto_exp_gain;
+
+   /* restore auto gain and exposure */
+   if (auto_gain)
+   ov5640_set_autogain(sensor, true);
+   if (auto_exp)
+   ov5640_set_autoexposure(sensor, true);
 
ret = ov5640_set_timings(sensor, mode);
if (ret < 0)
@@ -1681,6 +1690,15 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
sensor->pending_mode_change = f

[PATCH 1/5] media: ov5640: fix exposure regression

2018-07-04 Thread Hugues Fruchet
fixes: bf4a4b518c20 ("media: ov5640: Don't force the auto exposure state at 
start time").

Symptom was black image when capturing HD or 5Mp picture
due to manual exposure set to 1 while it was intended to
set autoexposure to "manual", fix this.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 18 --
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 1ecbb7a..4b9da8b 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -938,6 +938,12 @@ static int ov5640_load_regs(struct ov5640_dev *sensor,
return ret;
 }
 
+static int ov5640_set_autoexposure(struct ov5640_dev *sensor, bool on)
+{
+   return ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL,
+ BIT(0), on ? 0 : BIT(0));
+}
+
 /* read exposure, in number of line periods */
 static int ov5640_get_exposure(struct ov5640_dev *sensor)
 {
@@ -1593,7 +1599,7 @@ static int ov5640_set_mode_exposure_calc(struct 
ov5640_dev *sensor,
  */
 static int ov5640_set_mode_direct(struct ov5640_dev *sensor,
  const struct ov5640_mode_info *mode,
- s32 exposure)
+ bool auto_exp)
 {
int ret;
 
@@ -1610,7 +1616,8 @@ static int ov5640_set_mode_direct(struct ov5640_dev 
*sensor,
if (ret)
return ret;
 
-   return __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, exposure);
+   return __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, auto_exp ?
+ V4L2_EXPOSURE_AUTO : V4L2_EXPOSURE_MANUAL);
 }
 
 static int ov5640_set_mode(struct ov5640_dev *sensor,
@@ -1618,7 +1625,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
 {
const struct ov5640_mode_info *mode = sensor->current_mode;
enum ov5640_downsize_mode dn_mode, orig_dn_mode;
-   s32 exposure;
+   bool auto_exp =  sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO;
int ret;
 
dn_mode = mode->dn_mode;
@@ -1629,8 +1636,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
if (ret)
return ret;
 
-   exposure = sensor->ctrls.auto_exp->val;
-   ret = ov5640_set_exposure(sensor, V4L2_EXPOSURE_MANUAL);
+   ret = ov5640_set_autoexposure(sensor, false);
if (ret)
return ret;
 
@@ -1646,7 +1652,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
 * change inside subsampling or scaling
 * download firmware directly
 */
-   ret = ov5640_set_mode_direct(sensor, mode, exposure);
+   ret = ov5640_set_mode_direct(sensor, mode, auto_exp);
}
 
if (ret < 0)
-- 
1.9.1



Re: [PATCH] v4l2-ctrls.c: fix broken auto cluster handling

2018-07-02 Thread Hugues FRUCHET
Many thanks Hans,

This fix my issue with ov5640,

Best regards,
Hugues.

On 06/29/2018 12:12 PM, Hans Verkuil wrote:
> When you switch from auto to manual mode for an auto-cluster (e.g.
> autogain+gain controls), then the current HW value has to be copied
> to the current control value. However, has_changed was never set to
> true, so new_to_cur didn't actually copy this value.
> 
> Signed-off-by: Hans Verkuil 
> Reported-by: Hugues FRUCHET 

Tested-by: Hugues FRUCHET 

> ---
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c 
> b/drivers/media/v4l2-core/v4l2-ctrls.c
> index d29e45516eb7..d1087573da34 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
> @@ -3137,9 +3137,22 @@ static int try_or_set_cluster(struct v4l2_fh *fh, 
> struct v4l2_ctrl *master,
> 
>   /* If OK, then make the new values permanent. */
>   update_flag = is_cur_manual(master) != is_new_manual(master);
> - for (i = 0; i < master->ncontrols; i++)
> +
> + for (i = 0; i < master->ncontrols; i++) {
> + /*
> +  * If we switch from auto to manual mode, and this cluster
> +  * contains volatile controls, then all non-master controls
> +  * have to be marked as changed. The 'new' value contains
> +  * the volatile value (obtained by update_from_auto_cluster),
> +  * which now has to become the current value.
> +  */
> + if (i && update_flag && is_new_manual(master) &&
> + master->has_volatiles && master->cluster[i])
> + master->cluster[i]->has_changed = true;
> +
>   new_to_cur(fh, master->cluster[i], ch_flags |
>   ((update_flag && i > 0) ? V4L2_EVENT_CTRL_CH_FLAGS : 
> 0));
> + }
>   return 0;
>   }
> 

[PATCH v4] media: ov5640: fix frame interval enumeration

2018-06-21 Thread Hugues Fruchet
Driver must reject frame interval enumeration of unsupported resolution.
This was detected by v4l2-compliance format ioctl test:
v4l2-compliance Format ioctls:
info: found 2 frameintervals for pixel format 4745504a and size 176x144
  fail: v4l2-test-formats.cpp(123):
   found frame intervals for invalid size 177x144
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: FAIL

Signed-off-by: Hugues Fruchet 
---
version 2:
  - revisit patch according to Mauro comments:
See https://www.mail-archive.com/linux-media@vger.kernel.org/msg127380.html

version 3:
  - revisit patch using v4l2_find_nearest_size() helper as per Sakari 
suggestion:
See https://www.mail-archive.com/linux-media@vger.kernel.org/msg128186.html

version 4:
  - fix sparse warning:
See https://www.mail-archive.com/linux-media@vger.kernel.org/msg132925.html

 drivers/media/i2c/ov5640.c | 34 --
 1 file changed, 16 insertions(+), 18 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index f6e40cc..4257ca6 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1389,24 +1389,16 @@ static int ov5640_set_timings(struct ov5640_dev *sensor,
 ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr,
 int width, int height, bool nearest)
 {
-   const struct ov5640_mode_info *mode = NULL;
-   int i;
-
-   for (i = OV5640_NUM_MODES - 1; i >= 0; i--) {
-   mode = _mode_data[fr][i];
-
-   if (!mode->reg_data)
-   continue;
+   const struct ov5640_mode_info *mode;
 
-   if ((nearest && mode->hact <= width &&
-mode->vact <= height) ||
-   (!nearest && mode->hact == width &&
-mode->vact == height))
-   break;
-   }
+   mode = v4l2_find_nearest_size(_mode_data[fr][0],
+ ARRAY_SIZE(ov5640_mode_data[fr]),
+ hact, vact,
+ width, height);
 
-   if (nearest && i < 0)
-   mode = _mode_data[fr][0];
+   if (!mode ||
+   (!nearest && (mode->hact != width || mode->vact != height)))
+   return NULL;
 
return mode;
 }
@@ -2435,8 +2427,14 @@ static int ov5640_s_frame_interval(struct v4l2_subdev 
*sd,
 
sensor->current_fr = frame_rate;
sensor->frame_interval = fi->interval;
-   sensor->current_mode = ov5640_find_mode(sensor, frame_rate, mode->hact,
-   mode->vact, true);
+   mode = ov5640_find_mode(sensor, frame_rate, mode->hact,
+   mode->vact, true);
+   if (!mode) {
+   ret = -EINVAL;
+   goto out;
+   }
+
+   sensor->current_mode = mode;
sensor->pending_mode_change = true;
 out:
mutex_unlock(>lock);
-- 
1.9.1



[PATCH v3] media: ov5640: fix frame interval enumeration

2018-06-20 Thread Hugues Fruchet
Driver must reject frame interval enumeration of unsupported resolution.
This was detected by v4l2-compliance format ioctl test:
v4l2-compliance Format ioctls:
info: found 2 frameintervals for pixel format 4745504a and size 176x144
  fail: v4l2-test-formats.cpp(123):
   found frame intervals for invalid size 177x144
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: FAIL

Signed-off-by: Hugues Fruchet 
---
version 2:
  - revisit patch according to Mauro comments:
See https://www.mail-archive.com/linux-media@vger.kernel.org/msg127380.html

version 3:
  - revisit patch using v4l2_find_nearest_size() helper as per Sakari 
suggestion:
See https://www.mail-archive.com/linux-media@vger.kernel.org/msg128186.html

 drivers/media/i2c/ov5640.c | 34 --
 1 file changed, 16 insertions(+), 18 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index f6e40cc..e4b68e3 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1389,24 +1389,16 @@ static int ov5640_set_timings(struct ov5640_dev *sensor,
 ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr,
 int width, int height, bool nearest)
 {
-   const struct ov5640_mode_info *mode = NULL;
-   int i;
-
-   for (i = OV5640_NUM_MODES - 1; i >= 0; i--) {
-   mode = _mode_data[fr][i];
-
-   if (!mode->reg_data)
-   continue;
+   const struct ov5640_mode_info *mode;
 
-   if ((nearest && mode->hact <= width &&
-mode->vact <= height) ||
-   (!nearest && mode->hact == width &&
-mode->vact == height))
-   break;
-   }
+   mode = v4l2_find_nearest_size(ov5640_mode_data[fr],
+ ARRAY_SIZE(ov5640_mode_data[fr]),
+ hact, vact,
+ width, height);
 
-   if (nearest && i < 0)
-   mode = _mode_data[fr][0];
+   if (!mode ||
+   (!nearest && (mode->hact != width || mode->vact != height)))
+   return NULL;
 
return mode;
 }
@@ -2435,8 +2427,14 @@ static int ov5640_s_frame_interval(struct v4l2_subdev 
*sd,
 
sensor->current_fr = frame_rate;
sensor->frame_interval = fi->interval;
-   sensor->current_mode = ov5640_find_mode(sensor, frame_rate, mode->hact,
-   mode->vact, true);
+   mode = ov5640_find_mode(sensor, frame_rate, mode->hact,
+   mode->vact, true);
+   if (!mode) {
+   ret = -EINVAL;
+   goto out;
+   }
+
+   sensor->current_mode = mode;
sensor->pending_mode_change = true;
 out:
mutex_unlock(>lock);
-- 
1.9.1



[PATCH v2 0/3] OV5640 hflip, vflip and module orientation support

2018-06-18 Thread Hugues Fruchet
This patch serie relates to flip and mirror at sensor level through
registers "TIMING TC REG20/REG21".

The first commit implements HFLIP and VFLIP V4L2 controls
allowing V4L2 client to change horizontal and vertical flip
before or during streaming.

The second & third commit allows to inform driver of the physical
orientation of the sensor module through devicetree "rotation"
optional properties as defined by Sakari in media/video-interfaces.txt:
https://www.mail-archive.com/linux-media@vger.kernel.org/msg132345.html

===
= history =
===
version 2:
  - Fix remarks from Rob Hering on devicetree part (new commit for binding
and reference to video-interfaces.txt common bindings):
https://www.spinics.net/lists/linux-media/msg136048.html

Hugues Fruchet (3):
  media: ov5640: add HFLIP/VFLIP controls support
  dt-bindings: ov5640: Add "rotation" property
  media: ov5640: add support of module orientation

 .../devicetree/bindings/media/i2c/ov5640.txt   |   5 +
 drivers/media/i2c/ov5640.c | 127 ++---
 2 files changed, 114 insertions(+), 18 deletions(-)

-- 
1.9.1



[PATCH v2 1/3] media: ov5640: add HFLIP/VFLIP controls support

2018-06-18 Thread Hugues Fruchet
Add HFLIP/VFLIP controls support by setting registers REG21/REG20.
Useless values in hardcoded mode sequences are removed and
remaining binning values are now set after mode sequence being set.
Note that due to BSI (Back Side Illuminated) technology, image capture
is physically mirrored, mirror logic is so inversed in REG21 register
to cancel this effect.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 103 +
 1 file changed, 85 insertions(+), 18 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index f6e40cc..41039e5 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -64,6 +64,7 @@
 #define OV5640_REG_TIMING_DVPVO0x380a
 #define OV5640_REG_TIMING_HTS  0x380c
 #define OV5640_REG_TIMING_VTS  0x380e
+#define OV5640_REG_TIMING_TC_REG20 0x3820
 #define OV5640_REG_TIMING_TC_REG21 0x3821
 #define OV5640_REG_AEC_CTRL00  0x3a00
 #define OV5640_REG_AEC_B50_STEP0x3a08
@@ -199,6 +200,8 @@ struct ov5640_ctrls {
struct v4l2_ctrl *contrast;
struct v4l2_ctrl *hue;
struct v4l2_ctrl *test_pattern;
+   struct v4l2_ctrl *hflip;
+   struct v4l2_ctrl *vflip;
 };
 
 struct ov5640_dev {
@@ -341,7 +344,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct 
v4l2_ctrl *ctrl)
 static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = {
{0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-   {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+   {0x3814, 0x31, 0, 0},
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
@@ -360,7 +363,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct 
v4l2_ctrl *ctrl)
 static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = {
{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-   {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+   {0x3814, 0x31, 0, 0},
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
@@ -379,7 +382,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct 
v4l2_ctrl *ctrl)
 static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = {
{0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-   {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+   {0x3814, 0x31, 0, 0},
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
@@ -399,7 +402,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct 
v4l2_ctrl *ctrl)
 static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = {
{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-   {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+   {0x3814, 0x31, 0, 0},
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
@@ -418,7 +421,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct 
v4l2_ctrl *ctrl)
 static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = {
{0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-   {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+   {0x3814, 0x31, 0, 0},
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
@@ -437,7 +440,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct 
v4l2_ctrl *ctrl)
 static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = {
{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-   {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+   {0x3814, 0x31, 0, 0},
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803

[PATCH v2 3/3] media: ov5640: add support of module orientation

2018-06-18 Thread Hugues Fruchet
Add support of module being physically mounted upside down.
In this case, mirror and flip are enabled to fix captured images
orientation.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 28 ++--
 1 file changed, 26 insertions(+), 2 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 41039e5..5529b14 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -215,6 +215,7 @@ struct ov5640_dev {
struct regulator_bulk_data supplies[OV5640_NUM_SUPPLIES];
struct gpio_desc *reset_gpio;
struct gpio_desc *pwdn_gpio;
+   bool   upside_down;
 
/* lock to protect all members below */
struct mutex lock;
@@ -,6 +2223,8 @@ static int ov5640_set_ctrl_light_freq(struct ov5640_dev 
*sensor, int value)
 static int ov5640_set_ctrl_hflip(struct ov5640_dev *sensor, int value)
 {
/*
+* If sensor is mounted upside down, mirror logic is inversed.
+*
 * Sensor is a BSI (Back Side Illuminated) one,
 * so image captured is physically mirrored.
 * This is why mirror logic is inversed in
@@ -2235,11 +2238,14 @@ static int ov5640_set_ctrl_hflip(struct ov5640_dev 
*sensor, int value)
 */
return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21,
  BIT(2) | BIT(1),
- (!value) ? (BIT(2) | BIT(1)) : 0);
+ (!(value ^ sensor->upside_down)) ?
+ (BIT(2) | BIT(1)) : 0);
 }
 
 static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value)
 {
+   /* If sensor is mounted upside down, flip logic is inversed */
+
/*
 * TIMING TC REG20:
 * - [2]:   ISP vflip
@@ -2247,7 +2253,8 @@ static int ov5640_set_ctrl_vflip(struct ov5640_dev 
*sensor, int value)
 */
return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG20,
  BIT(2) | BIT(1),
- value ? (BIT(2) | BIT(1)) : 0);
+ (value ^ sensor->upside_down) ?
+ (BIT(2) | BIT(1)) : 0);
 }
 
 static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
@@ -2625,6 +2632,7 @@ static int ov5640_probe(struct i2c_client *client,
struct fwnode_handle *endpoint;
struct ov5640_dev *sensor;
struct v4l2_mbus_framefmt *fmt;
+   u32 rotation;
int ret;
 
sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
@@ -2650,6 +2658,22 @@ static int ov5640_probe(struct i2c_client *client,
 
sensor->ae_target = 52;
 
+   /* optional indication of physical rotation of sensor */
+   ret = fwnode_property_read_u32(of_fwnode_handle(client->dev.of_node),
+  "rotation", );
+   if (!ret) {
+   switch (rotation) {
+   case 180:
+   sensor->upside_down = true;
+   /* fall through */
+   case 0:
+   break;
+   default:
+   dev_warn(dev, "%u degrees rotation is not supported, 
ignoring...\n",
+rotation);
+   }
+   }
+
endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(>dev),
  NULL);
if (!endpoint) {
-- 
1.9.1



[PATCH v2 2/3] dt-bindings: ov5640: Add "rotation" property

2018-06-18 Thread Hugues Fruchet
Add the rotation property to the list of optional properties
for the OV5640 camera sensor.

Signed-off-by: Hugues Fruchet 
---
 Documentation/devicetree/bindings/media/i2c/ov5640.txt | 5 +
 1 file changed, 5 insertions(+)

diff --git a/Documentation/devicetree/bindings/media/i2c/ov5640.txt 
b/Documentation/devicetree/bindings/media/i2c/ov5640.txt
index 8e36da0..c97c2f2 100644
--- a/Documentation/devicetree/bindings/media/i2c/ov5640.txt
+++ b/Documentation/devicetree/bindings/media/i2c/ov5640.txt
@@ -13,6 +13,10 @@ Optional Properties:
   This is an active low signal to the OV5640.
 - powerdown-gpios: reference to the GPIO connected to the powerdown pin,
   if any. This is an active high signal to the OV5640.
+- rotation: as defined in
+   Documentation/devicetree/bindings/media/video-interfaces.txt,
+   valid values are 0 (sensor mounted upright) and 180 (sensor
+   mounted upside down).
 
 The device node must contain one 'port' child node for its digital output
 video port, in accordance with the video interface bindings defined in
@@ -51,6 +55,7 @@ Examples:
DVDD-supply = <_reg>;  /* 1.5v */
powerdown-gpios = < 19 GPIO_ACTIVE_HIGH>;
reset-gpios = < 20 GPIO_ACTIVE_LOW>;
+   rotation = <180>;
 
port {
/* MIPI CSI-2 bus endpoint */
-- 
1.9.1



Re: [PATCH 2/2] media: ov5640: add support of module orientation

2018-06-13 Thread Hugues FRUCHET
Hi Sakari, Rob,

Find a new proposal below:

On 06/13/2018 10:24 AM, Sakari Ailus wrote:
> On Wed, Jun 13, 2018 at 08:10:02AM +0000, Hugues FRUCHET wrote:
>> Hi Rob, thanks for review,
>>
>> On 06/13/2018 12:06 AM, Rob Herring wrote:
>>> On Mon, Jun 11, 2018 at 11:29:17AM +0200, Hugues Fruchet wrote:
>>>> Add support of module being physically mounted upside down.
>>>> In this case, mirror and flip are enabled to fix captured images
>>>> orientation.
>>>>
>>>> Signed-off-by: Hugues Fruchet 
>>>> ---
>>>>.../devicetree/bindings/media/i2c/ov5640.txt   |  3 +++
>>>
>>> Please split bindings to separate patches.
>>
>> OK, will do in next patchset.
>>
>>>
>>>>drivers/media/i2c/ov5640.c | 28 
>>>> --
>>>>2 files changed, 29 insertions(+), 2 deletions(-)
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/media/i2c/ov5640.txt 
>>>> b/Documentation/devicetree/bindings/media/i2c/ov5640.txt
>>>> index 8e36da0..f76eb7e 100644
>>>> --- a/Documentation/devicetree/bindings/media/i2c/ov5640.txt
>>>> +++ b/Documentation/devicetree/bindings/media/i2c/ov5640.txt
>>>> @@ -13,6 +13,8 @@ Optional Properties:
>>>>   This is an active low signal to the OV5640.
>>>>- powerdown-gpios: reference to the GPIO connected to the powerdown pin,
>>>>   if any. This is an active high signal to the OV5640.
>>>> +- rotation: integer property; valid values are 0 (sensor mounted upright)
>>>> +  and 180 (sensor mounted upside down).
>>>
>>> Didn't we just add this as a common property? If so, just reference the
>>> common definition. If not, it needs a common definition.
>>>
>>
>> A common definition has been introduced by Sakari, I'm reusing it, see:
>> https://www.mail-archive.com/linux-media@vger.kernel.org/msg132517.html
>>
>> I would so propose:
>>   >> +- rotation: as defined in
>>   >> +   Documentation/devicetree/bindings/media/video-interfaces.txt.
> 
> Shouldn't the description still include the valid values? As far as I can
> tell, these are ultimately device specific albeit more or less the same for
> *this kind* of sensors.

Yes you're right, let's put both together:
+- rotation: as defined in
+   Documentation/devicetree/bindings/media/video-interfaces.txt,
+   valid values are 0 (sensor mounted upright) and 180 (sensor
+   mounted upside down).


> 
> The file already contains a reference to video-interfaces.txt.
> 
Yes but it was related to 'port' child node.

Best regards,
Hugues.

Re: [PATCH 2/2] media: ov5640: add support of module orientation

2018-06-13 Thread Hugues FRUCHET
Hi Rob, thanks for review,

On 06/13/2018 12:06 AM, Rob Herring wrote:
> On Mon, Jun 11, 2018 at 11:29:17AM +0200, Hugues Fruchet wrote:
>> Add support of module being physically mounted upside down.
>> In this case, mirror and flip are enabled to fix captured images
>> orientation.
>>
>> Signed-off-by: Hugues Fruchet 
>> ---
>>   .../devicetree/bindings/media/i2c/ov5640.txt   |  3 +++
> 
> Please split bindings to separate patches.

OK, will do in next patchset.

> 
>>   drivers/media/i2c/ov5640.c | 28 
>> --
>>   2 files changed, 29 insertions(+), 2 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/media/i2c/ov5640.txt 
>> b/Documentation/devicetree/bindings/media/i2c/ov5640.txt
>> index 8e36da0..f76eb7e 100644
>> --- a/Documentation/devicetree/bindings/media/i2c/ov5640.txt
>> +++ b/Documentation/devicetree/bindings/media/i2c/ov5640.txt
>> @@ -13,6 +13,8 @@ Optional Properties:
>> This is an active low signal to the OV5640.
>>   - powerdown-gpios: reference to the GPIO connected to the powerdown pin,
>> if any. This is an active high signal to the OV5640.
>> +- rotation: integer property; valid values are 0 (sensor mounted upright)
>> +and 180 (sensor mounted upside down).
> 
> Didn't we just add this as a common property? If so, just reference the
> common definition. If not, it needs a common definition.
> 

A common definition has been introduced by Sakari, I'm reusing it, see:
https://www.mail-archive.com/linux-media@vger.kernel.org/msg132517.html

I would so propose:
 >> +- rotation: as defined in
 >> +   Documentation/devicetree/bindings/media/video-interfaces.txt.


Best regards,
Hugues.

Re: [PATCH 2/2] media: ov5640: add support of module orientation

2018-06-11 Thread Hugues FRUCHET
Hi Sakari,

I'm fine with the change to dev_fwnode(>dev).

Many thanks Sakari,

Hugues.

On 06/11/2018 12:10 PM, Sakari Ailus wrote:
> On Mon, Jun 11, 2018 at 11:29:17AM +0200, Hugues Fruchet wrote:
>> Add support of module being physically mounted upside down.
>> In this case, mirror and flip are enabled to fix captured images
>> orientation.
>>
>> Signed-off-by: Hugues Fruchet 
>> ---
>>   .../devicetree/bindings/media/i2c/ov5640.txt   |  3 +++
>>   drivers/media/i2c/ov5640.c | 28 
>> --
>>   2 files changed, 29 insertions(+), 2 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/media/i2c/ov5640.txt 
>> b/Documentation/devicetree/bindings/media/i2c/ov5640.txt
>> index 8e36da0..f76eb7e 100644
>> --- a/Documentation/devicetree/bindings/media/i2c/ov5640.txt
>> +++ b/Documentation/devicetree/bindings/media/i2c/ov5640.txt
>> @@ -13,6 +13,8 @@ Optional Properties:
>> This is an active low signal to the OV5640.
>>   - powerdown-gpios: reference to the GPIO connected to the powerdown pin,
>> if any. This is an active high signal to the OV5640.
>> +- rotation: integer property; valid values are 0 (sensor mounted upright)
>> +and 180 (sensor mounted upside down).
>>   
>>   The device node must contain one 'port' child node for its digital output
>>   video port, in accordance with the video interface bindings defined in
>> @@ -51,6 +53,7 @@ Examples:
>>  DVDD-supply = <_reg>;  /* 1.5v */
>>  powerdown-gpios = < 19 GPIO_ACTIVE_HIGH>;
>>  reset-gpios = < 20 GPIO_ACTIVE_LOW>;
>> +rotation = <180>;
>>   
>>  port {
>>  /* MIPI CSI-2 bus endpoint */
>> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
>> index 41039e5..5529b14 100644
>> --- a/drivers/media/i2c/ov5640.c
>> +++ b/drivers/media/i2c/ov5640.c
>> @@ -215,6 +215,7 @@ struct ov5640_dev {
>>  struct regulator_bulk_data supplies[OV5640_NUM_SUPPLIES];
>>  struct gpio_desc *reset_gpio;
>>  struct gpio_desc *pwdn_gpio;
>> +bool   upside_down;
>>   
>>  /* lock to protect all members below */
>>  struct mutex lock;
>> @@ -,6 +2223,8 @@ static int ov5640_set_ctrl_light_freq(struct 
>> ov5640_dev *sensor, int value)
>>   static int ov5640_set_ctrl_hflip(struct ov5640_dev *sensor, int value)
>>   {
>>  /*
>> + * If sensor is mounted upside down, mirror logic is inversed.
>> + *
>>   * Sensor is a BSI (Back Side Illuminated) one,
>>   * so image captured is physically mirrored.
>>   * This is why mirror logic is inversed in
>> @@ -2235,11 +2238,14 @@ static int ov5640_set_ctrl_hflip(struct ov5640_dev 
>> *sensor, int value)
>>   */
>>  return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21,
>>BIT(2) | BIT(1),
>> -  (!value) ? (BIT(2) | BIT(1)) : 0);
>> +  (!(value ^ sensor->upside_down)) ?
>> +  (BIT(2) | BIT(1)) : 0);
>>   }
>>   
>>   static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value)
>>   {
>> +/* If sensor is mounted upside down, flip logic is inversed */
>> +
>>  /*
>>   * TIMING TC REG20:
>>   * - [2]:   ISP vflip
>> @@ -2247,7 +2253,8 @@ static int ov5640_set_ctrl_vflip(struct ov5640_dev 
>> *sensor, int value)
>>   */
>>  return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG20,
>>BIT(2) | BIT(1),
>> -  value ? (BIT(2) | BIT(1)) : 0);
>> +  (value ^ sensor->upside_down) ?
>> +  (BIT(2) | BIT(1)) : 0);
>>   }
>>   
>>   static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
>> @@ -2625,6 +2632,7 @@ static int ov5640_probe(struct i2c_client *client,
>>  struct fwnode_handle *endpoint;
>>  struct ov5640_dev *sensor;
>>  struct v4l2_mbus_framefmt *fmt;
>> +u32 rotation;
>>  int ret;
>>   
>>  sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
>> @@ -2650,6 +2658,22 @@ static int ov5640_probe(struct i2c_client *client,
>>   
>>  sensor->ae_target = 52;
>>   
>> +/* optional indication of physical rotation of sensor */
>> +ret = fwnode_property_read_u32(of_fwnode_handle(client

[PATCH] media: stm32-dcmi: revisit stop streaming ops

2018-06-11 Thread Hugues Fruchet
Do not wait for interrupt completion when stopping streaming,
stopping sensor and disabling interruptions are enough.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/platform/stm32/stm32-dcmi.c | 29 +
 1 file changed, 1 insertion(+), 28 deletions(-)

diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
b/drivers/media/platform/stm32/stm32-dcmi.c
index 581ded0..f0134a6 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -87,7 +87,6 @@ enum state {
STOPPED = 0,
WAIT_FOR_BUFFER,
RUNNING,
-   STOPPING,
 };
 
 #define MIN_WIDTH  16U
@@ -432,18 +431,6 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
 
spin_lock_irq(>irqlock);
 
-   /* Stop capture is required */
-   if (dcmi->state == STOPPING) {
-   reg_clear(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR);
-
-   dcmi->state = STOPPED;
-
-   complete(>complete);
-
-   spin_unlock_irq(>irqlock);
-   return IRQ_HANDLED;
-   }
-
if ((dcmi->misr & IT_OVR) || (dcmi->misr & IT_ERR)) {
dcmi->errors_count++;
if (dcmi->misr & IT_OVR)
@@ -701,8 +688,6 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
 {
struct stm32_dcmi *dcmi = vb2_get_drv_priv(vq);
struct dcmi_buf *buf, *node;
-   unsigned long time_ms = msecs_to_jiffies(TIMEOUT_MS);
-   long timeout;
int ret;
 
/* Disable stream on the sub device */
@@ -712,13 +697,6 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
__func__, ret);
 
spin_lock_irq(>irqlock);
-   dcmi->state = STOPPING;
-   spin_unlock_irq(>irqlock);
-
-   timeout = wait_for_completion_interruptible_timeout(>complete,
-   time_ms);
-
-   spin_lock_irq(>irqlock);
 
/* Disable interruptions */
reg_clear(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR);
@@ -726,12 +704,6 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
/* Disable DCMI */
reg_clear(dcmi->regs, DCMI_CR, CR_ENABLE);
 
-   if (!timeout) {
-   dev_err(dcmi->dev, "%s: Timeout during stop streaming\n",
-   __func__);
-   dcmi->state = STOPPED;
-   }
-
/* Return all queued buffers to vb2 in ERROR state */
list_for_each_entry_safe(buf, node, >buffers, list) {
list_del_init(>list);
@@ -739,6 +711,7 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
}
 
dcmi->active = NULL;
+   dcmi->state = STOPPED;
 
spin_unlock_irq(>irqlock);
 
-- 
1.9.1



[PATCH 3/4] media: stm32-dcmi: clarify state logic on buffer starvation

2018-06-11 Thread Hugues Fruchet
Introduce WAIT_FOR_BUFFER state instead of "active" field checking
to manage buffer starvation case.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/platform/stm32/stm32-dcmi.c | 11 ---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
b/drivers/media/platform/stm32/stm32-dcmi.c
index 6ccf195..93bb03a 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -85,6 +85,7 @@
 
 enum state {
STOPPED = 0,
+   WAIT_FOR_BUFFER,
RUNNING,
STOPPING,
 };
@@ -230,6 +231,7 @@ static int dcmi_restart_capture(struct stm32_dcmi *dcmi)
if (list_empty(>buffers)) {
dev_dbg(dcmi->dev, "Capture restart is deferred to next buffer 
queueing\n");
dcmi->active = NULL;
+   dcmi->state = WAIT_FOR_BUFFER;
spin_unlock_irq(>irqlock);
return 0;
}
@@ -548,9 +550,11 @@ static void dcmi_buf_queue(struct vb2_buffer *vb)
 
spin_lock_irq(>irqlock);
 
-   if (dcmi->state == RUNNING && !dcmi->active) {
dcmi->active = buf;
 
+   if (dcmi->state == WAIT_FOR_BUFFER) {
+   dcmi->state = RUNNING;
+
dev_dbg(dcmi->dev, "Starting capture on buffer[%d] queued\n",
buf->vb.vb2_buf.index);
 
@@ -630,8 +634,6 @@ static int dcmi_start_streaming(struct vb2_queue *vq, 
unsigned int count)
/* Enable dcmi */
reg_set(dcmi->regs, DCMI_CR, CR_ENABLE);
 
-   dcmi->state = RUNNING;
-
dcmi->sequence = 0;
dcmi->errors_count = 0;
dcmi->overrun_count = 0;
@@ -644,6 +646,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, 
unsigned int count)
 */
if (list_empty(>buffers)) {
dev_dbg(dcmi->dev, "Start streaming is deferred to next buffer 
queueing\n");
+   dcmi->state = WAIT_FOR_BUFFER;
spin_unlock_irq(>irqlock);
return 0;
}
@@ -653,6 +656,8 @@ static int dcmi_start_streaming(struct vb2_queue *vq, 
unsigned int count)
 
dev_dbg(dcmi->dev, "Start streaming, starting capture\n");
 
+   dcmi->state = RUNNING;
+
spin_unlock_irq(>irqlock);
ret = dcmi_start_capture(dcmi);
if (ret) {
-- 
1.9.1



[PATCH] media: stm32-dcmi: code cleanup

2018-06-11 Thread Hugues Fruchet
Minor non-functional fixes around comments, variable namings
and trace point enhancement.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/platform/stm32/stm32-dcmi.c | 18 --
 1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
b/drivers/media/platform/stm32/stm32-dcmi.c
index c55e6b5..b796334 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -249,13 +249,12 @@ static int dcmi_restart_capture(struct stm32_dcmi *dcmi)
 static void dcmi_dma_callback(void *param)
 {
struct stm32_dcmi *dcmi = (struct stm32_dcmi *)param;
-   struct dma_chan *chan = dcmi->dma_chan;
struct dma_tx_state state;
enum dma_status status;
struct dcmi_buf *buf = dcmi->active;
 
/* Check DMA status */
-   status = dmaengine_tx_status(chan, dcmi->dma_cookie, );
+   status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, );
 
switch (status) {
case DMA_IN_PROGRESS:
@@ -309,10 +308,11 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi,
/* Prepare a DMA transaction */
desc = dmaengine_prep_slave_single(dcmi->dma_chan, buf->paddr,
   buf->size,
-  DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+  DMA_DEV_TO_MEM,
+  DMA_PREP_INTERRUPT);
if (!desc) {
-   dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_single failed 
for buffer size %zu\n",
-   __func__, buf->size);
+   dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_single failed 
for buffer phy=%pad size=%zu\n",
+   __func__, >paddr, buf->size);
return -EINVAL;
}
 
@@ -378,7 +378,6 @@ static void dcmi_process_jpeg(struct stm32_dcmi *dcmi)
 {
struct dma_tx_state state;
enum dma_status status;
-   struct dma_chan *chan = dcmi->dma_chan;
struct dcmi_buf *buf = dcmi->active;
 
if (!buf)
@@ -386,8 +385,7 @@ static void dcmi_process_jpeg(struct stm32_dcmi *dcmi)
 
/*
 * Because of variable JPEG buffer size sent by sensor,
-* DMA transfer never completes due to transfer size
-* never reached.
+* DMA transfer never completes due to transfer size never reached.
 * In order to ensure that all the JPEG data are transferred
 * in active buffer memory, DMA is drained.
 * Then DMA tx status gives the amount of data transferred
@@ -396,10 +394,10 @@ static void dcmi_process_jpeg(struct stm32_dcmi *dcmi)
 */
 
/* Drain DMA */
-   dmaengine_synchronize(chan);
+   dmaengine_synchronize(dcmi->dma_chan);
 
/* Get DMA residue to get JPEG size */
-   status = dmaengine_tx_status(chan, dcmi->dma_cookie, );
+   status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, );
if (status != DMA_ERROR && state.residue < buf->size) {
/* Return JPEG buffer to V4L2 with received JPEG buffer size */
dcmi_buffer_done(dcmi, buf, buf->size - state.residue, 0);
-- 
1.9.1



[PATCH 0/4] Revisit and fix DCMI buffers handling

2018-06-11 Thread Hugues Fruchet
Revisit and fix DCMI buffers handling.

Hugues Fruchet (4):
  media: stm32-dcmi: do not fall into error on buffer starvation
  media: stm32-dcmi: return buffer in error state on dma error
  media: stm32-dcmi: clarify state logic on buffer starvation
  media: stm32-dcmi: revisit buffer list management

 drivers/media/platform/stm32/stm32-dcmi.c | 80 ---
 1 file changed, 41 insertions(+), 39 deletions(-)

-- 
1.9.1



[PATCH 1/4] media: stm32-dcmi: do not fall into error on buffer starvation

2018-06-11 Thread Hugues Fruchet
Return silently instead of falling into error when running
out of available buffers when restarting capture.
Capture will be restarted when new buffers will be
provided by V4L2 client.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/platform/stm32/stm32-dcmi.c | 7 ++-
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
b/drivers/media/platform/stm32/stm32-dcmi.c
index b796334..a3fbfac 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -228,13 +228,10 @@ static int dcmi_restart_capture(struct stm32_dcmi *dcmi)
 
/* Restart a new DMA transfer with next buffer */
if (list_empty(>buffers)) {
-   dev_err(dcmi->dev, "%s: No more buffer queued, cannot capture 
buffer\n",
-   __func__);
-   dcmi->errors_count++;
+   dev_dbg(dcmi->dev, "Capture restart is deferred to next buffer 
queueing\n");
dcmi->active = NULL;
-
spin_unlock_irq(>irqlock);
-   return -EINVAL;
+   return 0;
}
 
dcmi->active = list_entry(dcmi->buffers.next,
-- 
1.9.1



[PATCH 2/4] media: stm32-dcmi: return buffer in error state on dma error

2018-06-11 Thread Hugues Fruchet
Return buffer to V4L2 in error state if DMA error occurs.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/platform/stm32/stm32-dcmi.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
b/drivers/media/platform/stm32/stm32-dcmi.c
index a3fbfac..6ccf195 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -262,6 +262,9 @@ static void dcmi_dma_callback(void *param)
break;
case DMA_ERROR:
dev_err(dcmi->dev, "%s: Received DMA_ERROR\n", __func__);
+
+   /* Return buffer to V4L2 in error state */
+   dcmi_buffer_done(dcmi, buf, 0, -EIO);
break;
case DMA_COMPLETE:
dev_dbg(dcmi->dev, "%s: Received DMA_COMPLETE\n", __func__);
-- 
1.9.1



[PATCH 4/4] media: stm32-dcmi: revisit buffer list management

2018-06-11 Thread Hugues Fruchet
Cleanup "active" field usage and enhance list management
to avoid exceptions when releasing buffers on error or
stopping streaming.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/platform/stm32/stm32-dcmi.c | 65 +++
 1 file changed, 31 insertions(+), 34 deletions(-)

diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
b/drivers/media/platform/stm32/stm32-dcmi.c
index 93bb03a..581ded0 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -191,7 +191,7 @@ static inline void reg_clear(void __iomem *base, u32 reg, 
u32 mask)
reg_write(base, reg, reg_read(base, reg) & ~mask);
 }
 
-static int dcmi_start_capture(struct stm32_dcmi *dcmi);
+static int dcmi_start_capture(struct stm32_dcmi *dcmi, struct dcmi_buf *buf);
 
 static void dcmi_buffer_done(struct stm32_dcmi *dcmi,
 struct dcmi_buf *buf,
@@ -203,6 +203,8 @@ static void dcmi_buffer_done(struct stm32_dcmi *dcmi,
if (!buf)
return;
 
+   list_del_init(>list);
+
vbuf = >vb;
 
vbuf->sequence = dcmi->sequence++;
@@ -220,6 +222,8 @@ static void dcmi_buffer_done(struct stm32_dcmi *dcmi,
 
 static int dcmi_restart_capture(struct stm32_dcmi *dcmi)
 {
+   struct dcmi_buf *buf;
+
spin_lock_irq(>irqlock);
 
if (dcmi->state != RUNNING) {
@@ -230,19 +234,16 @@ static int dcmi_restart_capture(struct stm32_dcmi *dcmi)
/* Restart a new DMA transfer with next buffer */
if (list_empty(>buffers)) {
dev_dbg(dcmi->dev, "Capture restart is deferred to next buffer 
queueing\n");
-   dcmi->active = NULL;
dcmi->state = WAIT_FOR_BUFFER;
spin_unlock_irq(>irqlock);
return 0;
}
-
-   dcmi->active = list_entry(dcmi->buffers.next,
- struct dcmi_buf, list);
-   list_del_init(>active->list);
+   buf = list_entry(dcmi->buffers.next, struct dcmi_buf, list);
+   dcmi->active = buf;
 
spin_unlock_irq(>irqlock);
 
-   return dcmi_start_capture(dcmi);
+   return dcmi_start_capture(dcmi, buf);
 }
 
 static void dcmi_dma_callback(void *param)
@@ -252,6 +253,8 @@ static void dcmi_dma_callback(void *param)
enum dma_status status;
struct dcmi_buf *buf = dcmi->active;
 
+   spin_lock_irq(>irqlock);
+
/* Check DMA status */
status = dmaengine_tx_status(dcmi->dma_chan, dcmi->dma_cookie, );
 
@@ -274,15 +277,19 @@ static void dcmi_dma_callback(void *param)
/* Return buffer to V4L2 */
dcmi_buffer_done(dcmi, buf, buf->size, 0);
 
+   spin_unlock_irq(>irqlock);
+
/* Restart capture */
if (dcmi_restart_capture(dcmi))
dev_err(dcmi->dev, "%s: Cannot restart capture on DMA 
complete\n",
__func__);
-   break;
+   return;
default:
dev_err(dcmi->dev, "%s: Received unknown status\n", __func__);
break;
}
+
+   spin_unlock_irq(>irqlock);
 }
 
 static int dcmi_start_dma(struct stm32_dcmi *dcmi,
@@ -334,10 +341,9 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi,
return 0;
 }
 
-static int dcmi_start_capture(struct stm32_dcmi *dcmi)
+static int dcmi_start_capture(struct stm32_dcmi *dcmi, struct dcmi_buf *buf)
 {
int ret;
-   struct dcmi_buf *buf = dcmi->active;
 
if (!buf)
return -EINVAL;
@@ -491,8 +497,6 @@ static int dcmi_queue_setup(struct vb2_queue *vq,
*nplanes = 1;
sizes[0] = size;
 
-   dcmi->active = NULL;
-
dev_dbg(dcmi->dev, "Setup queue, count=%d, size=%d\n",
*nbuffers, size);
 
@@ -550,23 +554,24 @@ static void dcmi_buf_queue(struct vb2_buffer *vb)
 
spin_lock_irq(>irqlock);
 
-   dcmi->active = buf;
+   /* Enqueue to video buffers list */
+   list_add_tail(>list, >buffers);
 
if (dcmi->state == WAIT_FOR_BUFFER) {
dcmi->state = RUNNING;
+   dcmi->active = buf;
 
dev_dbg(dcmi->dev, "Starting capture on buffer[%d] queued\n",
buf->vb.vb2_buf.index);
 
spin_unlock_irq(>irqlock);
-   if (dcmi_start_capture(dcmi))
+   if (dcmi_start_capture(dcmi, buf))
dev_err(dcmi->dev, "%s: Cannot restart capture on 
overflow or error\n",
__func__);
-   } else {
-   /* Enqueue to video buffers list */
-   list_add_tail(>list, >buffers);
-   spin_unlock_irq(>irqlock);
+   return;
}
+
+ 

[PATCH] media: stm32-dcmi: increase max width/height to 2592

2018-06-11 Thread Hugues Fruchet
DCMI can capture 5Mp raw frames, increase limit accordingly.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/platform/stm32/stm32-dcmi.c | 19 ---
 1 file changed, 4 insertions(+), 15 deletions(-)

diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
b/drivers/media/platform/stm32/stm32-dcmi.c
index 68da9ec..c55e6b5 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -90,14 +90,9 @@ enum state {
 };
 
 #define MIN_WIDTH  16U
-#define MAX_WIDTH  2048U
+#define MAX_WIDTH  2592U
 #define MIN_HEIGHT 16U
-#define MAX_HEIGHT 2048U
-
-#define MIN_JPEG_WIDTH 16U
-#define MAX_JPEG_WIDTH 2592U
-#define MIN_JPEG_HEIGHT16U
-#define MAX_JPEG_HEIGHT2592U
+#define MAX_HEIGHT 2592U
 
 #define TIMEOUT_MS 1000
 
@@ -844,14 +839,8 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct 
v4l2_format *f,
}
 
/* Limit to hardware capabilities */
-   if (pix->pixelformat == V4L2_PIX_FMT_JPEG) {
-   pix->width = clamp(pix->width, MIN_JPEG_WIDTH, MAX_JPEG_WIDTH);
-   pix->height =
-   clamp(pix->height, MIN_JPEG_HEIGHT, MAX_JPEG_HEIGHT);
-   } else {
-   pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH);
-   pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT);
-   }
+   pix->width = clamp(pix->width, MIN_WIDTH, MAX_WIDTH);
+   pix->height = clamp(pix->height, MIN_HEIGHT, MAX_HEIGHT);
 
/* No crop if JPEG is requested */
do_crop = dcmi->do_crop && (pix->pixelformat != V4L2_PIX_FMT_JPEG);
-- 
1.9.1



[PATCH] media: stm32-dcmi: add power saving support

2018-06-11 Thread Hugues Fruchet
Implements runtime & system sleep power management ops.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/platform/stm32/stm32-dcmi.c | 80 ---
 1 file changed, 64 insertions(+), 16 deletions(-)

diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
b/drivers/media/platform/stm32/stm32-dcmi.c
index 2e1933d..68da9ec 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -22,6 +22,7 @@
 #include 
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
@@ -578,9 +579,9 @@ static int dcmi_start_streaming(struct vb2_queue *vq, 
unsigned int count)
u32 val = 0;
int ret;
 
-   ret = clk_enable(dcmi->mclk);
+   ret = pm_runtime_get_sync(dcmi->dev);
if (ret) {
-   dev_err(dcmi->dev, "%s: Failed to start streaming, cannot 
enable clock\n",
+   dev_err(dcmi->dev, "%s: Failed to start streaming, cannot get 
sync\n",
__func__);
goto err_release_buffers;
}
@@ -590,7 +591,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, 
unsigned int count)
if (ret && ret != -ENOIOCTLCMD) {
dev_err(dcmi->dev, "%s: Failed to start streaming, subdev 
streamon error",
__func__);
-   goto err_disable_clock;
+   goto err_pm_put;
}
 
spin_lock_irq(>irqlock);
@@ -675,8 +676,8 @@ static int dcmi_start_streaming(struct vb2_queue *vq, 
unsigned int count)
 err_subdev_streamoff:
v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 0);
 
-err_disable_clock:
-   clk_disable(dcmi->mclk);
+err_pm_put:
+   pm_runtime_put(dcmi->dev);
 
 err_release_buffers:
spin_lock_irq(>irqlock);
@@ -749,7 +750,7 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
/* Stop all pending DMA operations */
dmaengine_terminate_all(dcmi->dma_chan);
 
-   clk_disable(dcmi->mclk);
+   pm_runtime_put(dcmi->dev);
 
if (dcmi->errors_count)
dev_warn(dcmi->dev, "Some errors found while streaming: 
errors=%d (overrun=%d), buffers=%d\n",
@@ -1751,12 +1752,6 @@ static int dcmi_probe(struct platform_device *pdev)
return -EPROBE_DEFER;
}
 
-   ret = clk_prepare(mclk);
-   if (ret) {
-   dev_err(>dev, "Unable to prepare mclk %p\n", mclk);
-   goto err_dma_release;
-   }
-
spin_lock_init(>irqlock);
mutex_init(>lock);
init_completion(>complete);
@@ -1772,7 +1767,7 @@ static int dcmi_probe(struct platform_device *pdev)
/* Initialize the top-level structure */
ret = v4l2_device_register(>dev, >v4l2_dev);
if (ret)
-   goto err_clk_unprepare;
+   goto err_dma_release;
 
dcmi->vdev = video_device_alloc();
if (!dcmi->vdev) {
@@ -1832,14 +1827,15 @@ static int dcmi_probe(struct platform_device *pdev)
dev_info(>dev, "Probe done\n");
 
platform_set_drvdata(pdev, dcmi);
+
+   pm_runtime_enable(>dev);
+
return 0;
 
 err_device_release:
video_device_release(dcmi->vdev);
 err_device_unregister:
v4l2_device_unregister(>v4l2_dev);
-err_clk_unprepare:
-   clk_unprepare(dcmi->mclk);
 err_dma_release:
dma_release_channel(dcmi->dma_chan);
 
@@ -1850,20 +1846,72 @@ static int dcmi_remove(struct platform_device *pdev)
 {
struct stm32_dcmi *dcmi = platform_get_drvdata(pdev);
 
+   pm_runtime_disable(>dev);
+
v4l2_async_notifier_unregister(>notifier);
v4l2_device_unregister(>v4l2_dev);
-   clk_unprepare(dcmi->mclk);
+
dma_release_channel(dcmi->dma_chan);
 
return 0;
 }
 
+static __maybe_unused int dcmi_runtime_suspend(struct device *dev)
+{
+   struct stm32_dcmi *dcmi = dev_get_drvdata(dev);
+
+   clk_disable_unprepare(dcmi->mclk);
+
+   return 0;
+}
+
+static __maybe_unused int dcmi_runtime_resume(struct device *dev)
+{
+   struct stm32_dcmi *dcmi = dev_get_drvdata(dev);
+   int ret;
+
+   ret = clk_prepare_enable(dcmi->mclk);
+   if (ret)
+   dev_err(dev, "%s: Failed to prepare_enable clock\n", __func__);
+
+   return ret;
+}
+
+static __maybe_unused int dcmi_suspend(struct device *dev)
+{
+   /* disable clock */
+   pm_runtime_force_suspend(dev);
+
+   /* change pinctrl state */
+   pinctrl_pm_select_sleep_state(dev);
+
+   return 0;
+}
+
+static __maybe_unused int dcmi_resume(struct device *dev)
+{
+   /* restore pinctl default state */
+   pinctrl_pm_select_default_state(dev);
+
+   /* clock enable */
+   pm_runtime_force_resume(dev);
+
+   return 0;
+}
+
+static const struct dev_pm_ops dcmi_pm_ops = {
+   

[PATCH 2/2] media: ov5640: add support of module orientation

2018-06-11 Thread Hugues Fruchet
Add support of module being physically mounted upside down.
In this case, mirror and flip are enabled to fix captured images
orientation.

Signed-off-by: Hugues Fruchet 
---
 .../devicetree/bindings/media/i2c/ov5640.txt   |  3 +++
 drivers/media/i2c/ov5640.c | 28 --
 2 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/media/i2c/ov5640.txt 
b/Documentation/devicetree/bindings/media/i2c/ov5640.txt
index 8e36da0..f76eb7e 100644
--- a/Documentation/devicetree/bindings/media/i2c/ov5640.txt
+++ b/Documentation/devicetree/bindings/media/i2c/ov5640.txt
@@ -13,6 +13,8 @@ Optional Properties:
   This is an active low signal to the OV5640.
 - powerdown-gpios: reference to the GPIO connected to the powerdown pin,
   if any. This is an active high signal to the OV5640.
+- rotation: integer property; valid values are 0 (sensor mounted upright)
+   and 180 (sensor mounted upside down).
 
 The device node must contain one 'port' child node for its digital output
 video port, in accordance with the video interface bindings defined in
@@ -51,6 +53,7 @@ Examples:
DVDD-supply = <_reg>;  /* 1.5v */
powerdown-gpios = < 19 GPIO_ACTIVE_HIGH>;
reset-gpios = < 20 GPIO_ACTIVE_LOW>;
+   rotation = <180>;
 
port {
/* MIPI CSI-2 bus endpoint */
diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 41039e5..5529b14 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -215,6 +215,7 @@ struct ov5640_dev {
struct regulator_bulk_data supplies[OV5640_NUM_SUPPLIES];
struct gpio_desc *reset_gpio;
struct gpio_desc *pwdn_gpio;
+   bool   upside_down;
 
/* lock to protect all members below */
struct mutex lock;
@@ -,6 +2223,8 @@ static int ov5640_set_ctrl_light_freq(struct ov5640_dev 
*sensor, int value)
 static int ov5640_set_ctrl_hflip(struct ov5640_dev *sensor, int value)
 {
/*
+* If sensor is mounted upside down, mirror logic is inversed.
+*
 * Sensor is a BSI (Back Side Illuminated) one,
 * so image captured is physically mirrored.
 * This is why mirror logic is inversed in
@@ -2235,11 +2238,14 @@ static int ov5640_set_ctrl_hflip(struct ov5640_dev 
*sensor, int value)
 */
return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG21,
  BIT(2) | BIT(1),
- (!value) ? (BIT(2) | BIT(1)) : 0);
+ (!(value ^ sensor->upside_down)) ?
+ (BIT(2) | BIT(1)) : 0);
 }
 
 static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value)
 {
+   /* If sensor is mounted upside down, flip logic is inversed */
+
/*
 * TIMING TC REG20:
 * - [2]:   ISP vflip
@@ -2247,7 +2253,8 @@ static int ov5640_set_ctrl_vflip(struct ov5640_dev 
*sensor, int value)
 */
return ov5640_mod_reg(sensor, OV5640_REG_TIMING_TC_REG20,
  BIT(2) | BIT(1),
- value ? (BIT(2) | BIT(1)) : 0);
+ (value ^ sensor->upside_down) ?
+ (BIT(2) | BIT(1)) : 0);
 }
 
 static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
@@ -2625,6 +2632,7 @@ static int ov5640_probe(struct i2c_client *client,
struct fwnode_handle *endpoint;
struct ov5640_dev *sensor;
struct v4l2_mbus_framefmt *fmt;
+   u32 rotation;
int ret;
 
sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
@@ -2650,6 +2658,22 @@ static int ov5640_probe(struct i2c_client *client,
 
sensor->ae_target = 52;
 
+   /* optional indication of physical rotation of sensor */
+   ret = fwnode_property_read_u32(of_fwnode_handle(client->dev.of_node),
+  "rotation", );
+   if (!ret) {
+   switch (rotation) {
+   case 180:
+   sensor->upside_down = true;
+   /* fall through */
+   case 0:
+   break;
+   default:
+   dev_warn(dev, "%u degrees rotation is not supported, 
ignoring...\n",
+rotation);
+   }
+   }
+
endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(>dev),
  NULL);
if (!endpoint) {
-- 
1.9.1



[PATCH 0/2] OV5640 hflip, vflip and module orientation support

2018-06-11 Thread Hugues Fruchet
This patch serie relates to flip and mirror at sensor level through
registers "TIMING TC REG20/REG21".

The first commit implements HFLIP and VFLIP V4L2 controls
allowing V4L2 client to change horizontal and vertical flip
before or during streaming.

The second commit allows to inform driver of the physical
orientation of the sensor module through devicetree "rotation"
optional properties as defined by Sakari in media/video-interfaces.txt:
https://www.mail-archive.com/linux-media@vger.kernel.org/msg132345.html

Hugues Fruchet (2):
  media: ov5640: add HFLIP/VFLIP controls support
  media: ov5640: add support of module orientation

 .../devicetree/bindings/media/i2c/ov5640.txt   |   3 +
 drivers/media/i2c/ov5640.c | 127 ++---
 2 files changed, 112 insertions(+), 18 deletions(-)

-- 
1.9.1



[PATCH 1/2] media: ov5640: add HFLIP/VFLIP controls support

2018-06-11 Thread Hugues Fruchet
Add HFLIP/VFLIP controls support by setting registers REG21/REG20.
Useless values in hardcoded mode sequences are removed and
remaining binning values are now set after mode sequence being set.
Note that due to BSI (Back Side Illuminated) technology, image capture
is physically mirrored, mirror logic is so inversed in REG21 register
to cancel this effect.

Signed-off-by: Hugues Fruchet 
---
 drivers/media/i2c/ov5640.c | 103 +
 1 file changed, 85 insertions(+), 18 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index f6e40cc..41039e5 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -64,6 +64,7 @@
 #define OV5640_REG_TIMING_DVPVO0x380a
 #define OV5640_REG_TIMING_HTS  0x380c
 #define OV5640_REG_TIMING_VTS  0x380e
+#define OV5640_REG_TIMING_TC_REG20 0x3820
 #define OV5640_REG_TIMING_TC_REG21 0x3821
 #define OV5640_REG_AEC_CTRL00  0x3a00
 #define OV5640_REG_AEC_B50_STEP0x3a08
@@ -199,6 +200,8 @@ struct ov5640_ctrls {
struct v4l2_ctrl *contrast;
struct v4l2_ctrl *hue;
struct v4l2_ctrl *test_pattern;
+   struct v4l2_ctrl *hflip;
+   struct v4l2_ctrl *vflip;
 };
 
 struct ov5640_dev {
@@ -341,7 +344,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct 
v4l2_ctrl *ctrl)
 static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = {
{0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-   {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+   {0x3814, 0x31, 0, 0},
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
@@ -360,7 +363,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct 
v4l2_ctrl *ctrl)
 static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = {
{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-   {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+   {0x3814, 0x31, 0, 0},
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
@@ -379,7 +382,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct 
v4l2_ctrl *ctrl)
 static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = {
{0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-   {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+   {0x3814, 0x31, 0, 0},
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
@@ -399,7 +402,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct 
v4l2_ctrl *ctrl)
 static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = {
{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-   {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+   {0x3814, 0x31, 0, 0},
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
@@ -418,7 +421,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct 
v4l2_ctrl *ctrl)
 static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = {
{0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-   {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+   {0x3814, 0x31, 0, 0},
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
{0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
@@ -437,7 +440,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct 
v4l2_ctrl *ctrl)
 static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = {
{0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
-   {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
+   {0x3814, 0x31, 0, 0},
{0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
{0x3802, 0x00, 0, 0}, {0x3803

Re: [PATCH v2 11/12] media: ov5640: Add 60 fps support

2018-05-17 Thread Hugues FRUCHET
Hi Maxime,

Thanks for fixes !

No special modification of v4l2-ctl, I'm using currently v4l-utils 1.12.3.
What output do you have ?

Best regards,
Hugues.

On 05/17/2018 10:52 AM, Maxime Ripard wrote:
> Hi Hugues,
> 
> On Tue, May 15, 2018 at 01:33:55PM +0000, Hugues FRUCHET wrote:
>> I've taken the whole serie and made some tests on STM32 platform using
>> DVP parallel interface.
>> Now JPEG is OK and I've not seen any regressions appart on framerate
>> control linked to this current patchset.
> 
> Thanks a lot for your feedback, I've (hopefully) fixed all the issues
> you reported here, most of the time taking your fix directly, except
> for 2 where I reworked the code instead.
> 
>> Here are issues observed around framerate control:
>> 1) Framerate enumeration is buggy and all resolutions are claimed
>> supporting 15/30/60:
>> v4l2-ctl --list-formats-ext
>> ioctl: VIDIOC_ENUM_FMT
>>   Index   : 0
>>   Type: Video Capture
>>   Pixel Format: 'JPEG' (compressed)
>>   Name: JFIF JPEG
>>   Size: Discrete 176x144
>>   Interval: Discrete 0.067s (15.000 fps)
>>   Interval: Discrete 0.033s (30.000 fps)
>>   Interval: Discrete 0.017s (60.000 fps)
> 
> One small question though, I don't seem to have that output for
> v4l2-ctl, is some hook needed in the v4l2 device for it to work?
> 
> Maxime
> 


Re: [PATCH v2 11/12] media: ov5640: Add 60 fps support

2018-05-15 Thread Hugues FRUCHET
Hi Maxime,

I've taken the whole serie and made some tests on STM32 platform using 
DVP parallel interface.
Now JPEG is OK and I've not seen any regressions appart on framerate 
control linked to this current patchset.

Here are issues observed around framerate control:
1) Framerate enumeration is buggy and all resolutions are claimed 
supporting 15/30/60:
v4l2-ctl --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
 Index   : 0
 Type: Video Capture
 Pixel Format: 'JPEG' (compressed)
 Name: JFIF JPEG
 Size: Discrete 176x144
 Interval: Discrete 0.067s (15.000 fps)
 Interval: Discrete 0.033s (30.000 fps)
 Interval: Discrete 0.017s (60.000 fps)
 Size: Discrete 320x240
 Interval: Discrete 0.067s (15.000 fps)
 Interval: Discrete 0.033s (30.000 fps)
 Interval: Discrete 0.017s (60.000 fps)
 Size: Discrete 640x480
 Interval: Discrete 0.067s (15.000 fps)
 Interval: Discrete 0.033s (30.000 fps)
 Interval: Discrete 0.017s (60.000 fps)
 Size: Discrete 720x480
 Interval: Discrete 0.067s (15.000 fps)
 Interval: Discrete 0.033s (30.000 fps)
 Interval: Discrete 0.017s (60.000 fps)
 Size: Discrete 720x576
 Interval: Discrete 0.067s (15.000 fps)
 Interval: Discrete 0.033s (30.000 fps)
 Interval: Discrete 0.017s (60.000 fps)
 Size: Discrete 1024x768
 Interval: Discrete 0.067s (15.000 fps)
 Interval: Discrete 0.033s (30.000 fps)
 Interval: Discrete 0.017s (60.000 fps)
 Size: Discrete 1280x720
 Interval: Discrete 0.067s (15.000 fps)
 Interval: Discrete 0.033s (30.000 fps)
 Interval: Discrete 0.017s (60.000 fps)
 Size: Discrete 1920x1080
 Interval: Discrete 0.067s (15.000 fps)
 Interval: Discrete 0.033s (30.000 fps)
 Interval: Discrete 0.017s (60.000 fps)
 Size: Discrete 2592x1944
 Interval: Discrete 0.067s (15.000 fps)
 Interval: Discrete 0.033s (30.000 fps)
 Interval: Discrete 0.017s (60.000 fps)

2) Frame interval setting is returning incorrect value (*1000):
v4l2-ctl --set-parm=15
<
Frame rate set to 15000.000 fps

3) After having fixed 1) and 2), 720x480 60fps is still supported:
 Size: Discrete 640x480
 Interval: Discrete 0.067s (15.000 fps)
 Interval: Discrete 0.033s (30.000 fps)
 Interval: Discrete 0.017s (60.000 fps)
 Size: Discrete 720x480
 Interval: Discrete 0.067s (15.000 fps)
 Interval: Discrete 0.033s (30.000 fps)
 Interval: Discrete 0.017s (60.000 fps)

Some fixes are proposed below:


On 04/16/2018 02:37 PM, Maxime Ripard wrote:
> Now that we have everything in place to compute the clock rate at runtime,
> we can enable the 60fps framerate for the mode we tested it with.
> 
> Signed-off-by: Maxime Ripard 
> ---
>   drivers/media/i2c/ov5640.c | 33 +
>   1 file changed, 25 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> index 690ed0238763..c01bbc5f9f34 100644
> --- a/drivers/media/i2c/ov5640.c
> +++ b/drivers/media/i2c/ov5640.c
> @@ -112,6 +112,7 @@ enum ov5640_mode_id {
>   enum ov5640_frame_rate {
>   OV5640_15_FPS = 0,
>   OV5640_30_FPS,
> + OV5640_60_FPS,
>   OV5640_NUM_FRAMERATES,
>   };
>   
> @@ -140,6 +141,7 @@ MODULE_PARM_DESC(virtual_channel,
>   static const int ov5640_framerates[] = {
>   [OV5640_15_FPS] = 15,
>   [OV5640_30_FPS] = 30,
> + [OV5640_60_FPS] = 60,
>   };
>   
>   /* regulator supplies */
> @@ -1398,12 +1400,19 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum 
> ov5640_frame_rate fr,
>   /* try to find another mode */
>   continue;
>   
> + /* Only 640x480 can operate at 60fps (for now) */
> + if (fr == OV5640_60_FPS &&
> + width != 640 && height != 480)

Should be
+   !(width == 640 && height == 480))
otherwise 720x480 is also supported (bug 3))

> + /* try to find another mode */
> + continue;
> +
>   break;
>   }
>   }

Re: [PATCH 10/12] media: ov5640: Enhance FPS handling

2018-03-13 Thread Hugues FRUCHET
Hi Maxime,

See below comments related to format compliance failing.

On 03/02/2018 03:34 PM, Maxime Ripard wrote:
> Now that we have moved the clock generation logic out of the bytes array,
> these arrays are identical between the 15fps and 30fps variants.
> 
> Remove the duplicate entries, and convert the code accordingly.
> 
> Signed-off-by: Maxime Ripard 
> ---
>   drivers/media/i2c/ov5640.c | 312 
> -
>   1 file changed, 56 insertions(+), 256 deletions(-)
> 
> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> index bdf378d80e07..5510a19281a4 100644
> --- a/drivers/media/i2c/ov5640.c
> +++ b/drivers/media/i2c/ov5640.c
> @@ -344,66 +344,7 @@ static const struct reg_value 
> ov5640_init_setting_30fps_VGA[] = {
>   {0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300},
>   };
>   
> -static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = {
> -
> - {0x3c07, 0x08, 0, 0},
> - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
> - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
> - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
> - {0x3810, 0x00, 0, 0},
> - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
> - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
> - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
> - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0},
> - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
> - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
> - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
> - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
> - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0},
> -};
> -
> -static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = {
> - {0x3c07, 0x08, 0, 0},
> - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
> - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
> - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
> - {0x3810, 0x00, 0, 0},
> - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
> - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
> - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
> - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0},
> - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
> - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
> - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
> - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
> - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
> -};
> -
> -static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = {
> -
> - {0x3c07, 0x08, 0, 0},
> - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
> - {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
> - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0},
> - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0},
> - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0},
> - {0x3810, 0x00, 0, 0},
> - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0},
> - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0},
> - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0},
> - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0},
> - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0},
> - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0},
> - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0},
> - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
> - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0},
> -};
> -
> -static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = {
> +static const struct reg_value ov5640_setting_VGA_640_480[] = {
>   {0x3c07, 0x08, 0, 0},
>   {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
>   {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0},
> @@ -422,7 +363,7 @@ static const struct reg_value 
> ov5640_setting_15fps_XGA_1024_768[] = {
>   {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
>   };
>   
> -static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = {
> +static const struct 

Re: [PATCH 11/12] media: ov5640: Add 60 fps support

2018-03-13 Thread Hugues FRUCHET
Hi Maxime,

See below rest of comments regarding framerate and compliance failure.

On 03/02/2018 03:34 PM, Maxime Ripard wrote:
> Now that we have everything in place to compute the clock rate at runtime,
> we can enable the 60fps framerate for the mode we tested it with.
> 
> Signed-off-by: Maxime Ripard 
> ---
>   drivers/media/i2c/ov5640.c | 20 +++-
>   1 file changed, 15 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> index 5510a19281a4..03838f42fb82 100644
> --- a/drivers/media/i2c/ov5640.c
> +++ b/drivers/media/i2c/ov5640.c
> @@ -111,6 +111,7 @@ enum ov5640_mode_id {
>   enum ov5640_frame_rate {
>   OV5640_15_FPS = 0,
>   OV5640_30_FPS,
> + OV5640_60_FPS,
>   OV5640_NUM_FRAMERATES,
>   };
>   
> @@ -144,6 +145,7 @@ MODULE_PARM_DESC(virtual_channel,
>   static const int ov5640_framerates[] = {
>   [OV5640_15_FPS] = 15,
>   [OV5640_30_FPS] = 30,
> + [OV5640_60_FPS] = 60,
>   };
>   
>   /* regulator supplies */
> @@ -1447,6 +1449,11 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum 
> ov5640_frame_rate fr,
>   fr != OV5640_15_FPS)
>   return NULL;
>   
> + /* Only 640x480 can operate at 60fps (for now) */
> + if (fr == OV5640_60_FPS &&
> + width != 640 && height != 480)
> + return NULL;
> +

Same comment as on patchset 10/12, VGA exception put in for loop:

for (i = OV5640_NUM_MODES - 1; i >= 0; i--) {
mode = _mode_data[i];

if (!mode->reg_data)
continue;

if ((nearest && mode->hact <= width &&
 mode->vact <= height) ||
(!nearest && mode->hact == width &&
 mode->vact == height)) {

/* 2592x1944 can only operate at 15fps */
if (mode->hact == 2592 && mode->vact == 1944 &&
fr != OV5640_15_FPS)
continue;/* next lower resolution */

/* Only 640x480 can operate at 60fps (for now) */
if (fr == OV5640_60_FPS &&
!(mode->hact == 640 && mode->vact == 480))
continue;/* next lower resolution */

break;/* select this resolution */
}
}


>   return mode;
>   }
>   
> @@ -1875,12 +1882,12 @@ static int ov5640_try_frame_interval(struct 
> ov5640_dev *sensor,
>   int ret;
>   
>   minfps = ov5640_framerates[OV5640_15_FPS];
> - maxfps = ov5640_framerates[OV5640_30_FPS];
> + maxfps = ov5640_framerates[OV5640_60_FPS];
>   
>   if (fi->numerator == 0) {
>   fi->denominator = maxfps;
>   fi->numerator = 1;
> - return OV5640_30_FPS;
> + return OV5640_60_FPS;

There is a problem here because we don't validate that 60fps is 
supported in the currently set mode, we must inject this framerate value 
in ov5640_find_mode(framerate, width, height) in order to validate it 
(-EINVAL if not supported):

  + ret = OV5640_60_FPS;
  + goto find_mode;
  + }
[...]
+find_mode:
mode = ov5640_find_mode(sensor, frame_rate, width, height, false);
return mode ? ret : -EINVAL;
  }

Then we have to catch error in ov5640_s_frame_interval() and return an 
acceptable frame interval to user:

frame_rate = ov5640_try_frame_interval(sensor, >interval,
   mode->hact, mode->vact);
-   if (frame_rate < 0)
-   frame_rate = OV5640_15_FPS;
-
-   sensor->current_fr = frame_rate;
-   sensor->frame_interval = fi->interval;
We also have to update current framerate only if framerate has been 
validated.

+   if (frame_rate < 0) {
+   /* return a valid frame interval value */
+   fi->interval = sensor->frame_interval;
goto out;
}

+   sensor->current_fr = frame_rate;
+   sensor->frame_interval = fi->interval;
sensor->pending_mode_change = true;
  out:


About 60 fps by default if (fi->numerator == 0): shouldn't we stick to a 
default value supported by all modes such as 30fps ?

>   }
>   
>   fps = DIV_ROUND_CLOSEST(fi->denominator, fi->numerator);
> @@ -1892,10 +1899,13 @@ static int ov5640_try_frame_interval(struct 
> ov5640_dev *sensor,
>   fi->denominator = minfps;
>   else if (2 * fps >= 2 * minfps + (maxfps - minfps))
>   fi->denominator = maxfps;
> - else
> - fi->denominator = minfps;
>   
> - ret = (fi->denominator == minfps) ? OV5640_15_FPS : OV5640_30_FPS;
> + if (fi->denominator == minfps)
> + ret = OV5640_15_FPS;
> + else if (fi->denominator == maxfps)
> + ret = OV5640_60_FPS;
> + else
> + ret = OV5640_30_FPS;
>   
>   

Re: [PATCH 00/12] media: ov5640: Misc cleanup and improvements

2018-03-13 Thread Hugues FRUCHET
Thanks Maxime for this great series !

I've tested this series successfully on STM32 platform, I had a 
regression on JPEG capture linked to revisit of clocking, but easy to fix.
I had another problem claimed by v4l2-compliance on format tests:
v4l2-test-formats.cpp(961): Video Capture: S_FMT(G_FMT) != G_FMT
this was more tricky to fix, it is linked to changes around framerate 
handling.

See my further comments in corresponding patchsets.

Best regards,
Hugues.

On 03/02/2018 03:34 PM, Maxime Ripard wrote:
> Hi,
> 
> Here is a "small" series that mostly cleans up the ov5640 driver code,
> slowly getting rid of the big data array for more understandable code
> (hopefully).
> 
> The biggest addition would be the clock rate computation at runtime,
> instead of relying on those arrays to setup the clock tree
> properly. As a side effect, it fixes the framerate that was off by
> around 10% on the smaller resolutions, and we now support 60fps.
> 
> This also introduces a bunch of new features.
> 
> Let me know what you think,
> Maxime
> 
> Maxime Ripard (10):
>media: ov5640: Don't force the auto exposure state at start time
>media: ov5640: Init properly the SCLK dividers
>media: ov5640: Change horizontal and vertical resolutions name
>media: ov5640: Add horizontal and vertical totals
>media: ov5640: Program the visible resolution
>media: ov5640: Adjust the clock based on the expected rate
>media: ov5640: Compute the clock rate at runtime
>media: ov5640: Enhance FPS handling
>media: ov5640: Add 60 fps support
>media: ov5640: Remove duplicate auto-exposure setup
> 
> Mylène Josserand (2):
>media: ov5640: Add auto-focus feature
>media: ov5640: Add light frequency control
> 
>   drivers/media/i2c/ov5640.c | 777 
> ++---
>   1 file changed, 452 insertions(+), 325 deletions(-)
> 

Re: [PATCH 09/12] media: ov5640: Compute the clock rate at runtime

2018-03-13 Thread Hugues FRUCHET
Hi Maxime,

Below comments about JPEG issue.

On 03/02/2018 03:34 PM, Maxime Ripard wrote:
> The clock rate, while hardcoded until now, is actually a function of the
> resolution, framerate and bytes per pixel. Now that we have an algorithm to
> adjust our clock rate, we can select it dynamically when we change the
> mode.
> 
> This changes a bit the clock rate being used, with the following effect:
> 
> +--+--+--+--+-+-++---+
> | Hact | Vact | Htot | Vtot | FPS | Hardcoded clock | Computed clock | 
> Deviation |
> +--+--+--+--+-+-++---+
> |  640 |  480 | 1896 | 1080 |  15 |5600 |   61430400 | 8.84 % 
>|
> |  640 |  480 | 1896 | 1080 |  30 |   11200 |  122860800 | 8.84 % 
>|
> | 1024 |  768 | 1896 | 1080 |  15 |5600 |   61430400 | 8.84 % 
>|
> | 1024 |  768 | 1896 | 1080 |  30 |   11200 |  122860800 | 8.84 % 
>|
> |  320 |  240 | 1896 |  984 |  15 |5600 |   55969920 | 0.05 % 
>|
> |  320 |  240 | 1896 |  984 |  30 |   11200 |  111939840 | 0.05 % 
>|
> |  176 |  144 | 1896 |  984 |  15 |5600 |   55969920 | 0.05 % 
>|
> |  176 |  144 | 1896 |  984 |  30 |   11200 |  111939840 | 0.05 % 
>|
> |  720 |  480 | 1896 |  984 |  15 |5600 |   55969920 | 0.05 % 
>|
> |  720 |  480 | 1896 |  984 |  30 |   11200 |  111939840 | 0.05 % 
>|
> |  720 |  576 | 1896 |  984 |  15 |5600 |   55969920 | 0.05 % 
>|
> |  720 |  576 | 1896 |  984 |  30 |   11200 |  111939840 | 0.05 % 
>|
> | 1280 |  720 | 1892 |  740 |  15 |4200 |   42002400 | 0.01 % 
>|
> | 1280 |  720 | 1892 |  740 |  30 |8400 |   84004800 | 0.01 % 
>|
> | 1920 | 1080 | 2500 | 1120 |  15 |8400 |   8400 | 0.00 % 
>|
> | 1920 | 1080 | 2500 | 1120 |  30 |   16800 |  16800 | 0.00 % 
>|
> | 2592 | 1944 | 2844 | 1944 |  15 |8400 |  165862080 | 49.36 
> %   |
> +--+--+--+--+-+-++---+
> 
> Only the 640x480, 1024x768 and 2592x1944 modes are significantly affected
> by the new formula.
> 
> In this case, 640x480 and 1024x768 are actually fixed by this driver.
> Indeed, the sensor was sending data at, for example, 27.33fps instead of
> 30fps. This is -9%, which is roughly what we're seeing in the array.
> Testing these modes with the new clock setup actually fix that error, and
> data are now sent at around 30fps.
> 
> 2592x1944, on the other hand, is probably due to the fact that this mode
> can only be used using MIPI-CSI2, in a two lane mode. This would have to be
> tested though.
> 
> Signed-off-by: Maxime Ripard 
> ---
>   drivers/media/i2c/ov5640.c | 41 +
>   1 file changed, 17 insertions(+), 24 deletions(-)
> 
> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> index 323cde27dd8b..bdf378d80e07 100644
> --- a/drivers/media/i2c/ov5640.c
> +++ b/drivers/media/i2c/ov5640.c
> @@ -126,6 +126,12 @@ static const struct ov5640_pixfmt ov5640_formats[] = {
>   { MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB, },
>   };
>   
> +/*
> + * FIXME: If we ever have something else, we'll obviously need to have
> + * something smarter.
> + */
> +#define OV5640_FORMATS_BPP   2
> +

We have the case with JPEG which is 1 byte.

>   /*
>* FIXME: remove this when a subdev API becomes available
>* to set the MIPI CSI-2 virtual channel.
> @@ -172,7 +178,6 @@ struct ov5640_mode_info {
>   u32 htot;
>   u32 vact;
>   u32 vtot;
> - u32 clock;
>   const struct reg_value *reg_data;
>   u32 reg_data_size;
>   };
> @@ -696,7 +701,6 @@ static const struct reg_value 
> ov5640_setting_15fps_QSXGA_2592_1944[] = {
>   /* power-on sensor init reg table */
>   static const struct ov5640_mode_info ov5640_mode_init_data = {
>   0, SUBSAMPLING, 640, 1896, 480, 984,
> - 11200,
>   ov5640_init_setting_30fps_VGA,
>   ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
>   };
> @@ -706,91 +710,74 @@ 
> ov5640_mode_data[OV5640_NUM_FRAMERATES][OV5640_NUM_MODES] = {
>   {
>   {OV5640_MODE_QCIF_176_144, SUBSAMPLING,
>176, 1896, 144, 984,
> -  5600,
>ov5640_setting_15fps_QCIF_176_144,
>ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)},
>   {OV5640_MODE_QVGA_320_240, SUBSAMPLING,
>320, 1896, 240, 984,
> -  5600,
>ov5640_setting_15fps_QVGA_320_240,
>ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)},
>   {OV5640_MODE_VGA_640_480, SUBSAMPLING,
>640, 1896, 480, 1080,
> -  5600,
>

Re: [PATCH] media: ov5640: add missing output pixel format setting

2018-03-12 Thread Hugues FRUCHET
Hi Akinobu,

Thanks for the patch, could you describe the test you made to reproduce 
the issue that I can test on my side ?

I'm using usually yavta or Gstreamer, but I don't know how to trig the 
power on/off independently of streamon/off.

Best regards,
Hugues.

On 03/11/2018 04:34 PM, Akinobu Mita wrote:
> The output pixel format changed by set_fmt() pad operation is not
> correctly applied.  It is intended to be restored by calling
> ov5640_set_framefmt() when the video stream is started.
> 
> However, when the device is powered on by s_power subdev operation before
> the video stream is started, the current output mode setting is restored
> by ov5640_restore_mode() that also clears pending_mode_change flag in
> ov5640_set_mode().  So ov5640_set_framefmt() isn't called as intended and
> the output pixel format is not restored.
> 
> This change adds the missing output pixel format setting in the
> ov5640_restore_mode() that is called when the device is powered on.
> 
> Cc: Steve Longerbeam <slongerb...@gmail.com>
> Cc: Hugues Fruchet <hugues.fruc...@st.com>
> Cc: Sakari Ailus <sakari.ai...@linux.intel.com>
> Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
> Signed-off-by: Akinobu Mita <akinobu.m...@gmail.com>
> ---
>   drivers/media/i2c/ov5640.c | 9 -
>   1 file changed, 8 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> index e2dd352..4eecc91 100644
> --- a/drivers/media/i2c/ov5640.c
> +++ b/drivers/media/i2c/ov5640.c
> @@ -1633,6 +1633,9 @@ static int ov5640_set_mode(struct ov5640_dev *sensor,
>   return 0;
>   }
>   
> +static int ov5640_set_framefmt(struct ov5640_dev *sensor,
> +struct v4l2_mbus_framefmt *format);
> +
>   /* restore the last set video mode after chip power-on */
>   static int ov5640_restore_mode(struct ov5640_dev *sensor)
>   {
> @@ -1644,7 +1647,11 @@ static int ov5640_restore_mode(struct ov5640_dev 
> *sensor)
>   return ret;
>   
>   /* now restore the last capture mode */
> - return ov5640_set_mode(sensor, _mode_init_data);
> + ret = ov5640_set_mode(sensor, _mode_init_data);
> + if (ret < 0)
> + return ret;
> +
> + return ov5640_set_framefmt(sensor, >fmt);
>   }
>   
>   static void ov5640_power(struct ov5640_dev *sensor, bool enable)
> 

Re: [PATCH] media: ov5640: fix frame interval enumeration

2018-03-08 Thread Hugues FRUCHET
Hi Mauro,

Thanks for review, I've just sent a v2 to rearrange code as per your 
suggestion and also add a NULL test case for mode even if this should 
not happen.

Best regards,
Hugues.

On 03/08/2018 11:46 AM, Mauro Carvalho Chehab wrote:
> Em Thu, 8 Mar 2018 07:39:09 -0300
> Mauro Carvalho Chehab  escreveu:
> 
>> Also, if this function starts returning NULL, I suspect that you also
>> need to change ov5640_s_frame_interval(), as currently it is called
>> at ov5640_s_frame_interval() as:
>>
>>  sensor->current_mode = ov5640_find_mode(sensor, frame_rate, mode->width,
>>  mode->height, true);
>>
>> without checking if the returned value is NULL. Setting
>> current_mode to NULL can cause oopses at ov5640_set_mode().
> 
> Actually, as ov5640_s_frame_interval() calls ov5640_try_fmt_internal()
> first. So, this should never happen.
> 
> 
> Thanks,
> Mauro
> 

[PATCH v2] media: ov5640: fix frame interval enumeration

2018-03-08 Thread Hugues Fruchet
Driver must reject frame interval enumeration of unsupported resolution.
This was detected by v4l2-compliance format ioctl test:
v4l2-compliance Format ioctls:
info: found 2 frameintervals for pixel format 4745504a and size 176x144
  fail: v4l2-test-formats.cpp(123):
   found frame intervals for invalid size 177x144
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: FAIL

Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
---
version 2:
  - revisit patch according to Mauro comments:
See https://www.mail-archive.com/linux-media@vger.kernel.org/msg127380.html

 drivers/media/i2c/ov5640.c | 16 +---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 676f635..5c08124 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1397,8 +1397,12 @@ static int ov5640_set_virtual_channel(struct ov5640_dev 
*sensor)
break;
}
 
-   if (nearest && i < 0)
+   if (i < 0) {
+   /* no match */
+   if (!nearest)
+   return NULL;
mode = _mode_data[fr][0];
+   }
 
return mode;
 }
@@ -2381,8 +2385,14 @@ static int ov5640_s_frame_interval(struct v4l2_subdev 
*sd,
 
sensor->current_fr = frame_rate;
sensor->frame_interval = fi->interval;
-   sensor->current_mode = ov5640_find_mode(sensor, frame_rate, mode->width,
-   mode->height, true);
+   mode = ov5640_find_mode(sensor, frame_rate, mode->width,
+   mode->height, true);
+   if (!mode) {
+   ret = -EINVAL;
+   goto out;
+   }
+
+   sensor->current_mode = mode;
sensor->pending_mode_change = true;
 out:
mutex_unlock(>lock);
-- 
1.9.1



[PATCH v2] media: stm32-dcmi: rework overrun/error case

2018-03-08 Thread Hugues Fruchet
Do not stop/restart dma on overrun or errors.
Dma will be restarted on current frame transfer
completion. Frame transfer completion is ensured
even if overrun or error occurs by DCMI continuous
capture mode which restarts data transfer at next
frame sync.
Do no warn on overrun while in irq thread, this slows down
system and lead to more overrun errors. Use a counter
instead and log errors at stop streaming.

Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
---
version 2:
  - typo: remove blank line at the end of stop_streaming()

 drivers/media/platform/stm32/stm32-dcmi.c | 29 +++--
 1 file changed, 11 insertions(+), 18 deletions(-)

diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
b/drivers/media/platform/stm32/stm32-dcmi.c
index 37d82d3..95ace0f 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -160,6 +160,7 @@ struct stm32_dcmi {
dma_cookie_tdma_cookie;
u32 misr;
int errors_count;
+   int overrun_count;
int buffers_count;
 };
 
@@ -373,23 +374,9 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
}
 
if ((dcmi->misr & IT_OVR) || (dcmi->misr & IT_ERR)) {
-   /*
-* An overflow or an error has been detected,
-* stop current DMA transfert & restart it
-*/
-   dev_warn(dcmi->dev, "%s: Overflow or error detected\n",
-__func__);
-
dcmi->errors_count++;
-   dev_dbg(dcmi->dev, "Restarting capture after DCMI error\n");
-
-   spin_unlock_irq(>irqlock);
-   dmaengine_terminate_all(dcmi->dma_chan);
-
-   if (dcmi_start_capture(dcmi))
-   dev_err(dcmi->dev, "%s: Cannot restart capture on 
overflow or error\n",
-   __func__);
-   return IRQ_HANDLED;
+   if (dcmi->misr & IT_OVR)
+   dcmi->overrun_count++;
}
 
spin_unlock_irq(>irqlock);
@@ -572,6 +559,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, 
unsigned int count)
 
dcmi->sequence = 0;
dcmi->errors_count = 0;
+   dcmi->overrun_count = 0;
dcmi->buffers_count = 0;
dcmi->active = NULL;
 
@@ -682,8 +670,13 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
 
clk_disable(dcmi->mclk);
 
-   dev_dbg(dcmi->dev, "Stop streaming, errors=%d buffers=%d\n",
-   dcmi->errors_count, dcmi->buffers_count);
+   if (dcmi->errors_count)
+   dev_warn(dcmi->dev, "Some errors found while streaming: 
errors=%d (overrun=%d), buffers=%d\n",
+dcmi->errors_count, dcmi->overrun_count,
+dcmi->buffers_count);
+   dev_dbg(dcmi->dev, "Stop streaming, errors=%d (overrun=%d), 
buffers=%d\n",
+   dcmi->errors_count, dcmi->overrun_count,
+   dcmi->buffers_count);
 }
 
 static const struct vb2_ops dcmi_video_qops = {
-- 
1.9.1



[PATCH] media: ov5640: fix frame interval enumeration

2018-03-08 Thread Hugues Fruchet
Driver must reject frame interval enumeration of unsupported resolution.
This was detected by v4l2-compliance format ioctl test:
v4l2-compliance Format ioctls:
  info: found 2 frameintervals for pixel format 4745504a and size 176x144
  fail: v4l2-test-formats.cpp(123):
   found frame intervals for invalid size 177x144
  test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: FAIL

Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
---
 drivers/media/i2c/ov5640.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 676f635..28dc687 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1400,6 +1400,9 @@ static int ov5640_set_virtual_channel(struct ov5640_dev 
*sensor)
if (nearest && i < 0)
mode = _mode_data[fr][0];
 
+   if (!nearest && i < 0)
+   return NULL;
+
return mode;
 }
 
-- 
1.9.1



Re: [PATCH] media: ov5640: fix get_/set_fmt colorspace related fields

2018-03-08 Thread Hugues FRUCHET
Hi Sakari,

This is the right one and it's OK to swap the lines for local variables, 
I'll keep this in mind for next changes.

Best regards,
Hugues.

On 03/07/2018 09:13 AM, Sakari Ailus wrote:
> Hi Hugues,
> 
> On Tue, Mar 06, 2018 at 06:04:39PM +0100, Hugues Fruchet wrote:
>> Fix set of missing colorspace related fields in get_/set_fmt.
>> Detected by v4l2-compliance tool.
>>
>> Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
> 
> Could you confirm this is the one you intended to send? There are two
> others with similar content.
> 
> ...
> 
>> @@ -2497,16 +2504,22 @@ static int ov5640_probe(struct i2c_client *client,
>>  struct fwnode_handle *endpoint;
>>  struct ov5640_dev *sensor;
>>  int ret;
>> +struct v4l2_mbus_framefmt *fmt;
> 
> This one I'd arrange before ret. The local variable declarations should
> generally look like a Christmas tree but upside down.
> 
> If you're happy with that, I can swap the two lines as well (no need for
> v2).
> 
>>   
>>  sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
>>  if (!sensor)
>>  return -ENOMEM;
>>   
>>  sensor->i2c_client = client;
>> -sensor->fmt.code = MEDIA_BUS_FMT_UYVY8_2X8;
>> -sensor->fmt.width = 640;
>> -sensor->fmt.height = 480;
>> -sensor->fmt.field = V4L2_FIELD_NONE;
>> +fmt = >fmt;
>> +fmt->code = ov5640_formats[0].code;
>> +fmt->colorspace = ov5640_formats[0].colorspace;
>> +fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
>> +fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
>> +fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
>> +fmt->width = 640;
>> +fmt->height = 480;
>> +fmt->field = V4L2_FIELD_NONE;
>>  sensor->frame_interval.numerator = 1;
>>  sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS];
>>  sensor->current_fr = OV5640_30_FPS;
> 

[PATCH] media: ov5640: fix get_/set_fmt colorspace related fields

2018-03-06 Thread Hugues Fruchet
Fix set of missing colorspace related fields in get_/set_fmt.
Detected by v4l2-compliance tool.

Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
---
 drivers/media/i2c/ov5640.c | 29 +
 1 file changed, 21 insertions(+), 8 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 03940f0..676f635 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1874,7 +1874,13 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev 
*sd,
if (ov5640_formats[i].code == fmt->code)
break;
if (i >= ARRAY_SIZE(ov5640_formats))
-   fmt->code = ov5640_formats[0].code;
+   i = 0;
+
+   fmt->code = ov5640_formats[i].code;
+   fmt->colorspace = ov5640_formats[i].colorspace;
+   fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
+   fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+   fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
 
return 0;
 }
@@ -1885,6 +1891,7 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
 {
struct ov5640_dev *sensor = to_ov5640_dev(sd);
const struct ov5640_mode_info *new_mode;
+   struct v4l2_mbus_framefmt *mbus_fmt = >format;
int ret;
 
if (format->pad != 0)
@@ -1897,7 +1904,7 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
goto out;
}
 
-   ret = ov5640_try_fmt_internal(sd, >format,
+   ret = ov5640_try_fmt_internal(sd, mbus_fmt,
  sensor->current_fr, _mode);
if (ret)
goto out;
@@ -1906,12 +1913,12 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *fmt =
v4l2_subdev_get_try_format(sd, cfg, 0);
 
-   *fmt = format->format;
+   *fmt = *mbus_fmt;
goto out;
}
 
sensor->current_mode = new_mode;
-   sensor->fmt = format->format;
+   sensor->fmt = *mbus_fmt;
sensor->pending_mode_change = true;
 out:
mutex_unlock(>lock);
@@ -2497,16 +2504,22 @@ static int ov5640_probe(struct i2c_client *client,
struct fwnode_handle *endpoint;
struct ov5640_dev *sensor;
int ret;
+   struct v4l2_mbus_framefmt *fmt;
 
sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
if (!sensor)
return -ENOMEM;
 
sensor->i2c_client = client;
-   sensor->fmt.code = MEDIA_BUS_FMT_UYVY8_2X8;
-   sensor->fmt.width = 640;
-   sensor->fmt.height = 480;
-   sensor->fmt.field = V4L2_FIELD_NONE;
+   fmt = >fmt;
+   fmt->code = ov5640_formats[0].code;
+   fmt->colorspace = ov5640_formats[0].colorspace;
+   fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
+   fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+   fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
+   fmt->width = 640;
+   fmt->height = 480;
+   fmt->field = V4L2_FIELD_NONE;
sensor->frame_interval.numerator = 1;
sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS];
sensor->current_fr = OV5640_30_FPS;
-- 
1.9.1



[PATCH] media: ov5640: fix colorspace compliance

2018-03-06 Thread Hugues Fruchet
Fix format ioctl colorspace related fields.
Detected by v4l2-compliance tool.

Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
---
 drivers/media/i2c/ov5640.c | 29 +
 1 file changed, 21 insertions(+), 8 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 03940f0..676f635 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1874,7 +1874,13 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev 
*sd,
if (ov5640_formats[i].code == fmt->code)
break;
if (i >= ARRAY_SIZE(ov5640_formats))
-   fmt->code = ov5640_formats[0].code;
+   i = 0;
+
+   fmt->code = ov5640_formats[i].code;
+   fmt->colorspace = ov5640_formats[i].colorspace;
+   fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
+   fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+   fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
 
return 0;
 }
@@ -1885,6 +1891,7 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
 {
struct ov5640_dev *sensor = to_ov5640_dev(sd);
const struct ov5640_mode_info *new_mode;
+   struct v4l2_mbus_framefmt *mbus_fmt = >format;
int ret;
 
if (format->pad != 0)
@@ -1897,7 +1904,7 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
goto out;
}
 
-   ret = ov5640_try_fmt_internal(sd, >format,
+   ret = ov5640_try_fmt_internal(sd, mbus_fmt,
  sensor->current_fr, _mode);
if (ret)
goto out;
@@ -1906,12 +1913,12 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *fmt =
v4l2_subdev_get_try_format(sd, cfg, 0);
 
-   *fmt = format->format;
+   *fmt = *mbus_fmt;
goto out;
}
 
sensor->current_mode = new_mode;
-   sensor->fmt = format->format;
+   sensor->fmt = *mbus_fmt;
sensor->pending_mode_change = true;
 out:
mutex_unlock(>lock);
@@ -2497,16 +2504,22 @@ static int ov5640_probe(struct i2c_client *client,
struct fwnode_handle *endpoint;
struct ov5640_dev *sensor;
int ret;
+   struct v4l2_mbus_framefmt *fmt;
 
sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
if (!sensor)
return -ENOMEM;
 
sensor->i2c_client = client;
-   sensor->fmt.code = MEDIA_BUS_FMT_UYVY8_2X8;
-   sensor->fmt.width = 640;
-   sensor->fmt.height = 480;
-   sensor->fmt.field = V4L2_FIELD_NONE;
+   fmt = >fmt;
+   fmt->code = ov5640_formats[0].code;
+   fmt->colorspace = ov5640_formats[0].colorspace;
+   fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
+   fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+   fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
+   fmt->width = 640;
+   fmt->height = 480;
+   fmt->field = V4L2_FIELD_NONE;
sensor->frame_interval.numerator = 1;
sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS];
sensor->current_fr = OV5640_30_FPS;
-- 
1.9.1



[PATCH] media: ov5640: fix colorspace compliance

2018-03-06 Thread Hugues Fruchet
Fix format ioctl colorspace related fields.
Detected by v4l2-compliance tool.

Change-Id: I645138297033bc409751a3c7fc63e014650b8417
Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
---
 drivers/media/i2c/ov5640.c | 29 +
 1 file changed, 21 insertions(+), 8 deletions(-)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 03940f0..676f635 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -1874,7 +1874,13 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev 
*sd,
if (ov5640_formats[i].code == fmt->code)
break;
if (i >= ARRAY_SIZE(ov5640_formats))
-   fmt->code = ov5640_formats[0].code;
+   i = 0;
+
+   fmt->code = ov5640_formats[i].code;
+   fmt->colorspace = ov5640_formats[i].colorspace;
+   fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
+   fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+   fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
 
return 0;
 }
@@ -1885,6 +1891,7 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
 {
struct ov5640_dev *sensor = to_ov5640_dev(sd);
const struct ov5640_mode_info *new_mode;
+   struct v4l2_mbus_framefmt *mbus_fmt = >format;
int ret;
 
if (format->pad != 0)
@@ -1897,7 +1904,7 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
goto out;
}
 
-   ret = ov5640_try_fmt_internal(sd, >format,
+   ret = ov5640_try_fmt_internal(sd, mbus_fmt,
  sensor->current_fr, _mode);
if (ret)
goto out;
@@ -1906,12 +1913,12 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *fmt =
v4l2_subdev_get_try_format(sd, cfg, 0);
 
-   *fmt = format->format;
+   *fmt = *mbus_fmt;
goto out;
}
 
sensor->current_mode = new_mode;
-   sensor->fmt = format->format;
+   sensor->fmt = *mbus_fmt;
sensor->pending_mode_change = true;
 out:
mutex_unlock(>lock);
@@ -2497,16 +2504,22 @@ static int ov5640_probe(struct i2c_client *client,
struct fwnode_handle *endpoint;
struct ov5640_dev *sensor;
int ret;
+   struct v4l2_mbus_framefmt *fmt;
 
sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
if (!sensor)
return -ENOMEM;
 
sensor->i2c_client = client;
-   sensor->fmt.code = MEDIA_BUS_FMT_UYVY8_2X8;
-   sensor->fmt.width = 640;
-   sensor->fmt.height = 480;
-   sensor->fmt.field = V4L2_FIELD_NONE;
+   fmt = >fmt;
+   fmt->code = ov5640_formats[0].code;
+   fmt->colorspace = ov5640_formats[0].colorspace;
+   fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
+   fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+   fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
+   fmt->width = 640;
+   fmt->height = 480;
+   fmt->field = V4L2_FIELD_NONE;
sensor->frame_interval.numerator = 1;
sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS];
sensor->current_fr = OV5640_30_FPS;
-- 
1.9.1



[PATCH v2] media: stm32-dcmi: add JPEG support

2018-02-28 Thread Hugues Fruchet
Add DCMI JPEG support.

Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
---
version 2:
  - Removed V4L2_FMT_FLAG_COMPRESSED flag setting already set by V4L2 core
See https://www.mail-archive.com/linux-media@vger.kernel.org/msg126825.html

 drivers/media/platform/stm32/stm32-dcmi.c | 193 ++
 1 file changed, 146 insertions(+), 47 deletions(-)

diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
b/drivers/media/platform/stm32/stm32-dcmi.c
index 269e963..36f9a77 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -93,6 +93,11 @@ enum state {
 #define MIN_HEIGHT 16U
 #define MAX_HEIGHT 2048U
 
+#define MIN_JPEG_WIDTH 16U
+#define MAX_JPEG_WIDTH 2592U
+#define MIN_JPEG_HEIGHT16U
+#define MAX_JPEG_HEIGHT2592U
+
 #define TIMEOUT_MS 1000
 
 struct dcmi_graph_entity {
@@ -191,14 +196,67 @@ static inline void reg_clear(void __iomem *base, u32 reg, 
u32 mask)
 
 static int dcmi_start_capture(struct stm32_dcmi *dcmi);
 
+static void dcmi_buffer_done(struct stm32_dcmi *dcmi,
+struct dcmi_buf *buf,
+size_t bytesused,
+int err)
+{
+   struct vb2_v4l2_buffer *vbuf;
+
+   if (!buf)
+   return;
+
+   vbuf = >vb;
+
+   vbuf->sequence = dcmi->sequence++;
+   vbuf->field = V4L2_FIELD_NONE;
+   vbuf->vb2_buf.timestamp = ktime_get_ns();
+   vb2_set_plane_payload(>vb2_buf, 0, bytesused);
+   vb2_buffer_done(>vb2_buf,
+   err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+   dev_dbg(dcmi->dev, "buffer[%d] done seq=%d, bytesused=%zu\n",
+   vbuf->vb2_buf.index, vbuf->sequence, bytesused);
+
+   dcmi->buffers_count++;
+   dcmi->active = NULL;
+}
+
+static int dcmi_restart_capture(struct stm32_dcmi *dcmi)
+{
+   spin_lock_irq(>irqlock);
+
+   if (dcmi->state != RUNNING) {
+   spin_unlock_irq(>irqlock);
+   return -EINVAL;
+   }
+
+   /* Restart a new DMA transfer with next buffer */
+   if (list_empty(>buffers)) {
+   dev_err(dcmi->dev, "%s: No more buffer queued, cannot capture 
buffer\n",
+   __func__);
+   dcmi->errors_count++;
+   dcmi->active = NULL;
+
+   spin_unlock_irq(>irqlock);
+   return -EINVAL;
+   }
+
+   dcmi->active = list_entry(dcmi->buffers.next,
+ struct dcmi_buf, list);
+   list_del_init(>active->list);
+
+   spin_unlock_irq(>irqlock);
+
+   return dcmi_start_capture(dcmi);
+}
+
 static void dcmi_dma_callback(void *param)
 {
struct stm32_dcmi *dcmi = (struct stm32_dcmi *)param;
struct dma_chan *chan = dcmi->dma_chan;
struct dma_tx_state state;
enum dma_status status;
-
-   spin_lock_irq(>irqlock);
+   struct dcmi_buf *buf = dcmi->active;
 
/* Check DMA status */
status = dmaengine_tx_status(chan, dcmi->dma_cookie, );
@@ -216,53 +274,18 @@ static void dcmi_dma_callback(void *param)
case DMA_COMPLETE:
dev_dbg(dcmi->dev, "%s: Received DMA_COMPLETE\n", __func__);
 
-   if (dcmi->active) {
-   struct dcmi_buf *buf = dcmi->active;
-   struct vb2_v4l2_buffer *vbuf = >active->vb;
-
-   vbuf->sequence = dcmi->sequence++;
-   vbuf->field = V4L2_FIELD_NONE;
-   vbuf->vb2_buf.timestamp = ktime_get_ns();
-   vb2_set_plane_payload(>vb2_buf, 0, buf->size);
-   vb2_buffer_done(>vb2_buf, VB2_BUF_STATE_DONE);
-   dev_dbg(dcmi->dev, "buffer[%d] done seq=%d\n",
-   vbuf->vb2_buf.index, vbuf->sequence);
-
-   dcmi->buffers_count++;
-   dcmi->active = NULL;
-   }
-
-   /* Restart a new DMA transfer with next buffer */
-   if (dcmi->state == RUNNING) {
-   if (list_empty(>buffers)) {
-   dev_err(dcmi->dev, "%s: No more buffer queued, 
cannot capture buffer\n",
-   __func__);
-   dcmi->errors_count++;
-   dcmi->active = NULL;
-
-   spin_unlock_irq(>irqlock);
-   return;
-   }
-
-   dcmi->active = list_entry(dcmi->buffers.next,
- struct dcmi_buf, list);
-
-   list_del_init(&g

Re: [PATCH] media: stm32-dcmi: add JPEG support

2018-02-28 Thread Hugues FRUCHET
Hi Hans,

Yes depends on 'fix lock scheme', I'll send a v2 of this jpeg commit for 
unneeded V4L2_FMT_FLAG_COMPRESSED.

Best regards,
Hugues.

On 02/26/2018 03:17 PM, Hans Verkuil wrote:
> On 02/22/2018 10:51 AM, Hugues Fruchet wrote:
>> Add DCMI JPEG support.
> 
> Does this patch depend on the 'fix lock scheme' patch?
> 
> It looks good to me except for one small thing (see below), but I think this
> depends on the 'fix lock scheme' patch, right?
> 
>>
>> Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
>> ---
>>   drivers/media/platform/stm32/stm32-dcmi.c | 195 
>> +++---
>>   1 file changed, 148 insertions(+), 47 deletions(-)
>>
>> diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
>> b/drivers/media/platform/stm32/stm32-dcmi.c
>> index 269e963..7eaaf7c 100644
>> --- a/drivers/media/platform/stm32/stm32-dcmi.c
>> +++ b/drivers/media/platform/stm32/stm32-dcmi.c
>> @@ -93,6 +93,11 @@ enum state {
>>   #define MIN_HEIGHT 16U
>>   #define MAX_HEIGHT 2048U
>>   
>> +#define MIN_JPEG_WIDTH  16U
>> +#define MAX_JPEG_WIDTH  2592U
>> +#define MIN_JPEG_HEIGHT 16U
>> +#define MAX_JPEG_HEIGHT 2592U
>> +
>>   #define TIMEOUT_MS 1000
>>   
>>   struct dcmi_graph_entity {
>> @@ -191,14 +196,67 @@ static inline void reg_clear(void __iomem *base, u32 
>> reg, u32 mask)
>>   
>>   static int dcmi_start_capture(struct stm32_dcmi *dcmi);
>>   
>> +static void dcmi_buffer_done(struct stm32_dcmi *dcmi,
>> + struct dcmi_buf *buf,
>> + size_t bytesused,
>> + int err)
>> +{
>> +struct vb2_v4l2_buffer *vbuf;
>> +
>> +if (!buf)
>> +return;
>> +
>> +vbuf = >vb;
>> +
>> +vbuf->sequence = dcmi->sequence++;
>> +vbuf->field = V4L2_FIELD_NONE;
>> +vbuf->vb2_buf.timestamp = ktime_get_ns();
>> +vb2_set_plane_payload(>vb2_buf, 0, bytesused);
>> +vb2_buffer_done(>vb2_buf,
>> +err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
>> +dev_dbg(dcmi->dev, "buffer[%d] done seq=%d, bytesused=%zu\n",
>> +vbuf->vb2_buf.index, vbuf->sequence, bytesused);
>> +
>> +dcmi->buffers_count++;
>> +dcmi->active = NULL;
>> +}
>> +
>> +static int dcmi_restart_capture(struct stm32_dcmi *dcmi)
>> +{
>> +spin_lock_irq(>irqlock);
>> +
>> +if (dcmi->state != RUNNING) {
>> +spin_unlock_irq(>irqlock);
>> +return -EINVAL;
>> +}
>> +
>> +/* Restart a new DMA transfer with next buffer */
>> +if (list_empty(>buffers)) {
>> +dev_err(dcmi->dev, "%s: No more buffer queued, cannot capture 
>> buffer\n",
>> +__func__);
>> +dcmi->errors_count++;
>> +dcmi->active = NULL;
>> +
>> +spin_unlock_irq(>irqlock);
>> +return -EINVAL;
>> +}
>> +
>> +dcmi->active = list_entry(dcmi->buffers.next,
>> +  struct dcmi_buf, list);
>> +list_del_init(>active->list);
>> +
>> +spin_unlock_irq(>irqlock);
>> +
>> +return dcmi_start_capture(dcmi);
>> +}
>> +
>>   static void dcmi_dma_callback(void *param)
>>   {
>>  struct stm32_dcmi *dcmi = (struct stm32_dcmi *)param;
>>  struct dma_chan *chan = dcmi->dma_chan;
>>  struct dma_tx_state state;
>>  enum dma_status status;
>> -
>> -spin_lock_irq(>irqlock);
>> +struct dcmi_buf *buf = dcmi->active;
>>   
>>  /* Check DMA status */
>>  status = dmaengine_tx_status(chan, dcmi->dma_cookie, );
>> @@ -216,53 +274,18 @@ static void dcmi_dma_callback(void *param)
>>  case DMA_COMPLETE:
>>  dev_dbg(dcmi->dev, "%s: Received DMA_COMPLETE\n", __func__);
>>   
>> -if (dcmi->active) {
>> -struct dcmi_buf *buf = dcmi->active;
>> -struct vb2_v4l2_buffer *vbuf = >active->vb;
>> -
>> -vbuf->sequence = dcmi->sequence++;
>> -vbuf->field = V4L2_FIELD_NONE;
>> -vbuf->vb2_buf.timestamp = ktime_get_ns();
>> -vb2_set_plane_payload(>vb2_buf, 0, buf->size);
>> -vb2_

Re: [PATCH v2] media: stm32-dcmi: fix lock scheme

2018-02-28 Thread Hugues FRUCHET
Hi Hans,

Problem was that lock was held while calling dmaengine APIs, which 
causes double lock issue when dma callback was called under context of 
dma_async_issue_pending() (see dcmi_start_dma() & below comments).
Rest of changes are around spin_lock() changed to spin_lock_irq() for 
safer lock management.

On 02/26/2018 02:56 PM, Hans Verkuil wrote:
> On 02/22/2018 10:49 AM, Hugues Fruchet wrote:
>> Fix lock scheme leading to spurious freeze.
> 
> Can you elaborate a bit more? It's hard to review since you don't
> describe what was wrong and why this fixes the problem.
> 
> Regards,
> 
>   Hans
> 
>>
>> Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
>> ---
>> version 2:
>>- dcmi_buf_queue() refactor to avoid to have "else" after "return"
>>  (warning detected by checkpatch.pl --strict -f stm32-dcmi.c)
>>
>>   drivers/media/platform/stm32/stm32-dcmi.c | 57 
>> +--
>>   1 file changed, 24 insertions(+), 33 deletions(-)
>>
>> diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
>> b/drivers/media/platform/stm32/stm32-dcmi.c
>> index 2fd8bed..5de18ad 100644
>> --- a/drivers/media/platform/stm32/stm32-dcmi.c
>> +++ b/drivers/media/platform/stm32/stm32-dcmi.c
>> @@ -197,7 +197,7 @@ static void dcmi_dma_callback(void *param)
>>  struct dma_tx_state state;
>>  enum dma_status status;
>>   
>> -spin_lock(>irqlock);
>> +spin_lock_irq(>irqlock);
>>   
>>  /* Check DMA status */
>>  status = dmaengine_tx_status(chan, dcmi->dma_cookie, );
>> @@ -239,7 +239,7 @@ static void dcmi_dma_callback(void *param)
>>  dcmi->errors_count++;
>>  dcmi->active = NULL;
>>   
>> -spin_unlock(>irqlock);
>> +spin_unlock_irq(>irqlock);
>>  return;
>>  }
>>   
>> @@ -248,13 +248,11 @@ static void dcmi_dma_callback(void *param)
>>   
>>  list_del_init(>active->list);
>>   
>> -if (dcmi_start_capture(dcmi)) {
>> +spin_unlock_irq(>irqlock);

Lock is released here before calling dcmi_start_capture() which calls 
dma_async_issue_pending()

>> +if (dcmi_start_capture(dcmi))
>>  dev_err(dcmi->dev, "%s: Cannot restart capture 
>> on DMA complete\n",
>>  __func__);
>> -
>> -spin_unlock(>irqlock);
>> -return;
>> -}
>> +return;
>>  }
>>   
>>  break;
>> @@ -263,7 +261,7 @@ static void dcmi_dma_callback(void *param)
>>  break;
>>  }
>>   
>> -spin_unlock(>irqlock);
>> +spin_unlock_irq(>irqlock);
>>   }
>>   
>>   static int dcmi_start_dma(struct stm32_dcmi *dcmi,
>> @@ -360,7 +358,7 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
>>   {
>>  struct stm32_dcmi *dcmi = arg;
>>   
>> -spin_lock(>irqlock);
>> +spin_lock_irq(>irqlock);
>>   
>>  /* Stop capture is required */
>>  if (dcmi->state == STOPPING) {
>> @@ -370,7 +368,7 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
>>   
>>  complete(>complete);
>>   
>> -spin_unlock(>irqlock);
>> +spin_unlock_irq(>irqlock);
>>  return IRQ_HANDLED;
>>  }
>>   
>> @@ -383,35 +381,34 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
>>   __func__);
>>   
>>  dcmi->errors_count++;
>> -dmaengine_terminate_all(dcmi->dma_chan);
>> -
>>  dev_dbg(dcmi->dev, "Restarting capture after DCMI error\n");
>>   
>> -if (dcmi_start_capture(dcmi)) {
>> +spin_unlock_irq(>irqlock);
Lock is released here before calling dma APIs (terminate then 
async_issue_pending)

>> +dmaengine_terminate_all(dcmi->dma_chan);
>> +
>> +if (dcmi_start_capture(dcmi))
>>  dev_err(dcmi->dev, "%s: Cannot restart capture on 
>> overflow or error\n",
>>  __func__);
>> -
>> -spin_unlock(>irqlock);
>> -

[PATCH] media: stm32-dcmi: add JPEG support

2018-02-22 Thread Hugues Fruchet
Add DCMI JPEG support.

Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
---
 drivers/media/platform/stm32/stm32-dcmi.c | 195 +++---
 1 file changed, 148 insertions(+), 47 deletions(-)

diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
b/drivers/media/platform/stm32/stm32-dcmi.c
index 269e963..7eaaf7c 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -93,6 +93,11 @@ enum state {
 #define MIN_HEIGHT 16U
 #define MAX_HEIGHT 2048U
 
+#define MIN_JPEG_WIDTH 16U
+#define MAX_JPEG_WIDTH 2592U
+#define MIN_JPEG_HEIGHT16U
+#define MAX_JPEG_HEIGHT2592U
+
 #define TIMEOUT_MS 1000
 
 struct dcmi_graph_entity {
@@ -191,14 +196,67 @@ static inline void reg_clear(void __iomem *base, u32 reg, 
u32 mask)
 
 static int dcmi_start_capture(struct stm32_dcmi *dcmi);
 
+static void dcmi_buffer_done(struct stm32_dcmi *dcmi,
+struct dcmi_buf *buf,
+size_t bytesused,
+int err)
+{
+   struct vb2_v4l2_buffer *vbuf;
+
+   if (!buf)
+   return;
+
+   vbuf = >vb;
+
+   vbuf->sequence = dcmi->sequence++;
+   vbuf->field = V4L2_FIELD_NONE;
+   vbuf->vb2_buf.timestamp = ktime_get_ns();
+   vb2_set_plane_payload(>vb2_buf, 0, bytesused);
+   vb2_buffer_done(>vb2_buf,
+   err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+   dev_dbg(dcmi->dev, "buffer[%d] done seq=%d, bytesused=%zu\n",
+   vbuf->vb2_buf.index, vbuf->sequence, bytesused);
+
+   dcmi->buffers_count++;
+   dcmi->active = NULL;
+}
+
+static int dcmi_restart_capture(struct stm32_dcmi *dcmi)
+{
+   spin_lock_irq(>irqlock);
+
+   if (dcmi->state != RUNNING) {
+   spin_unlock_irq(>irqlock);
+   return -EINVAL;
+   }
+
+   /* Restart a new DMA transfer with next buffer */
+   if (list_empty(>buffers)) {
+   dev_err(dcmi->dev, "%s: No more buffer queued, cannot capture 
buffer\n",
+   __func__);
+   dcmi->errors_count++;
+   dcmi->active = NULL;
+
+   spin_unlock_irq(>irqlock);
+   return -EINVAL;
+   }
+
+   dcmi->active = list_entry(dcmi->buffers.next,
+ struct dcmi_buf, list);
+   list_del_init(>active->list);
+
+   spin_unlock_irq(>irqlock);
+
+   return dcmi_start_capture(dcmi);
+}
+
 static void dcmi_dma_callback(void *param)
 {
struct stm32_dcmi *dcmi = (struct stm32_dcmi *)param;
struct dma_chan *chan = dcmi->dma_chan;
struct dma_tx_state state;
enum dma_status status;
-
-   spin_lock_irq(>irqlock);
+   struct dcmi_buf *buf = dcmi->active;
 
/* Check DMA status */
status = dmaengine_tx_status(chan, dcmi->dma_cookie, );
@@ -216,53 +274,18 @@ static void dcmi_dma_callback(void *param)
case DMA_COMPLETE:
dev_dbg(dcmi->dev, "%s: Received DMA_COMPLETE\n", __func__);
 
-   if (dcmi->active) {
-   struct dcmi_buf *buf = dcmi->active;
-   struct vb2_v4l2_buffer *vbuf = >active->vb;
-
-   vbuf->sequence = dcmi->sequence++;
-   vbuf->field = V4L2_FIELD_NONE;
-   vbuf->vb2_buf.timestamp = ktime_get_ns();
-   vb2_set_plane_payload(>vb2_buf, 0, buf->size);
-   vb2_buffer_done(>vb2_buf, VB2_BUF_STATE_DONE);
-   dev_dbg(dcmi->dev, "buffer[%d] done seq=%d\n",
-   vbuf->vb2_buf.index, vbuf->sequence);
-
-   dcmi->buffers_count++;
-   dcmi->active = NULL;
-   }
-
-   /* Restart a new DMA transfer with next buffer */
-   if (dcmi->state == RUNNING) {
-   if (list_empty(>buffers)) {
-   dev_err(dcmi->dev, "%s: No more buffer queued, 
cannot capture buffer\n",
-   __func__);
-   dcmi->errors_count++;
-   dcmi->active = NULL;
-
-   spin_unlock_irq(>irqlock);
-   return;
-   }
-
-   dcmi->active = list_entry(dcmi->buffers.next,
- struct dcmi_buf, list);
-
-   list_del_init(>active->list);
-
-   spin_unlock_irq(>irqlock);
-   if (dcmi_start_capture(dcmi))
-   dev_err(dcmi->dev, "%s: Cannot res

[PATCH] media: stm32-dcmi: fix unnecessary parentheses

2018-02-22 Thread Hugues Fruchet
Fix unnecessary parentheses in if conditions.
Detected by checkpatch.pl --strict.

Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
---
 drivers/media/platform/stm32/stm32-dcmi.c | 12 ++--
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
b/drivers/media/platform/stm32/stm32-dcmi.c
index 536c0d5..269e963 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -477,7 +477,7 @@ static void dcmi_buf_queue(struct vb2_buffer *vb)
 
spin_lock_irq(>irqlock);
 
-   if ((dcmi->state == RUNNING) && (!dcmi->active)) {
+   if (dcmi->state == RUNNING && !dcmi->active) {
dcmi->active = buf;
 
dev_dbg(dcmi->dev, "Starting capture on buffer[%d] queued\n",
@@ -730,7 +730,7 @@ static void __find_outer_frame_size(struct stm32_dcmi *dcmi,
int h_err = (fsize->height - pix->height);
int err = w_err + h_err;
 
-   if ((w_err >= 0) && (h_err >= 0) && (err < min_err)) {
+   if (w_err >= 0 && h_err >= 0 && err < min_err) {
min_err = err;
match = fsize;
}
@@ -1065,10 +1065,10 @@ static int dcmi_s_selection(struct file *file, void 
*priv,
r.top  = clamp_t(s32, r.top, 0, pix.height - r.height);
r.left = clamp_t(s32, r.left, 0, pix.width - r.width);
 
-   if (!((r.top == dcmi->sd_bounds.top) &&
- (r.left == dcmi->sd_bounds.left) &&
- (r.width == dcmi->sd_bounds.width) &&
- (r.height == dcmi->sd_bounds.height))) {
+   if (!(r.top == dcmi->sd_bounds.top &&
+ r.left == dcmi->sd_bounds.left &&
+ r.width == dcmi->sd_bounds.width &&
+ r.height == dcmi->sd_bounds.height)) {
/* Crop if request is different than sensor resolution */
dcmi->do_crop = true;
dcmi->crop = r;
-- 
1.9.1



[PATCH v2] media: stm32-dcmi: rework overrun/error case

2018-02-22 Thread Hugues Fruchet
Do not stop/restart dma on overrun or errors.
Dma will be restarted on current frame transfer
completion. Frame transfer completion is ensured
even if overrun or error occurs by DCMI continuous
capture mode which restarts data transfer at next
frame sync.
Do no warn on overrun while in irq thread, this slows down
system and lead to more overrun errors. Use a counter
instead and log errors at stop streaming.

Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
---
version 2:
  - Minor: remove extra line at end of dcmi_stop_streaming()

  drivers/media/platform/stm32/stm32-dcmi.c | 29 +++--
 1 file changed, 11 insertions(+), 18 deletions(-)

diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
b/drivers/media/platform/stm32/stm32-dcmi.c
index 5de18ad..536c0d5 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -160,6 +160,7 @@ struct stm32_dcmi {
dma_cookie_tdma_cookie;
u32 misr;
int errors_count;
+   int overrun_count;
int buffers_count;
 };
 
@@ -373,23 +374,9 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
}
 
if ((dcmi->misr & IT_OVR) || (dcmi->misr & IT_ERR)) {
-   /*
-* An overflow or an error has been detected,
-* stop current DMA transfert & restart it
-*/
-   dev_warn(dcmi->dev, "%s: Overflow or error detected\n",
-__func__);
-
dcmi->errors_count++;
-   dev_dbg(dcmi->dev, "Restarting capture after DCMI error\n");
-
-   spin_unlock_irq(>irqlock);
-   dmaengine_terminate_all(dcmi->dma_chan);
-
-   if (dcmi_start_capture(dcmi))
-   dev_err(dcmi->dev, "%s: Cannot restart capture on 
overflow or error\n",
-   __func__);
-   return IRQ_HANDLED;
+   if (dcmi->misr & IT_OVR)
+   dcmi->overrun_count++;
}
 
spin_unlock_irq(>irqlock);
@@ -572,6 +559,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, 
unsigned int count)
 
dcmi->sequence = 0;
dcmi->errors_count = 0;
+   dcmi->overrun_count = 0;
dcmi->buffers_count = 0;
dcmi->active = NULL;
 
@@ -682,8 +670,13 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
 
clk_disable(dcmi->mclk);
 
-   dev_dbg(dcmi->dev, "Stop streaming, errors=%d buffers=%d\n",
-   dcmi->errors_count, dcmi->buffers_count);
+   if (dcmi->errors_count)
+   dev_warn(dcmi->dev, "Some errors found while streaming: 
errors=%d (overrun=%d), buffers=%d\n",
+dcmi->errors_count, dcmi->overrun_count,
+dcmi->buffers_count);
+   dev_dbg(dcmi->dev, "Stop streaming, errors=%d (overrun=%d), 
buffers=%d\n",
+   dcmi->errors_count, dcmi->overrun_count,
+   dcmi->buffers_count);
 }
 
 static const struct vb2_ops dcmi_video_qops = {
-- 
1.9.1



[PATCH v2] media: stm32-dcmi: fix lock scheme

2018-02-22 Thread Hugues Fruchet
Fix lock scheme leading to spurious freeze.

Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
---
version 2:
  - dcmi_buf_queue() refactor to avoid to have "else" after "return"
(warning detected by checkpatch.pl --strict -f stm32-dcmi.c)

 drivers/media/platform/stm32/stm32-dcmi.c | 57 +--
 1 file changed, 24 insertions(+), 33 deletions(-)

diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
b/drivers/media/platform/stm32/stm32-dcmi.c
index 2fd8bed..5de18ad 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -197,7 +197,7 @@ static void dcmi_dma_callback(void *param)
struct dma_tx_state state;
enum dma_status status;
 
-   spin_lock(>irqlock);
+   spin_lock_irq(>irqlock);
 
/* Check DMA status */
status = dmaengine_tx_status(chan, dcmi->dma_cookie, );
@@ -239,7 +239,7 @@ static void dcmi_dma_callback(void *param)
dcmi->errors_count++;
dcmi->active = NULL;
 
-   spin_unlock(>irqlock);
+   spin_unlock_irq(>irqlock);
return;
}
 
@@ -248,13 +248,11 @@ static void dcmi_dma_callback(void *param)
 
list_del_init(>active->list);
 
-   if (dcmi_start_capture(dcmi)) {
+   spin_unlock_irq(>irqlock);
+   if (dcmi_start_capture(dcmi))
dev_err(dcmi->dev, "%s: Cannot restart capture 
on DMA complete\n",
__func__);
-
-   spin_unlock(>irqlock);
-   return;
-   }
+   return;
}
 
break;
@@ -263,7 +261,7 @@ static void dcmi_dma_callback(void *param)
break;
}
 
-   spin_unlock(>irqlock);
+   spin_unlock_irq(>irqlock);
 }
 
 static int dcmi_start_dma(struct stm32_dcmi *dcmi,
@@ -360,7 +358,7 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
 {
struct stm32_dcmi *dcmi = arg;
 
-   spin_lock(>irqlock);
+   spin_lock_irq(>irqlock);
 
/* Stop capture is required */
if (dcmi->state == STOPPING) {
@@ -370,7 +368,7 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
 
complete(>complete);
 
-   spin_unlock(>irqlock);
+   spin_unlock_irq(>irqlock);
return IRQ_HANDLED;
}
 
@@ -383,35 +381,34 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
 __func__);
 
dcmi->errors_count++;
-   dmaengine_terminate_all(dcmi->dma_chan);
-
dev_dbg(dcmi->dev, "Restarting capture after DCMI error\n");
 
-   if (dcmi_start_capture(dcmi)) {
+   spin_unlock_irq(>irqlock);
+   dmaengine_terminate_all(dcmi->dma_chan);
+
+   if (dcmi_start_capture(dcmi))
dev_err(dcmi->dev, "%s: Cannot restart capture on 
overflow or error\n",
__func__);
-
-   spin_unlock(>irqlock);
-   return IRQ_HANDLED;
-   }
+   return IRQ_HANDLED;
}
 
-   spin_unlock(>irqlock);
+   spin_unlock_irq(>irqlock);
return IRQ_HANDLED;
 }
 
 static irqreturn_t dcmi_irq_callback(int irq, void *arg)
 {
struct stm32_dcmi *dcmi = arg;
+   unsigned long flags;
 
-   spin_lock(>irqlock);
+   spin_lock_irqsave(>irqlock, flags);
 
dcmi->misr = reg_read(dcmi->regs, DCMI_MIS);
 
/* Clear interrupt */
reg_set(dcmi->regs, DCMI_ICR, IT_FRAME | IT_OVR | IT_ERR);
 
-   spin_unlock(>irqlock);
+   spin_unlock_irqrestore(>irqlock, flags);
 
return IRQ_WAKE_THREAD;
 }
@@ -490,9 +487,8 @@ static void dcmi_buf_queue(struct vb2_buffer *vb)
struct stm32_dcmi *dcmi =  vb2_get_drv_priv(vb->vb2_queue);
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct dcmi_buf *buf = container_of(vbuf, struct dcmi_buf, vb);
-   unsigned long flags = 0;
 
-   spin_lock_irqsave(>irqlock, flags);
+   spin_lock_irq(>irqlock);
 
if ((dcmi->state == RUNNING) && (!dcmi->active)) {
dcmi->active = buf;
@@ -500,19 +496,15 @@ static void dcmi_buf_queue(struct vb2_buffer *vb)
dev_dbg(dcmi->dev, "Starting capture on buffer[%d] queued\n",
buf->vb.vb2_buf.index);
 
-   if (dcmi_start_capture(dcmi)) {
+   spin_unlock_irq(>irqlock);
+   if (dcmi_start_capture(dc

[PATCH v2] media: ov5640: fix framerate update

2018-02-08 Thread Hugues Fruchet
After a framerate update through s_frame_interval(), the new
framerate was not taken into account when streaming,
but was taken into account on next session.
This was due to sensor->current_mode not updated accordingly to new
framerate setting in ov5640_s_frame_interval().

Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
---
 drivers/media/i2c/ov5640.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 3e7b43c..03940f0 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -2374,6 +2374,8 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
 
sensor->current_fr = frame_rate;
sensor->frame_interval = fi->interval;
+   sensor->current_mode = ov5640_find_mode(sensor, frame_rate, mode->width,
+   mode->height, true);
sensor->pending_mode_change = true;
 out:
mutex_unlock(>lock);
-- 
1.9.1



[PATCH] media: ov5640: fix framerate update

2018-02-08 Thread Hugues Fruchet
After a framerate update through s_frame_interval(), the new
framerate was not taken into account when streaming,
but was taken into account on next session.
This was due to sensor->current_mode not updated accordingly to new
framerate setting in ov5640_s_frame_interval().

Change-Id: I6d62510b708c181ec0310601b6e4fa1be06ffe90
Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
---
 drivers/media/i2c/ov5640.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 3e7b43c..03940f0 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -2374,6 +2374,8 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd,
 
sensor->current_fr = frame_rate;
sensor->frame_interval = fi->interval;
+   sensor->current_mode = ov5640_find_mode(sensor, frame_rate, mode->width,
+   mode->height, true);
sensor->pending_mode_change = true;
 out:
mutex_unlock(>lock);
-- 
1.9.1



Re: [PATCH] media: stm32-dcmi: add g/s_parm framerate support

2018-02-08 Thread Hugues FRUCHET
Thanks Hans,
v2 sent, rebased on your helpers !
Best regards,
Hugues.

On 02/07/2018 06:52 PM, Hans Verkuil wrote:
> On 02/07/2018 06:43 PM, Hugues Fruchet wrote:
>> Add g/s_parm framerate support by calling subdev
>> g/s_frame_interval ops.
>> This allows user to control sensor framerate by
>> calling ioctl G/S_PARM.
>>
>> Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
>> ---
>>   drivers/media/platform/stm32/stm32-dcmi.c | 49 
>> +++
>>   1 file changed, 49 insertions(+)
>>
>> diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
>> b/drivers/media/platform/stm32/stm32-dcmi.c
>> index ab555d4..8197554 100644
>> --- a/drivers/media/platform/stm32/stm32-dcmi.c
>> +++ b/drivers/media/platform/stm32/stm32-dcmi.c
>> @@ -1151,6 +1151,52 @@ static int dcmi_enum_framesizes(struct file *file, 
>> void *fh,
>>  return 0;
>>   }
>>   
>> +static int dcmi_g_parm(struct file *file, void *priv,
>> +   struct v4l2_streamparm *p)
>> +{
>> +struct stm32_dcmi *dcmi = video_drvdata(file);
>> +struct v4l2_subdev_frame_interval ival = { 0 };
>> +int ret;
>> +
>> +if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
>> +return -EINVAL;
>> +
>> +p->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
>> +ret = v4l2_subdev_call(dcmi->entity.subdev, video,
>> +   g_frame_interval, );
>> +if (ret)
>> +return ret;
>> +
>> +p->parm.capture.timeperframe = ival.interval;
>> +
>> +return ret;
>> +}
> 
> This function and the next can be simplified by using the help functions
> introduced here:
> 
> https://git.linuxtv.org/hverkuil/media_tree.git/log/?h=parm
> 
> I'll make a pull request for this later this week, so it's probably a good
> idea to base your code on this as well.
> 
> Regards,
> 
>   Hans
> 
>> +
>> +static int dcmi_s_parm(struct file *file, void *priv,
>> +   struct v4l2_streamparm *p)
>> +{
>> +struct stm32_dcmi *dcmi = video_drvdata(file);
>> +struct v4l2_subdev_frame_interval ival = {
>> +0,
>> +p->parm.capture.timeperframe
>> +};
>> +int ret;
>> +
>> +if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
>> +return -EINVAL;
>> +
>> +memset(>parm, 0, sizeof(p->parm));
>> +p->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
>> +ret = v4l2_subdev_call(dcmi->entity.subdev, video,
>> +   s_frame_interval, );
>> +if (ret)
>> +return ret;
>> +
>> +p->parm.capture.timeperframe = ival.interval;
>> +
>> +return ret;
>> +}
>> +
>>   static int dcmi_enum_frameintervals(struct file *file, void *fh,
>>  struct v4l2_frmivalenum *fival)
>>   {
>> @@ -1253,6 +1299,9 @@ static int dcmi_release(struct file *file)
>>  .vidioc_g_input = dcmi_g_input,
>>  .vidioc_s_input = dcmi_s_input,
>>   
>> +.vidioc_g_parm  = dcmi_g_parm,
>> +.vidioc_s_parm  = dcmi_s_parm,
>> +
>>  .vidioc_enum_framesizes = dcmi_enum_framesizes,
>>  .vidioc_enum_frameintervals = dcmi_enum_frameintervals,
>>   
>>
> 

[PATCH v2] media: stm32-dcmi: add g/s_parm framerate support

2018-02-08 Thread Hugues Fruchet
Add g/s_parm framerate support by calling subdev
g/s_frame_interval ops.
This allows user to control sensor framerate by
calling ioctl G/S_PARM.

Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
---
version 2:
  - Rebase on Hans branch to use new helpers:
See https://www.mail-archive.com/linux-media@vger.kernel.org/msg125769.html

 drivers/media/platform/stm32/stm32-dcmi.c | 19 +++
 1 file changed, 19 insertions(+)

diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
b/drivers/media/platform/stm32/stm32-dcmi.c
index 9460b30..60dd24c 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -1167,6 +1167,22 @@ static int dcmi_enum_framesizes(struct file *file, void 
*fh,
return 0;
 }
 
+static int dcmi_g_parm(struct file *file, void *priv,
+  struct v4l2_streamparm *p)
+{
+   struct stm32_dcmi *dcmi = video_drvdata(file);
+
+   return v4l2_g_parm_cap(video_devdata(file), dcmi->entity.subdev, p);
+}
+
+static int dcmi_s_parm(struct file *file, void *priv,
+  struct v4l2_streamparm *p)
+{
+   struct stm32_dcmi *dcmi = video_drvdata(file);
+
+   return v4l2_s_parm_cap(video_devdata(file), dcmi->entity.subdev, p);
+}
+
 static int dcmi_enum_frameintervals(struct file *file, void *fh,
struct v4l2_frmivalenum *fival)
 {
@@ -1269,6 +1285,9 @@ static int dcmi_release(struct file *file)
.vidioc_g_input = dcmi_g_input,
.vidioc_s_input = dcmi_s_input,
 
+   .vidioc_g_parm  = dcmi_g_parm,
+   .vidioc_s_parm  = dcmi_s_parm,
+
.vidioc_enum_framesizes = dcmi_enum_framesizes,
.vidioc_enum_frameintervals = dcmi_enum_frameintervals,
 
-- 
1.9.1



[PATCH] media: stm32-dcmi: add g/s_parm framerate support

2018-02-07 Thread Hugues Fruchet
Add g/s_parm framerate support by calling subdev
g/s_frame_interval ops.
This allows user to control sensor framerate by
calling ioctl G/S_PARM.

Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
---
 drivers/media/platform/stm32/stm32-dcmi.c | 49 +++
 1 file changed, 49 insertions(+)

diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
b/drivers/media/platform/stm32/stm32-dcmi.c
index ab555d4..8197554 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -1151,6 +1151,52 @@ static int dcmi_enum_framesizes(struct file *file, void 
*fh,
return 0;
 }
 
+static int dcmi_g_parm(struct file *file, void *priv,
+  struct v4l2_streamparm *p)
+{
+   struct stm32_dcmi *dcmi = video_drvdata(file);
+   struct v4l2_subdev_frame_interval ival = { 0 };
+   int ret;
+
+   if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+   return -EINVAL;
+
+   p->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+   ret = v4l2_subdev_call(dcmi->entity.subdev, video,
+  g_frame_interval, );
+   if (ret)
+   return ret;
+
+   p->parm.capture.timeperframe = ival.interval;
+
+   return ret;
+}
+
+static int dcmi_s_parm(struct file *file, void *priv,
+  struct v4l2_streamparm *p)
+{
+   struct stm32_dcmi *dcmi = video_drvdata(file);
+   struct v4l2_subdev_frame_interval ival = {
+   0,
+   p->parm.capture.timeperframe
+   };
+   int ret;
+
+   if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+   return -EINVAL;
+
+   memset(>parm, 0, sizeof(p->parm));
+   p->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+   ret = v4l2_subdev_call(dcmi->entity.subdev, video,
+  s_frame_interval, );
+   if (ret)
+   return ret;
+
+   p->parm.capture.timeperframe = ival.interval;
+
+   return ret;
+}
+
 static int dcmi_enum_frameintervals(struct file *file, void *fh,
struct v4l2_frmivalenum *fival)
 {
@@ -1253,6 +1299,9 @@ static int dcmi_release(struct file *file)
.vidioc_g_input = dcmi_g_input,
.vidioc_s_input = dcmi_s_input,
 
+   .vidioc_g_parm  = dcmi_g_parm,
+   .vidioc_s_parm  = dcmi_s_parm,
+
.vidioc_enum_framesizes = dcmi_enum_framesizes,
.vidioc_enum_frameintervals = dcmi_enum_frameintervals,
 
-- 
1.9.1



[PATCH] media: stm32-dcmi: rework overrun/error case

2018-02-07 Thread Hugues Fruchet
Do not stop/restart dma on overrun or errors.
Dma will be restarted on current frame transfer
completion. Frame transfer completion is ensured
even if overrun or error occurs by DCMI continuous
capture mode which restarts data transfer at next
frame sync.
Do no warn on overrun while in irq thread, this slows down
system and lead to more overrun errors. Use a counter
instead and log errors at stop streaming.

Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
---
 drivers/media/platform/stm32/stm32-dcmi.c | 30 --
 1 file changed, 12 insertions(+), 18 deletions(-)

diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
b/drivers/media/platform/stm32/stm32-dcmi.c
index 4a75756..ab555d4 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -160,6 +160,7 @@ struct stm32_dcmi {
dma_cookie_tdma_cookie;
u32 misr;
int errors_count;
+   int overrun_count;
int buffers_count;
 };
 
@@ -373,23 +374,9 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
}
 
if ((dcmi->misr & IT_OVR) || (dcmi->misr & IT_ERR)) {
-   /*
-* An overflow or an error has been detected,
-* stop current DMA transfert & restart it
-*/
-   dev_warn(dcmi->dev, "%s: Overflow or error detected\n",
-__func__);
-
dcmi->errors_count++;
-   dev_dbg(dcmi->dev, "Restarting capture after DCMI error\n");
-
-   spin_unlock_irq(>irqlock);
-   dmaengine_terminate_all(dcmi->dma_chan);
-
-   if (dcmi_start_capture(dcmi))
-   dev_err(dcmi->dev, "%s: Cannot restart capture on 
overflow or error\n",
-   __func__);
-   return IRQ_HANDLED;
+   if (dcmi->misr & IT_OVR)
+   dcmi->overrun_count++;
}
 
spin_unlock_irq(>irqlock);
@@ -574,6 +561,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, 
unsigned int count)
 
dcmi->sequence = 0;
dcmi->errors_count = 0;
+   dcmi->overrun_count = 0;
dcmi->buffers_count = 0;
dcmi->active = NULL;
 
@@ -684,8 +672,14 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
 
clk_disable(dcmi->mclk);
 
-   dev_dbg(dcmi->dev, "Stop streaming, errors=%d buffers=%d\n",
-   dcmi->errors_count, dcmi->buffers_count);
+   if (dcmi->errors_count)
+   dev_warn(dcmi->dev, "Some errors found while streaming: 
errors=%d (overrun=%d), buffers=%d\n",
+dcmi->errors_count, dcmi->overrun_count,
+dcmi->buffers_count);
+   dev_dbg(dcmi->dev, "Stop streaming, errors=%d (overrun=%d), 
buffers=%d\n",
+   dcmi->errors_count, dcmi->overrun_count,
+   dcmi->buffers_count);
+
 }
 
 static const struct vb2_ops dcmi_video_qops = {
-- 
1.9.1



[PATCH] media: stm32-dcmi: fix lock scheme

2018-02-07 Thread Hugues Fruchet
Fix lock scheme leading to spurious freeze.

Signed-off-by: Hugues Fruchet <hugues.fruc...@st.com>
---
 drivers/media/platform/stm32/stm32-dcmi.c | 57 ++-
 1 file changed, 25 insertions(+), 32 deletions(-)

diff --git a/drivers/media/platform/stm32/stm32-dcmi.c 
b/drivers/media/platform/stm32/stm32-dcmi.c
index 2fd8bed..4a75756 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -197,7 +197,7 @@ static void dcmi_dma_callback(void *param)
struct dma_tx_state state;
enum dma_status status;
 
-   spin_lock(>irqlock);
+   spin_lock_irq(>irqlock);
 
/* Check DMA status */
status = dmaengine_tx_status(chan, dcmi->dma_cookie, );
@@ -239,7 +239,7 @@ static void dcmi_dma_callback(void *param)
dcmi->errors_count++;
dcmi->active = NULL;
 
-   spin_unlock(>irqlock);
+   spin_unlock_irq(>irqlock);
return;
}
 
@@ -248,13 +248,11 @@ static void dcmi_dma_callback(void *param)
 
list_del_init(>active->list);
 
-   if (dcmi_start_capture(dcmi)) {
+   spin_unlock_irq(>irqlock);
+   if (dcmi_start_capture(dcmi))
dev_err(dcmi->dev, "%s: Cannot restart capture 
on DMA complete\n",
__func__);
-
-   spin_unlock(>irqlock);
-   return;
-   }
+   return;
}
 
break;
@@ -263,7 +261,7 @@ static void dcmi_dma_callback(void *param)
break;
}
 
-   spin_unlock(>irqlock);
+   spin_unlock_irq(>irqlock);
 }
 
 static int dcmi_start_dma(struct stm32_dcmi *dcmi,
@@ -360,7 +358,7 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
 {
struct stm32_dcmi *dcmi = arg;
 
-   spin_lock(>irqlock);
+   spin_lock_irq(>irqlock);
 
/* Stop capture is required */
if (dcmi->state == STOPPING) {
@@ -370,7 +368,7 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
 
complete(>complete);
 
-   spin_unlock(>irqlock);
+   spin_unlock_irq(>irqlock);
return IRQ_HANDLED;
}
 
@@ -383,35 +381,34 @@ static irqreturn_t dcmi_irq_thread(int irq, void *arg)
 __func__);
 
dcmi->errors_count++;
-   dmaengine_terminate_all(dcmi->dma_chan);
-
dev_dbg(dcmi->dev, "Restarting capture after DCMI error\n");
 
-   if (dcmi_start_capture(dcmi)) {
+   spin_unlock_irq(>irqlock);
+   dmaengine_terminate_all(dcmi->dma_chan);
+
+   if (dcmi_start_capture(dcmi))
dev_err(dcmi->dev, "%s: Cannot restart capture on 
overflow or error\n",
__func__);
-
-   spin_unlock(>irqlock);
-   return IRQ_HANDLED;
-   }
+   return IRQ_HANDLED;
}
 
-   spin_unlock(>irqlock);
+   spin_unlock_irq(>irqlock);
return IRQ_HANDLED;
 }
 
 static irqreturn_t dcmi_irq_callback(int irq, void *arg)
 {
struct stm32_dcmi *dcmi = arg;
+   unsigned long flags;
 
-   spin_lock(>irqlock);
+   spin_lock_irqsave(>irqlock, flags);
 
dcmi->misr = reg_read(dcmi->regs, DCMI_MIS);
 
/* Clear interrupt */
reg_set(dcmi->regs, DCMI_ICR, IT_FRAME | IT_OVR | IT_ERR);
 
-   spin_unlock(>irqlock);
+   spin_unlock_irqrestore(>irqlock, flags);
 
return IRQ_WAKE_THREAD;
 }
@@ -490,9 +487,8 @@ static void dcmi_buf_queue(struct vb2_buffer *vb)
struct stm32_dcmi *dcmi =  vb2_get_drv_priv(vb->vb2_queue);
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct dcmi_buf *buf = container_of(vbuf, struct dcmi_buf, vb);
-   unsigned long flags = 0;
 
-   spin_lock_irqsave(>irqlock, flags);
+   spin_lock_irq(>irqlock);
 
if ((dcmi->state == RUNNING) && (!dcmi->active)) {
dcmi->active = buf;
@@ -500,19 +496,17 @@ static void dcmi_buf_queue(struct vb2_buffer *vb)
dev_dbg(dcmi->dev, "Starting capture on buffer[%d] queued\n",
buf->vb.vb2_buf.index);
 
-   if (dcmi_start_capture(dcmi)) {
+   spin_unlock_irq(>irqlock);
+   if (dcmi_start_capture(dcmi))
dev_err(dcmi->dev, "%s: Cannot restart capture on 
overflow or error\n",
__func__);
-
- 

  1   2   3   4   5   >