drivers: video: add emulated Imager driver and RX driver
Add a new implementation of a test pattern generator, with the same architecture as real drivers: split receiver core and I2C-controlled sub-device, with changes of video format in "zephyr,emul-imager" leads to different data produced by "zephyr,emul-rx". Signed-off-by: Josuah Demangeon <me@josuah.net>
This commit is contained in:
parent
46a262ffe6
commit
9e908b1b72
12 changed files with 1173 additions and 4 deletions
|
@ -15,19 +15,19 @@
|
|||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
test_gpio: gpio@deadbeef {
|
||||
test_gpio: gpio@10001000 {
|
||||
compatible = "vnd,gpio";
|
||||
gpio-controller;
|
||||
reg = <0xdeadbeef 0x1000>;
|
||||
reg = <0x10001000 0x1000>;
|
||||
#gpio-cells = <0x2>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
test_i2c: i2c@11112222 {
|
||||
test_i2c: i2c@10002000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "vnd,i2c";
|
||||
reg = <0x11112222 0x1000>;
|
||||
reg = <0x10002000 0x1000>;
|
||||
status = "okay";
|
||||
clock-frequency = <100000>;
|
||||
|
||||
|
@ -65,6 +65,38 @@
|
|||
reg = <0x5>;
|
||||
reset-gpios = <&test_gpio 0 0>;
|
||||
};
|
||||
|
||||
test_i2c_video_emul_imager: video_emul_imager@6 {
|
||||
compatible = "zephyr,video-emul-imager";
|
||||
reg = <0x6>;
|
||||
|
||||
port {
|
||||
test_video_emul_imager_ep_out: endpoint {
|
||||
remote-endpoint-label = "test_video_emul_rx_ep_in";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
test_video_emul_rx: video_emul_rx@10003000 {
|
||||
compatible = "zephyr,video-emul-rx";
|
||||
reg = <0x10003000 0x1000>;
|
||||
|
||||
port {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
test_video_emul_rx_ep_in: endpoint@0 {
|
||||
reg = <0x0>;
|
||||
remote-endpoint-label = "test_video_emul_imager_ep_out";
|
||||
};
|
||||
|
||||
test_video_emul_rx_ep_out: endpoint@1 {
|
||||
reg = <0x1>;
|
||||
remote-endpoint-label = "application";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,3 +5,4 @@ find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
|||
project(integration)
|
||||
|
||||
target_sources(app PRIVATE src/video_common.c)
|
||||
target_sources(app PRIVATE src/video_emul.c)
|
||||
|
|
53
tests/drivers/video/api/app.overlay
Normal file
53
tests/drivers/video/api/app.overlay
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2024 tinyVision.ai Inc.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/ {
|
||||
test {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
test_i2c: i2c@10002000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "vnd,i2c";
|
||||
reg = <0x10002000 0x1000>;
|
||||
clock-frequency = <100000>;
|
||||
status = "okay";
|
||||
|
||||
test_video_emul_imager: video_emul_imager@6 {
|
||||
compatible = "zephyr,video-emul-imager";
|
||||
status = "okay";
|
||||
reg = <0x6>;
|
||||
|
||||
port {
|
||||
test_video_emul_imager_ep_out: endpoint {
|
||||
remote-endpoint-label = "test_video_emul_rx_ep_in";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
test_video_emul_rx: video_emul_rx@10003000 {
|
||||
compatible = "zephyr,video-emul-rx";
|
||||
reg = <0x10003000 0x1000>;
|
||||
status = "okay";
|
||||
|
||||
port {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
test_video_emul_rx_ep_in: endpoint@0 {
|
||||
reg = <0x0>;
|
||||
remote-endpoint-label = "test_video_emul_imager_ep_out";
|
||||
};
|
||||
|
||||
test_video_emul_rx_ep_out: endpoint@1 {
|
||||
reg = <0x1>;
|
||||
remote-endpoint-label = "application";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
189
tests/drivers/video/api/src/video_emul.c
Normal file
189
tests/drivers/video/api/src/video_emul.c
Normal file
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* Copyright (c) 2024 tinyVision.ai Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/ztest.h>
|
||||
#include <zephyr/drivers/video.h>
|
||||
#include <zephyr/drivers/video-controls.h>
|
||||
|
||||
const struct device *rx_dev = DEVICE_DT_GET(DT_NODELABEL(test_video_emul_rx));
|
||||
const struct device *imager_dev = DEVICE_DT_GET(DT_NODELABEL(test_video_emul_imager));
|
||||
|
||||
ZTEST(video_common, test_video_device)
|
||||
{
|
||||
zexpect_true(device_is_ready(rx_dev));
|
||||
zexpect_true(device_is_ready(imager_dev));
|
||||
|
||||
zexpect_ok(video_stream_start(imager_dev));
|
||||
zexpect_ok(video_stream_stop(imager_dev));
|
||||
|
||||
zexpect_ok(video_stream_start(rx_dev));
|
||||
zexpect_ok(video_stream_stop(rx_dev));
|
||||
}
|
||||
|
||||
ZTEST(video_common, test_video_format)
|
||||
{
|
||||
struct video_caps caps = {0};
|
||||
struct video_format fmt = {0};
|
||||
|
||||
zexpect_ok(video_get_caps(imager_dev, VIDEO_EP_OUT, &caps));
|
||||
|
||||
/* Test all the formats listed in the caps, the min and max values */
|
||||
for (size_t i = 0; caps.format_caps[i].pixelformat != 0; i++) {
|
||||
fmt.pixelformat = caps.format_caps[i].pixelformat;
|
||||
|
||||
fmt.height = caps.format_caps[i].height_min;
|
||||
fmt.width = caps.format_caps[i].width_min;
|
||||
zexpect_ok(video_set_format(imager_dev, VIDEO_EP_OUT, &fmt));
|
||||
zexpect_ok(video_get_format(imager_dev, VIDEO_EP_OUT, &fmt));
|
||||
zexpect_equal(fmt.pixelformat, caps.format_caps[i].pixelformat);
|
||||
zexpect_equal(fmt.width, caps.format_caps[i].width_min);
|
||||
zexpect_equal(fmt.height, caps.format_caps[i].height_min);
|
||||
|
||||
fmt.height = caps.format_caps[i].height_max;
|
||||
fmt.width = caps.format_caps[i].width_min;
|
||||
zexpect_ok(video_set_format(imager_dev, VIDEO_EP_OUT, &fmt));
|
||||
zexpect_ok(video_get_format(imager_dev, VIDEO_EP_OUT, &fmt));
|
||||
zexpect_equal(fmt.pixelformat, caps.format_caps[i].pixelformat);
|
||||
zexpect_equal(fmt.width, caps.format_caps[i].width_max);
|
||||
zexpect_equal(fmt.height, caps.format_caps[i].height_min);
|
||||
|
||||
fmt.height = caps.format_caps[i].height_min;
|
||||
fmt.width = caps.format_caps[i].width_max;
|
||||
zexpect_ok(video_set_format(imager_dev, VIDEO_EP_OUT, &fmt));
|
||||
zexpect_ok(video_get_format(imager_dev, VIDEO_EP_OUT, &fmt));
|
||||
zexpect_equal(fmt.pixelformat, caps.format_caps[i].pixelformat);
|
||||
zexpect_equal(fmt.width, caps.format_caps[i].width_min);
|
||||
zexpect_equal(fmt.height, caps.format_caps[i].height_max);
|
||||
|
||||
fmt.height = caps.format_caps[i].height_max;
|
||||
fmt.width = caps.format_caps[i].width_max;
|
||||
zexpect_ok(video_set_format(imager_dev, VIDEO_EP_OUT, &fmt));
|
||||
zexpect_ok(video_get_format(imager_dev, VIDEO_EP_OUT, &fmt));
|
||||
zexpect_equal(fmt.pixelformat, caps.format_caps[i].pixelformat);
|
||||
zexpect_equal(fmt.width, caps.format_caps[i].width_max);
|
||||
zexpect_equal(fmt.height, caps.format_caps[i].height_max);
|
||||
}
|
||||
|
||||
fmt.pixelformat = 0x00000000;
|
||||
zexpect_not_ok(video_set_format(imager_dev, VIDEO_EP_OUT, &fmt));
|
||||
zexpect_ok(video_get_format(imager_dev, VIDEO_EP_OUT, &fmt));
|
||||
zexpect_not_equal(fmt.pixelformat, 0x00000000, "should not store wrong formats");
|
||||
}
|
||||
|
||||
ZTEST(video_common, test_video_frmival)
|
||||
{
|
||||
struct video_format fmt;
|
||||
struct video_frmival_enum fie = {.format = &fmt};
|
||||
|
||||
/* Pick the current format for testing the frame interval enumeration */
|
||||
zexpect_ok(video_get_format(imager_dev, VIDEO_EP_OUT, &fmt));
|
||||
|
||||
/* Do a first enumeration of frame intervals, expected to work */
|
||||
zexpect_ok(video_enum_frmival(imager_dev, VIDEO_EP_OUT, &fie));
|
||||
zexpect_equal(fie.index, 1, "fie's index should increment by one at every iteration");
|
||||
|
||||
/* Test that every value of the frame interval enumerator can be applied */
|
||||
do {
|
||||
struct video_frmival q, a;
|
||||
uint32_t min, max, step;
|
||||
|
||||
zexpect_equal_ptr(fie.format, &fmt, "the format should not be changed");
|
||||
zexpect_true(fie.type == VIDEO_FRMIVAL_TYPE_STEPWISE ||
|
||||
fie.type == VIDEO_FRMIVAL_TYPE_DISCRETE);
|
||||
|
||||
switch (fie.type) {
|
||||
case VIDEO_FRMIVAL_TYPE_STEPWISE:
|
||||
/* Get everthing under the same denominator */
|
||||
q.denominator = fie.stepwise.min.denominator *
|
||||
fie.stepwise.max.denominator *
|
||||
fie.stepwise.step.denominator;
|
||||
min = fie.stepwise.max.denominator * fie.stepwise.step.denominator *
|
||||
fie.stepwise.min.numerator;
|
||||
max = fie.stepwise.min.denominator * fie.stepwise.step.denominator *
|
||||
fie.stepwise.max.numerator;
|
||||
step = fie.stepwise.min.denominator * fie.stepwise.max.denominator *
|
||||
fie.stepwise.step.numerator;
|
||||
|
||||
/* Test every supported frame interval */
|
||||
for (q.numerator = min; q.numerator <= max; q.numerator += step) {
|
||||
zexpect_ok(video_set_frmival(imager_dev, VIDEO_EP_OUT, &q));
|
||||
zexpect_ok(video_get_frmival(imager_dev, VIDEO_EP_OUT, &a));
|
||||
zexpect_equal(video_frmival_nsec(&q), video_frmival_nsec(&a));
|
||||
}
|
||||
break;
|
||||
case VIDEO_FRMIVAL_TYPE_DISCRETE:
|
||||
/* There is just one frame interval to test */
|
||||
zexpect_ok(video_set_frmival(imager_dev, VIDEO_EP_OUT, &fie.discrete));
|
||||
zexpect_ok(video_get_frmival(imager_dev, VIDEO_EP_OUT, &a));
|
||||
|
||||
zexpect_equal(video_frmival_nsec(&fie.discrete), video_frmival_nsec(&a));
|
||||
break;
|
||||
}
|
||||
} while (video_enum_frmival(imager_dev, VIDEO_EP_OUT, &fie) == 0);
|
||||
}
|
||||
|
||||
ZTEST(video_common, test_video_ctrl)
|
||||
{
|
||||
int value;
|
||||
|
||||
/* Exposure control, expected to be supported by all imagers */
|
||||
zexpect_ok(video_set_ctrl(imager_dev, VIDEO_CID_EXPOSURE, (void *)30));
|
||||
zexpect_ok(video_get_ctrl(imager_dev, VIDEO_CID_EXPOSURE, &value));
|
||||
zexpect_equal(value, 30);
|
||||
|
||||
/* Gain control, expected to be supported by all imagers */
|
||||
zexpect_ok(video_set_ctrl(imager_dev, VIDEO_CID_GAIN, (void *)30));
|
||||
zexpect_ok(video_get_ctrl(imager_dev, VIDEO_CID_GAIN, &value));
|
||||
zexpect_equal(value, 30);
|
||||
}
|
||||
|
||||
ZTEST(video_common, test_video_vbuf)
|
||||
{
|
||||
struct video_caps caps;
|
||||
struct video_format fmt;
|
||||
struct video_buffer *vbuf = NULL;
|
||||
|
||||
/* Get a list of supported format */
|
||||
zexpect_ok(video_get_caps(rx_dev, VIDEO_EP_OUT, &caps));
|
||||
|
||||
/* Pick set first format, just to use something supported */
|
||||
fmt.pixelformat = caps.format_caps[0].pixelformat;
|
||||
fmt.width = caps.format_caps[0].width_max;
|
||||
fmt.height = caps.format_caps[0].height_max;
|
||||
fmt.pitch = fmt.width * 2;
|
||||
zexpect_ok(video_set_format(rx_dev, VIDEO_EP_OUT, &fmt));
|
||||
|
||||
/* Allocate a buffer, assuming prj.conf gives enough memory for it */
|
||||
vbuf = video_buffer_alloc(fmt.pitch * fmt.height);
|
||||
zexpect_not_null(vbuf);
|
||||
|
||||
/* Start the virtual hardware */
|
||||
zexpect_ok(video_stream_start(rx_dev));
|
||||
|
||||
/* Enqueue a first buffer */
|
||||
zexpect_ok(video_enqueue(rx_dev, VIDEO_EP_OUT, vbuf));
|
||||
|
||||
/* Receive the completed buffer */
|
||||
zexpect_ok(video_dequeue(rx_dev, VIDEO_EP_OUT, &vbuf, K_FOREVER));
|
||||
zexpect_not_null(vbuf);
|
||||
zexpect_equal(vbuf->bytesused, vbuf->size);
|
||||
|
||||
/* Enqueue back the same buffer */
|
||||
zexpect_ok(video_enqueue(rx_dev, VIDEO_EP_OUT, vbuf));
|
||||
|
||||
/* Process the remaining buffers */
|
||||
zexpect_ok(video_flush(rx_dev, VIDEO_EP_OUT, false));
|
||||
|
||||
/* Expect the buffer to immediately be available */
|
||||
zexpect_ok(video_dequeue(rx_dev, VIDEO_EP_OUT, &vbuf, K_FOREVER));
|
||||
zexpect_not_null(vbuf);
|
||||
zexpect_equal(vbuf->bytesused, vbuf->size);
|
||||
|
||||
/* Nothing left in the queue, possible to stop */
|
||||
zexpect_ok(video_stream_stop(rx_dev));
|
||||
}
|
||||
|
||||
ZTEST_SUITE(video_emul, NULL, NULL, NULL, NULL, NULL);
|
Loading…
Add table
Add a link
Reference in a new issue