Skip to content

Conversation

cornway
Copy link

@cornway cornway commented Aug 29, 2025

Enabling proper zoom/scaling for smaller resolutions (e.g. QQVGA)
This is basically a copy paste from linux driver with some tweaks

This patch may need some refactoring/cleaning up, but generally I have verified it function properly
I found that without this patch, if i enable low resolution format (e.g. QVGA/QQVGA) I basically receive cropped image (top left corner) not scaled, so it makes it difficult to receive meaningfull image from camera for further processing

@josuah
Copy link
Contributor

josuah commented Aug 29, 2025

Thanks for the contribution!
However, Linux license is GPLv2 so it's not really possible to copy-paste from it.
Also, some possibly better way to handle it would be to allow the user to pass exactly any size they want.
Since it's computed from a macro, that might allow some runtime customization?

I think it supports arbitrary scaling, so this could be a good fit for the video_set_selection() API to allow the user to crop/scale at any supported size.

Copy link
Member

@dsseng dsseng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is basically a copy paste from linux driver with some tweaks

Please notice the ov2640 driver in Linux has no dual-licensing, and GPLv2 must not be copied into Zephyr. There seems to not be a significant code added, so let's wait for another review

P.S. Also please consider the SonarQube warnings

Comment on lines 571 to 582
OV2640_VIDEO_FORMAT_CAP(QQVGA_WIDTH, QQVGA_HEIGHT, VIDEO_PIX_FMT_RGB565), /* QQVGA */
OV2640_VIDEO_FORMAT_CAP(QCIF_WIDTH, QCIF_HEIGHT, VIDEO_PIX_FMT_RGB565), /* QCIF */
OV2640_VIDEO_FORMAT_CAP(CIF_WIDTH, CIF_HEIGHT, VIDEO_PIX_FMT_RGB565), /* CIF */
OV2640_VIDEO_FORMAT_CAP(VGA_WIDTH, VGA_HEIGHT, VIDEO_PIX_FMT_RGB565), /* VGA */
OV2640_VIDEO_FORMAT_CAP(SVGA_WIDTH, SVGA_HEIGHT, VIDEO_PIX_FMT_RGB565), /* SVGA */
OV2640_VIDEO_FORMAT_CAP(XGA_WIDTH, XGA_HEIGHT, VIDEO_PIX_FMT_RGB565), /* XVGA */
OV2640_VIDEO_FORMAT_CAP(SXGA_WIDTH, SXGA_HEIGHT, VIDEO_PIX_FMT_RGB565), /* SXGA */
OV2640_VIDEO_FORMAT_CAP(UXGA_WIDTH, UXGA_HEIGHT, VIDEO_PIX_FMT_RGB565), /* UXGA */
OV2640_VIDEO_FORMAT_CAP(QQVGA_WIDTH, QQVGA_HEIGHT, VIDEO_PIX_FMT_JPEG), /* QQVGA */
OV2640_VIDEO_FORMAT_CAP(QCIF_WIDTH, QCIF_HEIGHT, VIDEO_PIX_FMT_JPEG), /* QCIF */
OV2640_VIDEO_FORMAT_CAP(CIF_WIDTH, CIF_HEIGHT, VIDEO_PIX_FMT_JPEG), /* CIF */
OV2640_VIDEO_FORMAT_CAP(VGA_WIDTH, VGA_HEIGHT, VIDEO_PIX_FMT_JPEG), /* VGA */
OV2640_VIDEO_FORMAT_CAP(SVGA_WIDTH, SVGA_HEIGHT, VIDEO_PIX_FMT_JPEG), /* SVGA */
OV2640_VIDEO_FORMAT_CAP(XGA_WIDTH, XGA_HEIGHT, VIDEO_PIX_FMT_JPEG), /* XVGA */
OV2640_VIDEO_FORMAT_CAP(SXGA_WIDTH, SXGA_HEIGHT, VIDEO_PIX_FMT_JPEG), /* SXGA */
OV2640_VIDEO_FORMAT_CAP(UXGA_WIDTH, UXGA_HEIGHT, VIDEO_PIX_FMT_JPEG), /* UXGA */
{0}};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if breaking these out to constants is wanted

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be hard to remember what size is UXGA or QCIF, while 1600x1200 or 176x144 is obvious for the reader.
Maybe the goal was avoiding accidental typos?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then it would make sense to add numeric resolutions to the comments here, so they do not duplicate what's in the code

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was done to match window setting, so if format is meant to be supported - it is known that there is a scaling/windowing setting for it
Thank you

@cornway
Copy link
Author

cornway commented Sep 1, 2025

video_set_selection

Thank you for your comment
Seems video_set_selection() is not relevant here as it uses "st_stm32_dcmipp" compatible driver, which is not present in the device I'm working with
In my opinion, If I tell camera to output WxY resolution, I expect whole frame fit into that resolution, not the cropped part, especially if it is not obvious where that cropped region came from
OV2640 registers description is very vague, and this approach seems only working at the moment
I can refactor this code to avoid copy-pasting
Thank you, waiting for your reply

@cornway
Copy link
Author

cornway commented Sep 1, 2025

Honestly, I just wanted to ease the pain for those still working with that type of sensors, it took me some time to figure this out

Thanks for the contribution! However, Linux license is GPLv2 so it's not really possible to copy-paste from it. Also, some possibly better way to handle it would be to allow the user to pass exactly any size they want. Since it's computed from a macro, that might allow some runtime customization?

I think it supports arbitrary scaling, so this could be a good fit for the video_set_selection() API to allow the user to crop/scale at any supported size.

One more note: there is no possibility (seems to me) of having "any size" user wants, as there are a bunch of prescalers and dividers to be set to downsize properly, not every size fits into that combination
Thank you for your patience and time

#define UXGA_HSIZE (1600UL)
#define UXGA_VSIZE (1200UL)

#define VAL_SET(x, mask, rshift, lshift) ((((x) >> rshift) & mask) << lshift)
Copy link
Contributor

@josuah josuah Sep 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this different from FIELD_PREP()? This would also break the chain from GPL code to Zephyr to modify this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks similar, but in FIELD_PREP() mask must be pre-computed properly, there is no *shift arguments
Thank you for pointing that out, I think in that case I can just plug in FIELD_PREP

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no shift argument, but the macros can understand where the mask starts and ends, and shifts the value accordingly.

representing its field position and width [1]

To pre-compute the mask, you can use GENMASK(lsb, msb), these two are typically used together and allow to use the value from the datasheet without risk of typo by manual tweaking the masks:

#define THIS_FIELD_SET(x) FIELD_PREP(GENMASK(7, 3), x)

Or even better IMHO, just a plain transcription of the datasheet data without any smart:

#define THIS_FIELD_MASK GENMASK(7, 3)

Then in the code:

    {THIS_ADDR, FIELD_PREP(THIS_FIELD_MASK, 323)},

LOG_INF("Selected resolution %s", win->name);

/* Write DSP input registers */
ret |= ov2640_write_all(dev, uxga_regs, ARRAY_SIZE(uxga_regs));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For new code, would it be possible to use this even if it breaks the local style? Only if interested in helping with it, and no need to change the other locations.

	ret = ov2640_write_all(dev, uxga_regs, ARRAY_SIZE(uxga_regs));
	if (ret < 0) {
		return ret;
	}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not, let me plug this in

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might work to turn |= into a plain =, now that you added the if (ret < 0) check.

@josuah
Copy link
Contributor

josuah commented Sep 1, 2025

Sorry slightly long post to offer more context and offer choices to you as contributor.

Honestly, I just wanted to ease the pain for those still working with that type of sensors, it took me some time to figure this out

I agree the current sensors did not evolve as fast as the API did. They all need to get some spring clean-up to facilitate collaboration.

there is no possibility (seems to me) of having "any size" user wants

The datasheet possibly promised it but #define PER_SIZE_REG_SEQ(x, y, v_div, h_div, pclk_div) gets the dividers and clock hand crafted, for a preset of sizes. That's going to be a lot better than what is currently in the source, and does not burden

Currently Zephyr Video APIs are still somewhat documented in Linux documentation for the semantics, there's only basic function API usage in Zephyr. This is expected to change soon.

AFAIU, this macro does multiple things, which would ideally be dis-aggregated:

{CTRLI, CTRLI_LP_DP | CTRLI_V_DIV_SET(v_div) | CTRLI_H_DIV_SET(h_div)}, // [binning]
{ZMOW, ZMOW_OUTW_SET(x)}, // Vertical [cropping]
{ZMOH, ZMOH_OUTH_SET(y)}, // Horizontal [cropping]
{ZMHH, ZMHH_OUTW_SET(x) | ZMHH_OUTH_SET(y)}, // V/H [scaling]
{R_DVP_SP, pclk_div}, // [link-frequency]

That gives us 4 different APIs:

(+) the tiny VPU/ISP inside the image sensor becomes a tool the MCU can make use of
(+) simpler driver, pushing all the work to the video subsystem
(-) more work on this PR to get there (maintainers can give a hand)
(-) APIs are still not completely documented (maintainers can give a hand)

For instance transfer a low-res image (strong binning, good for low-noise), run some image detection on it, and transfer a JPEG image cropped to the detection result at max resolution.

If interested in trying, feel welcome, glad to help with that.
Otherwise, I think just a bit of tuning and removing the GPL'ed parts is going to be enough.

Either way, thank you for your efforts on improving OV2640! Small low-power low-cost image sensors can do a lot with good drivers. :) Any bit help, and your current proposal does not increase maintenance burden.

@josuah
Copy link
Contributor

josuah commented Sep 1, 2025

Ah, there we go, a WIP pull request showing some way to integrate cropping into an image sensor driver:

@cornway
Copy link
Author

cornway commented Sep 1, 2025

Sorry slightly long post to offer more context and offer choices to you as contributor.

Honestly, I just wanted to ease the pain for those still working with that type of sensors, it took me some time to figure this out

I agree the current sensors did not evolve as fast as the API did. They all need to get some spring clean-up to facilitate collaboration.

there is no possibility (seems to me) of having "any size" user wants

The datasheet possibly promised it but #define PER_SIZE_REG_SEQ(x, y, v_div, h_div, pclk_div) gets the dividers and clock hand crafted, for a preset of sizes. That's going to be a lot better than what is currently in the source, and does not burden

Currently Zephyr Video APIs are still somewhat documented in Linux documentation for the semantics, there's only basic function API usage in Zephyr. This is expected to change soon.

AFAIU, this macro does multiple things, which would ideally be dis-aggregated:

{CTRLI, CTRLI_LP_DP | CTRLI_V_DIV_SET(v_div) | CTRLI_H_DIV_SET(h_div)}, // [binning]
{ZMOW, ZMOW_OUTW_SET(x)}, // Vertical [cropping]
{ZMOH, ZMOH_OUTH_SET(y)}, // Horizontal [cropping]
{ZMHH, ZMHH_OUTW_SET(x) | ZMHH_OUTH_SET(y)}, // V/H [scaling]
{R_DVP_SP, pclk_div}, // [link-frequency]

That gives us 4 different APIs:

* [`video_set_format()`](https://docs.zephyrproject.org/latest/doxygen/html/group__video__interface.html#gab93c2cb09bf5b0629b665cc4a079e3cd) for configuring `[binning]`

* [`video_set_selection()`](https://docs.zephyrproject.org/latest/doxygen/html/group__video__interface.html#ga21f2e7d6b5ec0c50ceeee580c6272613) with [VIDEO_SEL_TGT_CROP](https://docs.zephyrproject.org/latest/doxygen/html/group__video__interface.html#ggae375c0586e3505632cc69348935c9b54aa42c3de3eeefb5340a2a1877ec8c4b17) for `[cropping]`

* [`video_set_selection()`](https://docs.zephyrproject.org/latest/doxygen/html/group__video__interface.html#ga21f2e7d6b5ec0c50ceeee580c6272613) with [VIDEO_SEL_TGT_COMPOSE](https://docs.zephyrproject.org/latest/doxygen/html/group__video__interface.html#ggae375c0586e3505632cc69348935c9b54a0558ad68bff086cd3ff3f82b53946f49) for `[scaling]`

* [`video_set_ctrl()`](https://docs.zephyrproject.org/latest/doxygen/html/group__video__interface.html#ga1cce17a3dfc881a1080708c7bc417aac) with [VIDEO_CID_LINK_FREQ](https://docs.zephyrproject.org/latest/doxygen/html/group__video__controls.html#ga2142e2819c445b70d82067a3cfb193c8) for the link frequency.

(+) the tiny VPU/ISP inside the image sensor becomes a tool the MCU can make use of (+) simpler driver, pushing all the work to the video subsystem (-) more work on this PR to get there (maintainers can give a hand) (-) APIs are still not completely documented (maintainers can give a hand)

For instance transfer a low-res image (strong binning, good for low-noise), run some image detection on it, and transfer a JPEG image cropped to the detection result at max resolution.

If interested in trying, feel welcome, glad to help with that. Otherwise, I think just a bit of tuning and removing the GPL'ed parts is going to be enough.

Either way, thank you for your efforts on improving OV2640! Small low-power low-cost image sensors can do a lot with good drivers. :) Any bit help, and your current proposal does not increase maintenance burden.

Thank you for your contribution in this PR
Yes, what you are saying makes sense, and I'm definitely interested
So to sum up :

  • I address comments regarding resolution constants
  • Remove unnecessary changes (like code formatting), or make separate commit out of it
  • Add this
ret = ov2640_write_all(dev, uxga_regs, ARRAY_SIZE(uxga_regs));
	if (ret < 0) {
		return ret;
	}
  • And most important - remove GPL'ed parts (e.g. reuse FIELD_PREP() macro)

I'll follow up on adding proper API later on
Please correct me if I missed anything
Btw, I have another pr (linked one) hanging around, please take a look
#94536

I have a lot more stuff to commit later on, but just what to handle them one by one
Thank you

@josuah
Copy link
Contributor

josuah commented Sep 2, 2025

  • I address comments regarding resolution constants
  • Remove unnecessary changes (like code formatting), or make separate commit out of it
  • Add this if (ret < 0) { return ret; }
  • And most important - remove GPL'ed parts (e.g. reuse FIELD_PREP() macro)

I confirm that's all.
The improvements mentioned are long-term ideals.
I will propose some examples below.

Thank you again!

Comment on lines 211 to 213
{CTRLI, CTRLI_LP_DP | CTRLI_V_DIV_SET(v_div) | CTRLI_H_DIV_SET(h_div)}, \
{ZMOW, ZMOW_OUTW_SET(x)}, {ZMOH, ZMOH_OUTH_SET(y)}, \
{ZMHH, ZMHH_OUTW_SET(x) | ZMHH_OUTH_SET(y)}, {R_DVP_SP, pclk_div}, {RESET, 0x00}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The XXXX(x) macros are only used once, maybe this can be all put inline here:

#define OV2640_SIZE_REGS(x, y, v_div, h_div, pclk_div)                                         \
	{CTRLI,                                                                                    \
	 CTRLI_LP_DP | FIELD_PREP(CTRLI_V_DIV_MASK, v_div) | FIELD_PREP(CTRLI_H_DIV_MASK, h_div)}, \
	{ZMOW, FIELD_PREP(ZMOW_OUTW_MASK, x)},                                                     \
	{ZMOH, FIELD_PREP(ZMOH_OUTH_MASK, y)},                                                     \
	{ZMHH, FIELD_PREP(ZMHH_OUTW_MASK, x) | FIELD_PREP(ZMHH_OUTH_MASK, y)},                     \
	{R_DVP_SP, pclk_div},                                                                      \
	{RESET, 0x00}

But that's just some minor thing to try to reduce driver-specific macros, nothing blocking.

@cornway cornway force-pushed the cornway/enhance-ov2640-sensor-driver branch from 445e4b6 to 74e560a Compare September 4, 2025 16:00
@cornway cornway requested review from josuah and dsseng September 4, 2025 16:01
@cornway cornway force-pushed the cornway/enhance-ov2640-sensor-driver branch from 74e560a to 3355ed0 Compare September 4, 2025 20:09
@cornway
Copy link
Author

cornway commented Sep 15, 2025

@josuah

Kindly ask you to review
Thank you

Copy link
Contributor

@josuah josuah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to do these two changes? This might be an occasion to get familiar with the Zephyr doc on contributing PRs to be all set for subsequent PRs:

  • In a PR, introducing something is expected to not be fixed by the next commits, so while it's appreciated that you kept the formatting changes separate, it will need to have the right formatting directly for newly contributed code.
  • You might need to put different 1st line for the commits, to avoid confusion of which commit does what. Something very short is ok.
  • We might need to keep 240x240 for now, or update the various samples using ESP32, as it is the default

Sorry for the late request for change, this PR did stay quite some time without review.

Thank you for your patience and for helping with image sensor driver maintenance!

LOG_ERR("Couldn't find window size for desired resolution setting");
return -EINVAL;
}
LOG_INF("Selected resolution %s", win->name);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By using %ux%u here instead of textual name, it becomes possible to save ~60 bytes. Worth considering?

Comment on lines 475 to 574
OV2640_VIDEO_FORMAT_CAP(160, 120, VIDEO_PIX_FMT_RGB565), /* QQVGA */
OV2640_VIDEO_FORMAT_CAP(176, 144, VIDEO_PIX_FMT_RGB565), /* QCIF */
OV2640_VIDEO_FORMAT_CAP(240, 160, VIDEO_PIX_FMT_RGB565), /* HQVGA */
OV2640_VIDEO_FORMAT_CAP(240, 240, VIDEO_PIX_FMT_RGB565), /* 240x240 */
OV2640_VIDEO_FORMAT_CAP(320, 240, VIDEO_PIX_FMT_RGB565), /* QVGA */
OV2640_VIDEO_FORMAT_CAP(352, 288, VIDEO_PIX_FMT_RGB565), /* CIF */
OV2640_VIDEO_FORMAT_CAP(640, 480, VIDEO_PIX_FMT_RGB565), /* VGA */
OV2640_VIDEO_FORMAT_CAP(800, 600, VIDEO_PIX_FMT_RGB565), /* SVGA */
OV2640_VIDEO_FORMAT_CAP(1024, 768, VIDEO_PIX_FMT_RGB565), /* XVGA */
OV2640_VIDEO_FORMAT_CAP(1280, 1024, VIDEO_PIX_FMT_RGB565), /* SXGA */
OV2640_VIDEO_FORMAT_CAP(1600, 1200, VIDEO_PIX_FMT_RGB565), /* UXGA */
OV2640_VIDEO_FORMAT_CAP(160, 120, VIDEO_PIX_FMT_JPEG), /* QQVGA */
OV2640_VIDEO_FORMAT_CAP(176, 144, VIDEO_PIX_FMT_JPEG), /* QCIF */
OV2640_VIDEO_FORMAT_CAP(240, 160, VIDEO_PIX_FMT_JPEG), /* HQVGA */
OV2640_VIDEO_FORMAT_CAP(320, 240, VIDEO_PIX_FMT_JPEG), /* QVGA */
OV2640_VIDEO_FORMAT_CAP(352, 288, VIDEO_PIX_FMT_JPEG), /* CIF */
OV2640_VIDEO_FORMAT_CAP(640, 480, VIDEO_PIX_FMT_JPEG), /* VGA */
OV2640_VIDEO_FORMAT_CAP(800, 600, VIDEO_PIX_FMT_JPEG), /* SVGA */
OV2640_VIDEO_FORMAT_CAP(1024, 768, VIDEO_PIX_FMT_JPEG), /* XVGA */
OV2640_VIDEO_FORMAT_CAP(1280, 1024, VIDEO_PIX_FMT_JPEG), /* SXGA */
OV2640_VIDEO_FORMAT_CAP(1600, 1200, VIDEO_PIX_FMT_JPEG), /* UXGA */
OV2640_VIDEO_FORMAT_CAP(QQVGA_WIDTH, QQVGA_HEIGHT, VIDEO_PIX_FMT_RGB565), /* 160 x 120 QQVGA */
OV2640_VIDEO_FORMAT_CAP(QCIF_WIDTH, QCIF_HEIGHT, VIDEO_PIX_FMT_RGB565), /* 176 x 144 QCIF */
OV2640_VIDEO_FORMAT_CAP(CIF_WIDTH, CIF_HEIGHT, VIDEO_PIX_FMT_RGB565), /* 352 x 288 CIF */
OV2640_VIDEO_FORMAT_CAP(VGA_WIDTH, VGA_HEIGHT, VIDEO_PIX_FMT_RGB565), /* 640 x 480 VGA */
OV2640_VIDEO_FORMAT_CAP(SVGA_WIDTH, SVGA_HEIGHT, VIDEO_PIX_FMT_RGB565), /* 800 x 600 SVGA */
OV2640_VIDEO_FORMAT_CAP(XGA_WIDTH, XGA_HEIGHT, VIDEO_PIX_FMT_RGB565), /* 1024 x 768 XVGA */
OV2640_VIDEO_FORMAT_CAP(SXGA_WIDTH, SXGA_HEIGHT, VIDEO_PIX_FMT_RGB565), /* 1280 x 1024 SXGA */
OV2640_VIDEO_FORMAT_CAP(UXGA_WIDTH, UXGA_HEIGHT, VIDEO_PIX_FMT_RGB565), /* 1600 x 1200 UXGA */
OV2640_VIDEO_FORMAT_CAP(QQVGA_WIDTH, QQVGA_HEIGHT, VIDEO_PIX_FMT_JPEG), /* 160 x 120 QQVGA */
OV2640_VIDEO_FORMAT_CAP(QCIF_WIDTH, QCIF_HEIGHT, VIDEO_PIX_FMT_JPEG), /* 176 x 144 QCIF */
OV2640_VIDEO_FORMAT_CAP(CIF_WIDTH, CIF_HEIGHT, VIDEO_PIX_FMT_JPEG), /* 352 x 288 CIF */
OV2640_VIDEO_FORMAT_CAP(VGA_WIDTH, VGA_HEIGHT, VIDEO_PIX_FMT_JPEG), /* 640 x 480 VGA */
OV2640_VIDEO_FORMAT_CAP(SVGA_WIDTH, SVGA_HEIGHT, VIDEO_PIX_FMT_JPEG), /* 800 x 600 SVGA */
OV2640_VIDEO_FORMAT_CAP(XGA_WIDTH, XGA_HEIGHT, VIDEO_PIX_FMT_JPEG), /* 1024 x 768 XVGA */
OV2640_VIDEO_FORMAT_CAP(SXGA_WIDTH, SXGA_HEIGHT, VIDEO_PIX_FMT_JPEG), /* 1280 x 1024 SXGA */
OV2640_VIDEO_FORMAT_CAP(UXGA_WIDTH, UXGA_HEIGHT, VIDEO_PIX_FMT_JPEG), /* 1600 x 1200 UXGA */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this removes the 240x240 resolution used by Espressif to showcase their Zephyr camera support via the ESP32-S3 Eye: https://github.com/zephyrproject-rtos/zephyr/blob/main/samples/drivers/video/capture/boards/esp32s3_eye_procpu.conf#L9-L10

CONFIG_VIDEO_FRAME_HEIGHT=240
CONFIG_VIDEO_FRAME_WIDTH=240

But maybe that's not a problem if it's for something better.
Is the plan to move all the cropping/windowing support the video_set_selection() API in a latter PR?

If so, it is tempting to only remove the formats once they can be supported again through a different API.

LOG_INF("Selected resolution %s", win->name);

/* Write DSP input registers */
ret |= ov2640_write_all(dev, uxga_regs, ARRAY_SIZE(uxga_regs));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might work to turn |= into a plain =, now that you added the if (ret < 0) check.

@cornway cornway force-pushed the cornway/enhance-ov2640-sensor-driver branch from 247c1ab to f836502 Compare September 16, 2025 15:36
@cornway
Copy link
Author

cornway commented Sep 16, 2025

I did my best, and hopefully I satisfied all the requested changes this time 😄
Kindly ask to review once again

@cornway cornway requested a review from josuah September 16, 2025 19:12
static const struct ov2640_win_size ov2640_supported_win_sizes[] = {
OV2640_SIZE(QQVGA_WIDTH, QQVGA_HEIGHT, ov2640_qqvga_regs),
OV2640_SIZE(QCIF_WIDTH, QCIF_HEIGHT, ov2640_qcif_regs),
OV2640_SIZE(240, 240, ov2640_qvga_regs),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to still use the QVGA registers even though the resolution is announced as 240x240.

As a result, the capture sample works with the ESP32 S3 Eye, but instead of looking like this (before):

PXL_20250917_093827260

It looks like this (after):

PXL_20250917_093936553

I first rebased your PR on top of #95958 then ran it with this:

west build -b esp32s3_eye/esp32s3/procpu samples/drivers/video/capture

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what the problem is
Let me fix it first and I let you know once this is done
Thank you for going through verification

Copy link
Contributor

@josuah josuah Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to add a ov2640_240x240_regs that crops the width in the meantime that video_set_selection() is introduced? It helps with getting a working demo of the ESP32 driver.

[EDIT: thanks! I will test again as soon as I get a chance]

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed, please take a look
You may need to adjust these prescalers

static const struct ov2640_reg ov2640_240x240_regs[] = {
	OV2640_ZOOM_CONFIG(240, 240, 2, 2, 4),
};

2, 2, 4 if that doesn't work, unfortunately I don't have that board so can't check

Thank you!

@cornway cornway force-pushed the cornway/enhance-ov2640-sensor-driver branch from f836502 to 6b9c86a Compare September 17, 2025 09:59
@josuah
Copy link
Contributor

josuah commented Sep 17, 2025

About the commit message, here is what I understand:

  • The current driver only supports UXGA as base resolution, and was making a combination of cropping and scaling as a way to support an arbitrary list of smaller resolutions (convenient to adapt the image output to the size of a display).
  • The new driver does the same thing, but modifies more parameters (such as PCLK divider), which and only uses scaling and not cropping.

Enabling proper zoom/scaling for windowed resolutions; e.g. smaller than uxga.

Here is how I understand that zooming, scaling, and windowing (cropping?) relate (where FoV is Field of View):

video-transform-terminilogy

Did I understand correctly? Your answer might as well be a good commit message btw.

Thank you!

@cornway
Copy link
Author

cornway commented Sep 17, 2025

#> About the commit message, here is what I understand:

* The current driver only supports UXGA as base resolution, and was making a combination of cropping and scaling as a way to support an arbitrary list of smaller resolutions (convenient to adapt the image output to the size of a display).

* The new driver does the same thing, but modifies more parameters (such as `PCLK` divider), which and only uses scaling and not cropping.

Enabling proper zoom/scaling for windowed resolutions; e.g. smaller than uxga.

Here is how I understand that zooming, scaling, and windowing (cropping?) relate (where FoV is Field of View):
video-transform-terminilogy

Did I understand correctly? Your answer might as well be a good commit message btw.

Thank you!

To the best of my knowledge here - cropping is not implemented properly in that driver, I mean that sensor doesn't have clear description on cropped region registers and I couldn't find anything that shows how to utilize it; That leads to - without proper prescaling/dividing it just crops top left corner not the center as in your drawings
So in this implementation (this PR) - it just does scaling (down scaling exactly), not cropping
Without this PR - it doesn't do any scaling , but just cropping of top left corner of the frame

@cornway cornway requested a review from josuah September 17, 2025 10:31
@cornway
Copy link
Author

cornway commented Sep 23, 2025

@josuah
Kindly reminder to review this patch and verify
Thank you 😃

@cornway cornway force-pushed the cornway/enhance-ov2640-sensor-driver branch from 6b9c86a to 9f8d1ff Compare October 9, 2025 12:01
@cornway
Copy link
Author

cornway commented Oct 9, 2025

Good day, can anyone take a look at this PR once again please ?
@josuah @dsseng
thank you so much

@dsseng
Copy link
Member

dsseng commented Oct 9, 2025

I do not have any comments, I'm not on the video maintainer team and just dropped some general C programming tips. If no reviews follow in a week, feel free to use #pr-help on Discord to grab someone's attention

@cornway
Copy link
Author

cornway commented Oct 9, 2025

I do not have any comments, I'm not on the video maintainer team and just dropped some general C programming tips. If no reviews follow in a week, feel free to use #pr-help on Discord to grab someone's attention

Thank you
Will do !
Have a nice day

Copy link
Contributor

@josuah josuah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rebased this on top of main and tested again:

Before: the test circle is not centered even though picture taken in front of the sensor (as you described)

Image

After: the test circle is now an ovale, wrong proportions (limitation where it's not possible to set the offset of the readout insofar

Image

This is a driver/doc/hardware limitation for another.
Though, this is IMHO a big improvement over cropping, in particular for every other format.

More background in a follow-up discussion.

Thanks for the improvements (and for waiting), LGTM!

@josuah
Copy link
Contributor

josuah commented Oct 9, 2025

You will need to rebase on latest main to fix the new jsonschema dependency. This happened on a few PRs.

@cornway
Copy link
Author

cornway commented Oct 9, 2025

I rebased this on top of main and tested again:

Before: the test circle is not centered even though picture taken in front of the sensor (as you described)

Image

After: the test circle is now an ovale, wrong proportions (limitation where it's not possible to set the offset of the readout insofar

Image

This is a driver/doc/hardware limitation for another. Though, this is IMHO a big improvement over cropping, in particular for every other format.

More background in a follow-up discussion.

Thanks for the improvements (and for waiting), LGTM!

Thanks, will do rebase
Thank you for verifying that
I have a pile of another improvements, just need this to go first 😄

Enabling proper zoom/scaling for windowed resolutions;
e.g. smaller than uxga
That makes camera's ISP to downscale frame to a desired resolution


Signed-off-by: Roman Pustobaiev <[email protected]>
Apply code formatting to some parts of the ov2640 driver

Signed-off-by: Roman Pustobaiev <[email protected]>
@cornway cornway force-pushed the cornway/enhance-ov2640-sensor-driver branch from 9f8d1ff to d50b1bf Compare October 9, 2025 14:10
Copy link

sonarqubecloud bot commented Oct 9, 2025

Copy link

@avolmat-st avolmat-st left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update. My understanding is that this will pick a resolution in the list in order to apply it to the sensor and that resolution might not be the exact resolution asked by the application

(checking for width >= and height >= instead of width == / height ==).
In such case, upon a set_fmt, the resolution actually applied might be different from the one requested.
This is ok, however the information should be given back to the application in such case by setting back the new resolution into the set_fmt fmt pointer.

#define OV2640_ZOOM_CONFIG(x, y, v_div, h_div, pclk_div) \
{CTRLI, \
CTRLI_LP_DP | FIELD_PREP(GENMASK(5, 3), v_div) | FIELD_PREP(GENMASK(2, 0), h_div)}, \
{ZMOW, FIELD_PREP(GENMASK(7, 0), (x) >> 2)}, \

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: indentation to fix for this line and below, since currently it makes it hard to easily understand that there is actually 5 registers being set

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was mostly the clang format caused this, I'll try to change it
Thank you

{ZMOH, FIELD_PREP(GENMASK(7, 0), (y) >> 2)}, \
{ZMHH, FIELD_PREP(GENMASK(1, 0), (x) >> (8 + 2)) | \
FIELD_PREP(GENMASK(2, 2), (y) >> (8 + 2))}, \
{R_DVP_SP, pclk_div}, {RESET, 0x00}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: maybe keep only 1 register setting per line to make it easier to read

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That also came from clang-format

}

static int ov2640_set_resolution(const struct device *dev,
uint16_t img_width, uint16_t img_height)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(not modified in this patch but since I noticed it I mention it here)

In several places within the driver, the width / height are uint16_t while in video API (and in some function within this driver), this is uint32_t, so I think this should be fixed.
The first issue is within the set_fmt into which a uint32_t is put into a uint16_t.

@cornway
Copy link
Author

cornway commented Oct 9, 2025

Thanks for the update. My understanding is that this will pick a resolution in the list in order to apply it to the sensor and that resolution might not be the exact resolution asked by the application

(checking for width >= and height >= instead of width == / height ==). In such case, upon a set_fmt, the resolution actually applied might be different from the one requested. This is ok, however the information should be given back to the application in such case by setting back the new resolution into the set_fmt fmt pointer.

Thank you for pointing that out
Is it ok to push another commit that fixes that, or better to squash it in ?

@avolmat-st
Copy link

avolmat-st commented Oct 9, 2025

Thanks for the update. My understanding is that this will pick a resolution in the list in order to apply it to the sensor and that resolution might not be the exact resolution asked by the application
(checking for width >= and height >= instead of width == / height ==). In such case, upon a set_fmt, the resolution actually applied might be different from the one requested. This is ok, however the information should be given back to the application in such case by setting back the new resolution into the set_fmt fmt pointer.

Thank you for pointing that out Is it ok to push another commit that fixes that, or better to squash it in ?

I think it should be together with the modification which lead to having the set format not same as the one requested. This lead to having the behavior modified. So I'd go with doing it in the existing commit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: Video Video subsystem
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants