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

69
drivers/video/video_emul_imager.c

@ -38,8 +38,8 @@ LOG_MODULE_REGISTER(video_emul_imager, CONFIG_VIDEO_LOG_LEVEL); @@ -38,8 +38,8 @@ LOG_MODULE_REGISTER(video_emul_imager, CONFIG_VIDEO_LOG_LEVEL);
uint8_t emul_imager_fake_regs[10];
enum emul_imager_fmt_id {
RGB565_64x20,
YUYV_64x20,
RGB565_320x240,
YUYV_320x240,
};
struct emul_imager_reg {
@ -62,8 +62,8 @@ struct emul_imager_config { @@ -62,8 +62,8 @@ struct emul_imager_config {
};
struct emul_imager_data {
/* First field is a framebuffer for I/O emulation purpose */
uint8_t framebuffer[CONFIG_VIDEO_EMUL_IMAGER_FRAMEBUFFER_SIZE];
/* First field is a line buffer for I/O emulation purpose */
uint8_t framebuffer[320 * sizeof(uint16_t)];
/* Other fields are shared with real hardware drivers */
const struct emul_imager_mode *mode;
enum emul_imager_fmt_id fmt_id;
@ -83,46 +83,46 @@ static const struct emul_imager_reg emul_imager_init_regs[] = { @@ -83,46 +83,46 @@ static const struct emul_imager_reg emul_imager_init_regs[] = {
* to set the timing parameters and other mode-dependent configuration.
*/
static const struct emul_imager_reg emul_imager_rgb565_64x20[] = {
{EMUL_IMAGER_REG_TIMING1, 0x64},
{EMUL_IMAGER_REG_TIMING2, 0x20},
static const struct emul_imager_reg emul_imager_rgb565_320x240[] = {
{EMUL_IMAGER_REG_TIMING1, 0x32},
{EMUL_IMAGER_REG_TIMING2, 0x24},
{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},
{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},
{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},
{0},
};
struct emul_imager_mode emul_imager_rgb565_64x20_modes[] = {
{.fps = 15, .regs = {emul_imager_rgb565_64x20, emul_imager_rgb565_64x20_15fps}},
{.fps = 30, .regs = {emul_imager_rgb565_64x20, emul_imager_rgb565_64x20_30fps}},
{.fps = 60, .regs = {emul_imager_rgb565_64x20, emul_imager_rgb565_64x20_60fps}},
struct emul_imager_mode emul_imager_rgb565_320x240_modes[] = {
{.fps = 15, .regs = {emul_imager_rgb565_320x240, emul_imager_rgb565_320x240_15fps}},
{.fps = 30, .regs = {emul_imager_rgb565_320x240, emul_imager_rgb565_320x240_30fps}},
{.fps = 60, .regs = {emul_imager_rgb565_320x240, emul_imager_rgb565_320x240_60fps}},
{0},
};
static const struct emul_imager_reg emul_imager_yuyv_64x20[] = {
{EMUL_IMAGER_REG_TIMING1, 0x64},
{EMUL_IMAGER_REG_TIMING2, 0x20},
static const struct emul_imager_reg emul_imager_yuyv_320x240[] = {
{EMUL_IMAGER_REG_TIMING1, 0x32},
{EMUL_IMAGER_REG_TIMING2, 0x24},
{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},
{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},
{0},
};
struct emul_imager_mode emul_imager_yuyv_64x20_modes[] = {
{.fps = 15, .regs = {emul_imager_yuyv_64x20, emul_imager_yuyv_64x20_15fps}},
{.fps = 30, .regs = {emul_imager_yuyv_64x20, emul_imager_yuyv_64x20_30fps}},
struct emul_imager_mode emul_imager_yuyv_320x240_modes[] = {
{.fps = 15, .regs = {emul_imager_yuyv_320x240, emul_imager_yuyv_320x240_15fps}},
{.fps = 30, .regs = {emul_imager_yuyv_320x240, emul_imager_yuyv_320x240_30fps}},
{0},
};
@ -130,26 +130,27 @@ struct emul_imager_mode emul_imager_yuyv_64x20_modes[] = { @@ -130,26 +130,27 @@ struct emul_imager_mode emul_imager_yuyv_64x20_modes[] = {
* index, matching fmts[].
*/
static const struct emul_imager_mode *emul_imager_modes[] = {
[RGB565_64x20] = emul_imager_rgb565_64x20_modes,
[YUYV_64x20] = emul_imager_yuyv_64x20_modes,
[RGB565_320x240] = emul_imager_rgb565_320x240_modes,
[YUYV_320x240] = emul_imager_yuyv_320x240_modes,
};
/* 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.
*/
#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), \
.width_min = (width), \
.width_max = (width), \
.width_step = 0, \
.height_min = (height), \
.height_max = (height), \
.width_step = 0, \
.height_step = 0, \
}
static const struct video_format_cap fmts[] = {
[RGB565_64x20] = EMUL_IMAGER_VIDEO_FORMAT_CAP(64, 20, VIDEO_PIX_FMT_RGB565),
[YUYV_64x20] = EMUL_IMAGER_VIDEO_FORMAT_CAP(64, 20, VIDEO_PIX_FMT_YUYV),
[RGB565_320x240] = EMUL_IMAGER_VIDEO_FORMAT_CAP(VIDEO_PIX_FMT_RGB565, 320, 240),
[YUYV_320x240] = EMUL_IMAGER_VIDEO_FORMAT_CAP(VIDEO_PIX_FMT_YUYV, 320, 240),
{0},
};
@ -351,10 +352,7 @@ static void emul_imager_fill_framebuffer(const struct device *const dev, struct @@ -351,10 +352,7 @@ static void emul_imager_fill_framebuffer(const struct device *const dev, struct
memset(fb16, 0, fmt->pitch);
}
/* Duplicate the first row over the whole frame */
for (size_t i = 1; i < fmt->height; i++) {
memcpy(data->framebuffer + fmt->pitch * i, data->framebuffer, fmt->pitch);
}
/* Note: The framebuffer only contains a single row of data */
}
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 @@ -368,13 +366,6 @@ static int emul_imager_set_fmt(const struct device *const dev, enum video_endpoi
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) {
return 0;
}

9
drivers/video/video_emul_rx.c

@ -156,10 +156,13 @@ static void emul_rx_worker(struct k_work *work) @@ -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);
/* Simulate the MIPI/DVP hardware transferring image data from the imager to the
* video buffer memory using DMA. The vbuf->size is checked in emul_rx_enqueue().
/* Simulate the MIPI/DVP hardware transferring image data line-by-line from the
* 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 */
k_fifo_put(&data->fifo_out, vbuf);

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

@ -1,5 +1,11 @@ @@ -1,5 +1,11 @@
CONFIG_ZTEST=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
# Required to support larger buffers on native_sim
CONFIG_SYS_HEAP_BIG_ONLY=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) @@ -184,6 +184,9 @@ ZTEST(video_common, test_video_vbuf)
/* Nothing left in the queue, possible to stop */
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);

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

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

Loading…
Cancel
Save