Browse Source

drivers: video: emul: store only one line of data

Only store a single line of the full frame. This allows to support a
large enough frame size to remove the Kconfig option, which simplifies
the implementation (fewer checks needed).

Signed-off-by: Josuah Demangeon <me@josuah.net>
pull/83061/head
Josuah Demangeon 7 months ago committed by Benjamin Cabé
parent
commit
a86da87a52
  1. 16
      drivers/video/Kconfig.emul_imager
  2. 69
      drivers/video/video_emul_imager.c
  3. 9
      drivers/video/video_emul_rx.c
  4. 8
      tests/drivers/video/api/prj.conf
  5. 3
      tests/drivers/video/api/src/video_emul.c
  6. 3
      tests/drivers/video/api/testcase.yaml

16
drivers/video/Kconfig.emul_imager

@ -6,13 +6,9 @@ config VIDEO_EMUL_IMAGER
depends on DT_HAS_ZEPHYR_VIDEO_EMUL_IMAGER_ENABLED depends on DT_HAS_ZEPHYR_VIDEO_EMUL_IMAGER_ENABLED
default y default y
help help
Enable driver for the emulated Imager. Enable driver for the emulated Imager. A line buffer contains
the color pattern within the imager data struct, at the first
config VIDEO_EMUL_IMAGER_FRAMEBUFFER_SIZE field, and the "zephyr,video-emul-rx" driver will memcpy() this
int "Internal framebuffer size used for link emulation purpose" line over a full frame, simulating a MIPI link transmetting
default 4096 the lines of data from the imager to the memory. The default
help value fits 2 bit-per-pixel, 640 pixel-wide frames.
Configure the size of the internal framebuffer the emulated Imager
driver uses to simulate MIPI transfers. This is the first field of
dev->data, and the emulated video MIPI driver will `memcpy()` it
into the video buffer.

69
drivers/video/video_emul_imager.c

@ -38,8 +38,8 @@ LOG_MODULE_REGISTER(video_emul_imager, CONFIG_VIDEO_LOG_LEVEL);
uint8_t emul_imager_fake_regs[10]; uint8_t emul_imager_fake_regs[10];
enum emul_imager_fmt_id { enum emul_imager_fmt_id {
RGB565_64x20, RGB565_320x240,
YUYV_64x20, YUYV_320x240,
}; };
struct emul_imager_reg { struct emul_imager_reg {
@ -62,8 +62,8 @@ struct emul_imager_config {
}; };
struct emul_imager_data { struct emul_imager_data {
/* First field is a framebuffer for I/O emulation purpose */ /* First field is a line buffer for I/O emulation purpose */
uint8_t framebuffer[CONFIG_VIDEO_EMUL_IMAGER_FRAMEBUFFER_SIZE]; uint8_t framebuffer[320 * sizeof(uint16_t)];
/* Other fields are shared with real hardware drivers */ /* Other fields are shared with real hardware drivers */
const struct emul_imager_mode *mode; const struct emul_imager_mode *mode;
enum emul_imager_fmt_id fmt_id; enum emul_imager_fmt_id fmt_id;
@ -83,46 +83,46 @@ static const struct emul_imager_reg emul_imager_init_regs[] = {
* to set the timing parameters and other mode-dependent configuration. * to set the timing parameters and other mode-dependent configuration.
*/ */
static const struct emul_imager_reg emul_imager_rgb565_64x20[] = { static const struct emul_imager_reg emul_imager_rgb565_320x240[] = {
{EMUL_IMAGER_REG_TIMING1, 0x64}, {EMUL_IMAGER_REG_TIMING1, 0x32},
{EMUL_IMAGER_REG_TIMING2, 0x20}, {EMUL_IMAGER_REG_TIMING2, 0x24},
{0}, {0},
}; };
static const struct emul_imager_reg emul_imager_rgb565_64x20_15fps[] = { static const struct emul_imager_reg emul_imager_rgb565_320x240_15fps[] = {
{EMUL_IMAGER_REG_TIMING3, 15}, {EMUL_IMAGER_REG_TIMING3, 15},
{0}, {0},
}; };
static const struct emul_imager_reg emul_imager_rgb565_64x20_30fps[] = { static const struct emul_imager_reg emul_imager_rgb565_320x240_30fps[] = {
{EMUL_IMAGER_REG_TIMING3, 30}, {EMUL_IMAGER_REG_TIMING3, 30},
{0}, {0},
}; };
static const struct emul_imager_reg emul_imager_rgb565_64x20_60fps[] = { static const struct emul_imager_reg emul_imager_rgb565_320x240_60fps[] = {
{EMUL_IMAGER_REG_TIMING3, 60}, {EMUL_IMAGER_REG_TIMING3, 60},
{0}, {0},
}; };
struct emul_imager_mode emul_imager_rgb565_64x20_modes[] = { struct emul_imager_mode emul_imager_rgb565_320x240_modes[] = {
{.fps = 15, .regs = {emul_imager_rgb565_64x20, emul_imager_rgb565_64x20_15fps}}, {.fps = 15, .regs = {emul_imager_rgb565_320x240, emul_imager_rgb565_320x240_15fps}},
{.fps = 30, .regs = {emul_imager_rgb565_64x20, emul_imager_rgb565_64x20_30fps}}, {.fps = 30, .regs = {emul_imager_rgb565_320x240, emul_imager_rgb565_320x240_30fps}},
{.fps = 60, .regs = {emul_imager_rgb565_64x20, emul_imager_rgb565_64x20_60fps}}, {.fps = 60, .regs = {emul_imager_rgb565_320x240, emul_imager_rgb565_320x240_60fps}},
{0}, {0},
}; };
static const struct emul_imager_reg emul_imager_yuyv_64x20[] = { static const struct emul_imager_reg emul_imager_yuyv_320x240[] = {
{EMUL_IMAGER_REG_TIMING1, 0x64}, {EMUL_IMAGER_REG_TIMING1, 0x32},
{EMUL_IMAGER_REG_TIMING2, 0x20}, {EMUL_IMAGER_REG_TIMING2, 0x24},
{0}, {0},
}; };
static const struct emul_imager_reg emul_imager_yuyv_64x20_15fps[] = { static const struct emul_imager_reg emul_imager_yuyv_320x240_15fps[] = {
{EMUL_IMAGER_REG_TIMING3, 15}, {EMUL_IMAGER_REG_TIMING3, 15},
{0}, {0},
}; };
static const struct emul_imager_reg emul_imager_yuyv_64x20_30fps[] = { static const struct emul_imager_reg emul_imager_yuyv_320x240_30fps[] = {
{EMUL_IMAGER_REG_TIMING3, 30}, {EMUL_IMAGER_REG_TIMING3, 30},
{0}, {0},
}; };
struct emul_imager_mode emul_imager_yuyv_64x20_modes[] = { struct emul_imager_mode emul_imager_yuyv_320x240_modes[] = {
{.fps = 15, .regs = {emul_imager_yuyv_64x20, emul_imager_yuyv_64x20_15fps}}, {.fps = 15, .regs = {emul_imager_yuyv_320x240, emul_imager_yuyv_320x240_15fps}},
{.fps = 30, .regs = {emul_imager_yuyv_64x20, emul_imager_yuyv_64x20_30fps}}, {.fps = 30, .regs = {emul_imager_yuyv_320x240, emul_imager_yuyv_320x240_30fps}},
{0}, {0},
}; };
@ -130,26 +130,27 @@ struct emul_imager_mode emul_imager_yuyv_64x20_modes[] = {
* index, matching fmts[]. * index, matching fmts[].
*/ */
static const struct emul_imager_mode *emul_imager_modes[] = { static const struct emul_imager_mode *emul_imager_modes[] = {
[RGB565_64x20] = emul_imager_rgb565_64x20_modes, [RGB565_320x240] = emul_imager_rgb565_320x240_modes,
[YUYV_64x20] = emul_imager_yuyv_64x20_modes, [YUYV_320x240] = emul_imager_yuyv_320x240_modes,
}; };
/* Video device capabilities where the supported resolutions and pixel formats are listed. /* Video device capabilities where the supported resolutions and pixel formats are listed.
* The format ID is used as index to fetch the matching mode from the list above. * The format ID is used as index to fetch the matching mode from the list above.
*/ */
#define EMUL_IMAGER_VIDEO_FORMAT_CAP(width, height, format) \ #define EMUL_IMAGER_VIDEO_FORMAT_CAP(format, width, height) \
{ \ { \
/* For a real imager, the width and height would be macro parameters */ \
.pixelformat = (format), \ .pixelformat = (format), \
.width_min = (width), \ .width_min = (width), \
.width_max = (width), \ .width_max = (width), \
.width_step = 0, \
.height_min = (height), \ .height_min = (height), \
.height_max = (height), \ .height_max = (height), \
.width_step = 0, \
.height_step = 0, \ .height_step = 0, \
} }
static const struct video_format_cap fmts[] = { static const struct video_format_cap fmts[] = {
[RGB565_64x20] = EMUL_IMAGER_VIDEO_FORMAT_CAP(64, 20, VIDEO_PIX_FMT_RGB565), [RGB565_320x240] = EMUL_IMAGER_VIDEO_FORMAT_CAP(VIDEO_PIX_FMT_RGB565, 320, 240),
[YUYV_64x20] = EMUL_IMAGER_VIDEO_FORMAT_CAP(64, 20, VIDEO_PIX_FMT_YUYV), [YUYV_320x240] = EMUL_IMAGER_VIDEO_FORMAT_CAP(VIDEO_PIX_FMT_YUYV, 320, 240),
{0}, {0},
}; };
@ -351,10 +352,7 @@ static void emul_imager_fill_framebuffer(const struct device *const dev, struct
memset(fb16, 0, fmt->pitch); memset(fb16, 0, fmt->pitch);
} }
/* Duplicate the first row over the whole frame */ /* Note: The framebuffer only contains a single row of data */
for (size_t i = 1; i < fmt->height; i++) {
memcpy(data->framebuffer + fmt->pitch * i, data->framebuffer, fmt->pitch);
}
} }
static int emul_imager_set_fmt(const struct device *const dev, enum video_endpoint_id ep, static int emul_imager_set_fmt(const struct device *const dev, enum video_endpoint_id ep,
@ -368,13 +366,6 @@ static int emul_imager_set_fmt(const struct device *const dev, enum video_endpoi
return -EINVAL; return -EINVAL;
} }
if (fmt->pitch * fmt->height > CONFIG_VIDEO_EMUL_IMAGER_FRAMEBUFFER_SIZE) {
LOG_ERR("%s has %u bytes of memory, unable to support %x %ux%u (%u bytes)",
dev->name, CONFIG_VIDEO_EMUL_IMAGER_FRAMEBUFFER_SIZE, fmt->pixelformat,
fmt->width, fmt->height, fmt->pitch * fmt->height);
return -ENOMEM;
}
if (memcmp(&data->fmt, fmt, sizeof(data->fmt)) == 0) { if (memcmp(&data->fmt, fmt, sizeof(data->fmt)) == 0) {
return 0; return 0;
} }

9
drivers/video/video_emul_rx.c

@ -156,10 +156,13 @@ static void emul_rx_worker(struct k_work *work)
LOG_DBG("Inserting %u bytes into buffer %p", vbuf->bytesused, vbuf->buffer); LOG_DBG("Inserting %u bytes into buffer %p", vbuf->bytesused, vbuf->buffer);
/* Simulate the MIPI/DVP hardware transferring image data from the imager to the /* Simulate the MIPI/DVP hardware transferring image data line-by-line from the
* video buffer memory using DMA. The vbuf->size is checked in emul_rx_enqueue(). * imager to the video buffer memory using DMA copying the data line-by-line over
* the whole frame. vbuf->size is already checked in emul_rx_enqueue().
*/ */
memcpy(vbuf->buffer, cfg->source_dev->data, vbuf->bytesused); for (size_t i = 0; i + fmt->pitch <= vbuf->bytesused; i += fmt->pitch) {
memcpy(vbuf->buffer + i, cfg->source_dev->data, fmt->pitch);
}
/* Once the buffer is completed, submit it to the video buffer */ /* Once the buffer is completed, submit it to the video buffer */
k_fifo_put(&data->fifo_out, vbuf); k_fifo_put(&data->fifo_out, vbuf);

8
tests/drivers/video/api/prj.conf

@ -1,5 +1,11 @@
CONFIG_ZTEST=y CONFIG_ZTEST=y
CONFIG_VIDEO=y CONFIG_VIDEO=y
CONFIG_VIDEO_BUFFER_POOL_SZ_MAX=16384
# Just enough for a single frame in RGB565 format: 320 * 420 * 2 + some margin
CONFIG_VIDEO_BUFFER_POOL_SZ_MAX=300000
CONFIG_VIDEO_BUFFER_POOL_NUM_MAX=1 CONFIG_VIDEO_BUFFER_POOL_NUM_MAX=1
# Required to support larger buffers on native_sim
CONFIG_SYS_HEAP_BIG_ONLY=y
CONFIG_VIDEO_LOG_LEVEL_DBG=y CONFIG_VIDEO_LOG_LEVEL_DBG=y

3
tests/drivers/video/api/src/video_emul.c

@ -184,6 +184,9 @@ ZTEST(video_common, test_video_vbuf)
/* Nothing left in the queue, possible to stop */ /* Nothing left in the queue, possible to stop */
zexpect_ok(video_stream_stop(rx_dev)); zexpect_ok(video_stream_stop(rx_dev));
/* Nothing tested, but this should not crash */
video_buffer_release(vbuf);
} }
ZTEST_SUITE(video_emul, NULL, NULL, NULL, NULL, NULL); ZTEST_SUITE(video_emul, NULL, NULL, NULL, NULL, NULL);

3
tests/drivers/video/api/testcase.yaml

@ -1,8 +1,9 @@
# Copyright (c) 2024 tinyVision.ai Inc. # Copyright (c) 2024-2025 tinyVision.ai Inc.
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
tests: tests:
drivers.video.api: drivers.video.api:
min_ram: 32
tags: tags:
- drivers - drivers
- video - video

Loading…
Cancel
Save