Skip to content

Commit 53964c7

Browse files
committed
drivers: video: ov2640 driver: Enhancing ov2640 driver
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]>
1 parent aa6f87d commit 53964c7

File tree

1 file changed

+141
-45
lines changed

1 file changed

+141
-45
lines changed

drivers/video/ov2640.c

Lines changed: 141 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -151,18 +151,101 @@ LOG_MODULE_REGISTER(video_ov2640, CONFIG_VIDEO_LOG_LEVEL);
151151
#define REG32 0x32
152152
#define REG32_UXGA 0x36
153153

154-
/* Configuration arrays */
155-
#define SVGA_HSIZE (800)
156-
#define SVGA_VSIZE (600)
157-
158-
#define UXGA_HSIZE (1600)
159-
#define UXGA_VSIZE (1200)
154+
#define CIF_WIDTH 352
155+
#define CIF_HEIGHT 288
156+
#define HD_720_WIDTH 1280
157+
#define HD_720_HEIGHT 720
158+
#define HD_1080_WIDTH 1920
159+
#define HD_1080_HEIGHT 1080
160+
#define QCIF_WIDTH 176
161+
#define QCIF_HEIGHT 144
162+
#define QQCIF_WIDTH 88
163+
#define QQCIF_HEIGHT 72
164+
#define QQVGA_WIDTH 160
165+
#define QQVGA_HEIGHT 120
166+
#define QVGA_WIDTH 320
167+
#define QVGA_HEIGHT 240
168+
#define SVGA_WIDTH 800
169+
#define SVGA_HEIGHT 600
170+
#define SXGA_WIDTH 1280
171+
#define SXGA_HEIGHT 1024
172+
#define VGA_WIDTH 640
173+
#define VGA_HEIGHT 480
174+
#define UXGA_WIDTH 1600
175+
#define UXGA_HEIGHT 1200
176+
#define XGA_WIDTH 1024
177+
#define XGA_HEIGHT 768
160178

161179
struct ov2640_reg {
162180
uint8_t addr;
163181
uint8_t value;
164182
};
165183

184+
struct ov2640_win_size {
185+
char *name;
186+
uint32_t width;
187+
uint32_t height;
188+
const struct ov2640_reg *regs;
189+
uint32_t regs_size;
190+
};
191+
192+
#define PER_SIZE_REG_SEQ(x, y, v_div, h_div, pclk_div) \
193+
{ CTRLI, CTRLI_LP_DP | FIELD_PREP(GENMASK(5, 3), v_div) | \
194+
FIELD_PREP(GENMASK(2, 0), h_div)}, \
195+
{ ZMOW, FIELD_PREP(GENMASK(7, 0), (x) >> 2) }, \
196+
{ ZMOH, FIELD_PREP(GENMASK(7, 0), (y) >> 2) }, \
197+
{ ZMHH, FIELD_PREP(GENMASK(1, 0), (x) >> (8+2)) | FIELD_PREP(GENMASK(2, 2), (y) >> (8+2)) }, \
198+
{ R_DVP_SP, pclk_div }, \
199+
{ RESET, 0x00}
200+
201+
static const struct ov2640_reg ov2640_qqvga_regs[] = {
202+
PER_SIZE_REG_SEQ(QQVGA_WIDTH, QQVGA_HEIGHT, 3, 3, 8),
203+
};
204+
static const struct ov2640_reg ov2640_qcif_regs[] = {
205+
PER_SIZE_REG_SEQ(QCIF_WIDTH, QCIF_HEIGHT, 3, 3, 4),
206+
};
207+
static const struct ov2640_reg ov2640_qvga_regs[] = {
208+
PER_SIZE_REG_SEQ(QVGA_WIDTH, QVGA_HEIGHT, 2, 2, 4),
209+
};
210+
static const struct ov2640_reg ov2640_cif_regs[] = {
211+
PER_SIZE_REG_SEQ(CIF_WIDTH, CIF_HEIGHT, 2, 2, 8),
212+
};
213+
static const struct ov2640_reg ov2640_vga_regs[] = {
214+
PER_SIZE_REG_SEQ(VGA_WIDTH, VGA_HEIGHT, 0, 0, 2),
215+
};
216+
static const struct ov2640_reg ov2640_svga_regs[] = {
217+
PER_SIZE_REG_SEQ(SVGA_WIDTH, SVGA_HEIGHT, 1, 1, 2),
218+
};
219+
static const struct ov2640_reg ov2640_xga_regs[] = {
220+
PER_SIZE_REG_SEQ(XGA_WIDTH, XGA_HEIGHT, 0, 0, 2),
221+
{ CTRLI, 0x00},
222+
};
223+
static const struct ov2640_reg ov2640_sxga_regs[] = {
224+
PER_SIZE_REG_SEQ(SXGA_WIDTH, SXGA_HEIGHT, 0, 0, 2),
225+
{ CTRLI, 0x00},
226+
{ R_DVP_SP, 2 | R_DVP_SP_AUTO_MODE },
227+
};
228+
static const struct ov2640_reg ov2640_uxga_regs[] = {
229+
PER_SIZE_REG_SEQ(UXGA_WIDTH, UXGA_HEIGHT, 0, 0, 0),
230+
{ CTRLI, 0x00},
231+
{ R_DVP_SP, 0 | R_DVP_SP_AUTO_MODE },
232+
};
233+
234+
#define OV2640_SIZE(n, w, h, r) \
235+
{.name = n, .width = w , .height = h, .regs = r, .regs_size = ARRAY_SIZE(r) }
236+
237+
static const struct ov2640_win_size ov2640_supported_win_sizes[] = {
238+
OV2640_SIZE("QQVGA", QQVGA_WIDTH, QQVGA_HEIGHT, ov2640_qqvga_regs),
239+
OV2640_SIZE("QCIF", QCIF_WIDTH, QCIF_HEIGHT, ov2640_qcif_regs),
240+
OV2640_SIZE("QVGA", QVGA_WIDTH, QVGA_HEIGHT, ov2640_qvga_regs),
241+
OV2640_SIZE("CIF", CIF_WIDTH, CIF_HEIGHT, ov2640_cif_regs),
242+
OV2640_SIZE("VGA", VGA_WIDTH, VGA_HEIGHT, ov2640_vga_regs),
243+
OV2640_SIZE("SVGA", SVGA_WIDTH, SVGA_HEIGHT, ov2640_svga_regs),
244+
OV2640_SIZE("XGA", XGA_WIDTH, XGA_HEIGHT, ov2640_xga_regs),
245+
OV2640_SIZE("SXGA", SXGA_WIDTH, SXGA_HEIGHT, ov2640_sxga_regs),
246+
OV2640_SIZE("UXGA", UXGA_WIDTH, UXGA_HEIGHT, ov2640_uxga_regs),
247+
};
248+
166249
static const struct ov2640_reg default_regs[] = {
167250
{ BANK_SEL, BANK_SEL_DSP },
168251
{ 0x2c, 0xff },
@@ -376,20 +459,20 @@ static const struct ov2640_reg uxga_regs[] = {
376459
{ R_BYPASS, R_BYPASS_DSP_BYPAS },
377460

378461
{ RESET, RESET_DVP },
379-
{ HSIZE8, (UXGA_HSIZE>>3)}, /* Image Horizontal Size HSIZE[10:3] */
380-
{ VSIZE8, (UXGA_VSIZE>>3)}, /* Image Vertical Size VSIZE[10:3] */
462+
{ HSIZE8, (UXGA_WIDTH>>3)}, /* Image Horizontal Size HSIZE[10:3] */
463+
{ VSIZE8, (UXGA_HEIGHT>>3)}, /* Image Vertical Size VSIZE[10:3] */
381464

382465
/* {HSIZE[11], HSIZE[2:0], VSIZE[2:0]} */
383-
{ SIZEL, ((UXGA_HSIZE>>6)&0x40) | ((UXGA_HSIZE&0x7)<<3) | (UXGA_VSIZE&0x7)},
466+
{ SIZEL, ((UXGA_WIDTH>>6)&0x40) | ((UXGA_WIDTH&0x7)<<3) | (UXGA_HEIGHT&0x7)},
384467

385468
{ XOFFL, 0x00 }, /* OFFSET_X[7:0] */
386469
{ YOFFL, 0x00 }, /* OFFSET_Y[7:0] */
387-
{ HSIZE, ((UXGA_HSIZE>>2)&0xFF) }, /* H_SIZE[7:0] real/4 */
388-
{ VSIZE, ((UXGA_VSIZE>>2)&0xFF) }, /* V_SIZE[7:0] real/4 */
470+
{ HSIZE, ((UXGA_WIDTH>>2)&0xFF) }, /* H_SIZE[7:0] real/4 */
471+
{ VSIZE, ((UXGA_HEIGHT>>2)&0xFF) }, /* V_SIZE[7:0] real/4 */
389472

390473
/* V_SIZE[8]/OFFSET_Y[10:8]/H_SIZE[8]/OFFSET_X[10:8] */
391-
{ VHYX, ((UXGA_VSIZE>>3)&0x80) | ((UXGA_HSIZE>>7)&0x08) },
392-
{ TEST, (UXGA_HSIZE>>4)&0x80}, /* H_SIZE[9] */
474+
{ VHYX, ((UXGA_HEIGHT>>3)&0x80) | ((UXGA_WIDTH>>7)&0x08) },
475+
{ TEST, (UXGA_WIDTH>>4)&0x80}, /* H_SIZE[9] */
393476

394477
{ CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN |
395478
CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN },
@@ -400,7 +483,8 @@ static const struct ov2640_reg uxga_regs[] = {
400483
{ R_DVP_SP, R_DVP_SP_AUTO_MODE | 0x04},
401484

402485
{ R_BYPASS, R_BYPASS_DSP_EN },
403-
{ RESET, 0x00 },
486+
/* Keep reset asserted as zoom config is coming next */
487+
/* { RESET, 0x00 }, */
404488
{0, 0},
405489
};
406490

@@ -472,27 +556,22 @@ struct ov2640_data {
472556
}
473557

474558
static const struct video_format_cap fmts[] = {
475-
OV2640_VIDEO_FORMAT_CAP(160, 120, VIDEO_PIX_FMT_RGB565), /* QQVGA */
476-
OV2640_VIDEO_FORMAT_CAP(176, 144, VIDEO_PIX_FMT_RGB565), /* QCIF */
477-
OV2640_VIDEO_FORMAT_CAP(240, 160, VIDEO_PIX_FMT_RGB565), /* HQVGA */
478-
OV2640_VIDEO_FORMAT_CAP(240, 240, VIDEO_PIX_FMT_RGB565), /* 240x240 */
479-
OV2640_VIDEO_FORMAT_CAP(320, 240, VIDEO_PIX_FMT_RGB565), /* QVGA */
480-
OV2640_VIDEO_FORMAT_CAP(352, 288, VIDEO_PIX_FMT_RGB565), /* CIF */
481-
OV2640_VIDEO_FORMAT_CAP(640, 480, VIDEO_PIX_FMT_RGB565), /* VGA */
482-
OV2640_VIDEO_FORMAT_CAP(800, 600, VIDEO_PIX_FMT_RGB565), /* SVGA */
483-
OV2640_VIDEO_FORMAT_CAP(1024, 768, VIDEO_PIX_FMT_RGB565), /* XVGA */
484-
OV2640_VIDEO_FORMAT_CAP(1280, 1024, VIDEO_PIX_FMT_RGB565), /* SXGA */
485-
OV2640_VIDEO_FORMAT_CAP(1600, 1200, VIDEO_PIX_FMT_RGB565), /* UXGA */
486-
OV2640_VIDEO_FORMAT_CAP(160, 120, VIDEO_PIX_FMT_JPEG), /* QQVGA */
487-
OV2640_VIDEO_FORMAT_CAP(176, 144, VIDEO_PIX_FMT_JPEG), /* QCIF */
488-
OV2640_VIDEO_FORMAT_CAP(240, 160, VIDEO_PIX_FMT_JPEG), /* HQVGA */
489-
OV2640_VIDEO_FORMAT_CAP(320, 240, VIDEO_PIX_FMT_JPEG), /* QVGA */
490-
OV2640_VIDEO_FORMAT_CAP(352, 288, VIDEO_PIX_FMT_JPEG), /* CIF */
491-
OV2640_VIDEO_FORMAT_CAP(640, 480, VIDEO_PIX_FMT_JPEG), /* VGA */
492-
OV2640_VIDEO_FORMAT_CAP(800, 600, VIDEO_PIX_FMT_JPEG), /* SVGA */
493-
OV2640_VIDEO_FORMAT_CAP(1024, 768, VIDEO_PIX_FMT_JPEG), /* XVGA */
494-
OV2640_VIDEO_FORMAT_CAP(1280, 1024, VIDEO_PIX_FMT_JPEG), /* SXGA */
495-
OV2640_VIDEO_FORMAT_CAP(1600, 1200, VIDEO_PIX_FMT_JPEG), /* UXGA */
559+
OV2640_VIDEO_FORMAT_CAP(QQVGA_WIDTH, QQVGA_HEIGHT, VIDEO_PIX_FMT_RGB565), /* 160 x 120 QQVGA */
560+
OV2640_VIDEO_FORMAT_CAP(QCIF_WIDTH, QCIF_HEIGHT, VIDEO_PIX_FMT_RGB565), /* 176 x 144 QCIF */
561+
OV2640_VIDEO_FORMAT_CAP(CIF_WIDTH, CIF_HEIGHT, VIDEO_PIX_FMT_RGB565), /* 352 x 288 CIF */
562+
OV2640_VIDEO_FORMAT_CAP(VGA_WIDTH, VGA_HEIGHT, VIDEO_PIX_FMT_RGB565), /* 640 x 480 VGA */
563+
OV2640_VIDEO_FORMAT_CAP(SVGA_WIDTH, SVGA_HEIGHT, VIDEO_PIX_FMT_RGB565), /* 800 x 600 SVGA */
564+
OV2640_VIDEO_FORMAT_CAP(XGA_WIDTH, XGA_HEIGHT, VIDEO_PIX_FMT_RGB565), /* 1024 x 768 XVGA */
565+
OV2640_VIDEO_FORMAT_CAP(SXGA_WIDTH, SXGA_HEIGHT, VIDEO_PIX_FMT_RGB565), /* 1280 x 1024 SXGA */
566+
OV2640_VIDEO_FORMAT_CAP(UXGA_WIDTH, UXGA_HEIGHT, VIDEO_PIX_FMT_RGB565), /* 1600 x 1200 UXGA */
567+
OV2640_VIDEO_FORMAT_CAP(QQVGA_WIDTH, QQVGA_HEIGHT, VIDEO_PIX_FMT_JPEG), /* 160 x 120 QQVGA */
568+
OV2640_VIDEO_FORMAT_CAP(QCIF_WIDTH, QCIF_HEIGHT, VIDEO_PIX_FMT_JPEG), /* 176 x 144 QCIF */
569+
OV2640_VIDEO_FORMAT_CAP(CIF_WIDTH, CIF_HEIGHT, VIDEO_PIX_FMT_JPEG), /* 352 x 288 CIF */
570+
OV2640_VIDEO_FORMAT_CAP(VGA_WIDTH, VGA_HEIGHT, VIDEO_PIX_FMT_JPEG), /* 640 x 480 VGA */
571+
OV2640_VIDEO_FORMAT_CAP(SVGA_WIDTH, SVGA_HEIGHT, VIDEO_PIX_FMT_JPEG), /* 800 x 600 SVGA */
572+
OV2640_VIDEO_FORMAT_CAP(XGA_WIDTH, XGA_HEIGHT, VIDEO_PIX_FMT_JPEG), /* 1024 x 768 XVGA */
573+
OV2640_VIDEO_FORMAT_CAP(SXGA_WIDTH, SXGA_HEIGHT, VIDEO_PIX_FMT_JPEG), /* 1280 x 1024 SXGA */
574+
OV2640_VIDEO_FORMAT_CAP(UXGA_WIDTH, UXGA_HEIGHT, VIDEO_PIX_FMT_JPEG), /* 1600 x 1200 UXGA */
496575
{ 0 }
497576
};
498577

@@ -775,6 +854,17 @@ static int ov2640_set_vertical_flip(const struct device *dev, int enable)
775854
return ret;
776855
}
777856

857+
static const struct ov2640_win_size *ov2640_select_win(uint32_t width, uint32_t height)
858+
{
859+
int i;
860+
for (i = 0; i < ARRAY_SIZE(ov2640_supported_win_sizes); i++) {
861+
if (ov2640_supported_win_sizes[i].width >= width &&
862+
ov2640_supported_win_sizes[i].height >= height)
863+
return &ov2640_supported_win_sizes[i];
864+
}
865+
return NULL;
866+
}
867+
778868
static int ov2640_set_resolution(const struct device *dev,
779869
uint16_t img_width, uint16_t img_height)
780870
{
@@ -784,23 +874,29 @@ static int ov2640_set_resolution(const struct device *dev,
784874
uint16_t w = img_width;
785875
uint16_t h = img_height;
786876

877+
const struct ov2640_win_size *win = ov2640_select_win(w, h);
878+
if (NULL == win) {
879+
LOG_ERR("Couldn't find window size for desired resolution setting");
880+
return -EINVAL;
881+
}
882+
LOG_INF("Selected resolution %s", win->name);
883+
884+
/* Write DSP input registers */
885+
ret |= ov2640_write_all(dev, uxga_regs, ARRAY_SIZE(uxga_regs));
886+
if (ret < 0) {
887+
return ret;
888+
}
889+
787890
/* Disable DSP */
788891
ret |= ov2640_write_reg(&cfg->i2c, BANK_SEL, BANK_SEL_DSP);
789892
ret |= ov2640_write_reg(&cfg->i2c, R_BYPASS, R_BYPASS_DSP_BYPAS);
790893

791-
/* Write output width */
792-
ret |= ov2640_write_reg(&cfg->i2c, ZMOW, (w >> 2) & 0xFF); /* OUTW[7:0] (real/4) */
793-
ret |= ov2640_write_reg(&cfg->i2c, ZMOH, (h >> 2) & 0xFF); /* OUTH[7:0] (real/4) */
794-
ret |= ov2640_write_reg(&cfg->i2c, ZMHH, ((h >> 8) & 0x04) |
795-
((w>>10) & 0x03)); /* OUTH[8]/OUTW[9:8] */
894+
ret |= ov2640_write_all(dev, win->regs, win->regs_size);
796895

797896
/* Set CLKRC */
798897
ret |= ov2640_write_reg(&cfg->i2c, BANK_SEL, BANK_SEL_SENSOR);
799898
ret |= ov2640_write_reg(&cfg->i2c, CLKRC, cfg->clock_rate_control);
800899

801-
/* Write DSP input registers */
802-
ov2640_write_all(dev, uxga_regs, ARRAY_SIZE(uxga_regs));
803-
804900
/* Enable DSP */
805901
ret |= ov2640_write_reg(&cfg->i2c, BANK_SEL, BANK_SEL_DSP);
806902
ret |= ov2640_write_reg(&cfg->i2c, R_BYPASS, R_BYPASS_DSP_EN);
@@ -1004,8 +1100,8 @@ static int ov2640_init(const struct device *dev)
10041100
/* set default/init format SVGA RGB565 */
10051101
struct video_format fmt = {
10061102
.pixelformat = VIDEO_PIX_FMT_RGB565,
1007-
.width = SVGA_HSIZE,
1008-
.height = SVGA_VSIZE,
1103+
.width = SVGA_WIDTH,
1104+
.height = SVGA_HEIGHT,
10091105
};
10101106

10111107
#if DT_INST_NODE_HAS_PROP(0, reset_gpios)

0 commit comments

Comments
 (0)