Skip to content

Commit f759f5b

Browse files
committed
drm/vc4: tests: Introduce a mocking infrastructure
In order to test the current atomic_check hooks we need to have a DRM device that has roughly the same capabilities and layout that the actual hardware. We'll also need a bunch of functions to create arbitrary atomic states. Let's create some helpers to create a device that behaves like the real one, and some helpers to maintain the atomic state we want to check. Reviewed-by: Javier Martinez Canillas <javierm@redhat.com> Reviewed-by: Maíra Canal <mcanal@igalia.com> Link: https://lore.kernel.org/r/20221123-rpi-kunit-tests-v3-17-4615a663a84a@cerno.tech Signed-off-by: Maxime Ripard <maxime@cerno.tech>
1 parent 640dbcc commit f759f5b

File tree

12 files changed

+511
-13
lines changed

12 files changed

+511
-13
lines changed

drivers/gpu/drm/vc4/Kconfig

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,19 @@ config DRM_VC4_HDMI_CEC
3434
help
3535
Choose this option if you have a Broadcom VC4 GPU
3636
and want to use CEC.
37+
38+
config DRM_VC4_KUNIT_TEST
39+
bool "KUnit tests for VC4" if !KUNIT_ALL_TESTS
40+
depends on DRM_VC4 && KUNIT
41+
select DRM_KUNIT_TEST_HELPERS
42+
default KUNIT_ALL_TESTS
43+
help
44+
This builds unit tests for the VC4 DRM/KMS driver. This option is
45+
not useful for distributions or general kernels, but only for kernel
46+
developers working on the VC4 driver.
47+
48+
For more information on KUnit and unit tests in general,
49+
please refer to the KUnit documentation in
50+
Documentation/dev-tools/kunit/.
51+
52+
If in doubt, say "N".

drivers/gpu/drm/vc4/Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ vc4-y := \
2525
vc4_validate.o \
2626
vc4_validate_shaders.o
2727

28+
vc4-$(CONFIG_DRM_VC4_KUNIT_TEST) += \
29+
tests/vc4_mock.o \
30+
tests/vc4_mock_crtc.o \
31+
tests/vc4_mock_output.o \
32+
tests/vc4_mock_plane.o
33+
2834
vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o
2935

3036
obj-$(CONFIG_DRM_VC4) += vc4.o
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CONFIG_ARCH_BCM=y
2+
CONFIG_ARCH_BCM2835=y
3+
CONFIG_BCM2835_MBOX=y
4+
CONFIG_KUNIT=y
5+
CONFIG_DRM=y
6+
CONFIG_DRM_VC4=y
7+
CONFIG_DRM_VC4_KUNIT_TEST=y
8+
CONFIG_MAILBOX=y
9+
CONFIG_RASPBERRYPI_FIRMWARE=y
10+
CONFIG_SND=y
11+
CONFIG_SND_SOC=y
12+
CONFIG_SOUND=y
13+
CONFIG_COMMON_CLK=y
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <drm/drm_drv.h>
4+
#include <drm/drm_kunit_helpers.h>
5+
6+
#include <kunit/test.h>
7+
8+
#include "vc4_mock.h"
9+
10+
struct vc4_mock_output_desc {
11+
enum vc4_encoder_type vc4_encoder_type;
12+
unsigned int encoder_type;
13+
unsigned int connector_type;
14+
};
15+
16+
#define VC4_MOCK_OUTPUT_DESC(_vc4_type, _etype, _ctype) \
17+
{ \
18+
.vc4_encoder_type = _vc4_type, \
19+
.encoder_type = _etype, \
20+
.connector_type = _ctype, \
21+
}
22+
23+
struct vc4_mock_pipe_desc {
24+
const struct vc4_crtc_data *data;
25+
const struct vc4_mock_output_desc *outputs;
26+
unsigned int noutputs;
27+
};
28+
29+
#define VC4_MOCK_CRTC_DESC(_data, ...) \
30+
{ \
31+
.data = _data, \
32+
.outputs = (struct vc4_mock_output_desc[]) { __VA_ARGS__ }, \
33+
.noutputs = sizeof((struct vc4_mock_output_desc[]) { __VA_ARGS__ }) / \
34+
sizeof(struct vc4_mock_output_desc), \
35+
}
36+
37+
#define VC4_MOCK_PIXELVALVE_DESC(_data, ...) \
38+
VC4_MOCK_CRTC_DESC(&(_data)->base, __VA_ARGS__)
39+
40+
struct vc4_mock_desc {
41+
const struct vc4_mock_pipe_desc *pipes;
42+
unsigned int npipes;
43+
};
44+
45+
#define VC4_MOCK_DESC(...) \
46+
{ \
47+
.pipes = (struct vc4_mock_pipe_desc[]) { __VA_ARGS__ }, \
48+
.npipes = sizeof((struct vc4_mock_pipe_desc[]) { __VA_ARGS__ }) / \
49+
sizeof(struct vc4_mock_pipe_desc), \
50+
}
51+
52+
static const struct vc4_mock_desc vc4_mock =
53+
VC4_MOCK_DESC(
54+
VC4_MOCK_CRTC_DESC(&vc4_txp_crtc_data,
55+
VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP,
56+
DRM_MODE_ENCODER_VIRTUAL,
57+
DRM_MODE_CONNECTOR_WRITEBACK)),
58+
VC4_MOCK_PIXELVALVE_DESC(&bcm2835_pv0_data,
59+
VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_DSI0,
60+
DRM_MODE_ENCODER_DSI,
61+
DRM_MODE_CONNECTOR_DSI),
62+
VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_DPI,
63+
DRM_MODE_ENCODER_DPI,
64+
DRM_MODE_CONNECTOR_DPI)),
65+
VC4_MOCK_PIXELVALVE_DESC(&bcm2835_pv1_data,
66+
VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_DSI1,
67+
DRM_MODE_ENCODER_DSI,
68+
DRM_MODE_CONNECTOR_DSI)),
69+
VC4_MOCK_PIXELVALVE_DESC(&bcm2835_pv2_data,
70+
VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_HDMI0,
71+
DRM_MODE_ENCODER_TMDS,
72+
DRM_MODE_CONNECTOR_HDMIA),
73+
VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_VEC,
74+
DRM_MODE_ENCODER_TVDAC,
75+
DRM_MODE_CONNECTOR_Composite)),
76+
);
77+
78+
static const struct vc4_mock_desc vc5_mock =
79+
VC4_MOCK_DESC(
80+
VC4_MOCK_CRTC_DESC(&vc4_txp_crtc_data,
81+
VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP,
82+
DRM_MODE_ENCODER_VIRTUAL,
83+
DRM_MODE_CONNECTOR_WRITEBACK)),
84+
VC4_MOCK_PIXELVALVE_DESC(&bcm2711_pv0_data,
85+
VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_DSI0,
86+
DRM_MODE_ENCODER_DSI,
87+
DRM_MODE_CONNECTOR_DSI),
88+
VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_DPI,
89+
DRM_MODE_ENCODER_DPI,
90+
DRM_MODE_CONNECTOR_DPI)),
91+
VC4_MOCK_PIXELVALVE_DESC(&bcm2711_pv1_data,
92+
VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_DSI1,
93+
DRM_MODE_ENCODER_DSI,
94+
DRM_MODE_CONNECTOR_DSI)),
95+
VC4_MOCK_PIXELVALVE_DESC(&bcm2711_pv2_data,
96+
VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_HDMI0,
97+
DRM_MODE_ENCODER_TMDS,
98+
DRM_MODE_CONNECTOR_HDMIA)),
99+
VC4_MOCK_PIXELVALVE_DESC(&bcm2711_pv3_data,
100+
VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_VEC,
101+
DRM_MODE_ENCODER_TVDAC,
102+
DRM_MODE_CONNECTOR_Composite)),
103+
VC4_MOCK_PIXELVALVE_DESC(&bcm2711_pv4_data,
104+
VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_HDMI1,
105+
DRM_MODE_ENCODER_TMDS,
106+
DRM_MODE_CONNECTOR_HDMIA)),
107+
);
108+
109+
static int __build_one_pipe(struct kunit *test, struct drm_device *drm,
110+
const struct vc4_mock_pipe_desc *pipe)
111+
{
112+
struct vc4_dummy_plane *dummy_plane;
113+
struct drm_plane *plane;
114+
struct vc4_dummy_crtc *dummy_crtc;
115+
struct drm_crtc *crtc;
116+
unsigned int i;
117+
118+
dummy_plane = vc4_dummy_plane(test, drm, DRM_PLANE_TYPE_PRIMARY);
119+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_plane);
120+
121+
plane = &dummy_plane->plane.base;
122+
dummy_crtc = vc4_mock_pv(test, drm, plane, pipe->data);
123+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_crtc);
124+
125+
crtc = &dummy_crtc->crtc.base;
126+
for (i = 0; i < pipe->noutputs; i++) {
127+
const struct vc4_mock_output_desc *mock_output = &pipe->outputs[i];
128+
struct vc4_dummy_output *dummy_output;
129+
130+
dummy_output = vc4_dummy_output(test, drm, crtc,
131+
mock_output->vc4_encoder_type,
132+
mock_output->encoder_type,
133+
mock_output->connector_type);
134+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_output);
135+
}
136+
137+
return 0;
138+
}
139+
140+
static int __build_mock(struct kunit *test, struct drm_device *drm,
141+
const struct vc4_mock_desc *mock)
142+
{
143+
unsigned int i;
144+
145+
for (i = 0; i < mock->npipes; i++) {
146+
const struct vc4_mock_pipe_desc *pipe = &mock->pipes[i];
147+
int ret;
148+
149+
ret = __build_one_pipe(test, drm, pipe);
150+
KUNIT_ASSERT_EQ(test, ret, 0);
151+
}
152+
153+
return 0;
154+
}
155+
156+
static struct vc4_dev *__mock_device(struct kunit *test, bool is_vc5)
157+
{
158+
struct drm_device *drm;
159+
const struct drm_driver *drv = is_vc5 ? &vc5_drm_driver : &vc4_drm_driver;
160+
const struct vc4_mock_desc *desc = is_vc5 ? &vc5_mock : &vc4_mock;
161+
struct vc4_dev *vc4;
162+
struct device *dev;
163+
int ret;
164+
165+
dev = drm_kunit_helper_alloc_device(test);
166+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
167+
168+
vc4 = drm_kunit_helper_alloc_drm_device_with_driver(test, dev,
169+
struct vc4_dev, base,
170+
drv);
171+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
172+
173+
vc4->dev = dev;
174+
vc4->is_vc5 = is_vc5;
175+
176+
vc4->hvs = __vc4_hvs_alloc(vc4, NULL);
177+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4->hvs);
178+
179+
drm = &vc4->base;
180+
ret = __build_mock(test, drm, desc);
181+
KUNIT_ASSERT_EQ(test, ret, 0);
182+
183+
ret = vc4_kms_load(drm);
184+
KUNIT_ASSERT_EQ(test, ret, 0);
185+
186+
ret = drm_dev_register(drm, 0);
187+
KUNIT_ASSERT_EQ(test, ret, 0);
188+
189+
return vc4;
190+
}
191+
192+
struct vc4_dev *vc4_mock_device(struct kunit *test)
193+
{
194+
return __mock_device(test, false);
195+
}
196+
197+
struct vc4_dev *vc5_mock_device(struct kunit *test)
198+
{
199+
return __mock_device(test, true);
200+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
3+
#ifndef VC4_MOCK_H_
4+
#define VC4_MOCK_H_
5+
6+
#include "../vc4_drv.h"
7+
8+
static inline
9+
struct drm_crtc *vc4_find_crtc_for_encoder(struct kunit *test,
10+
struct drm_device *drm,
11+
struct drm_encoder *encoder)
12+
{
13+
struct drm_crtc *crtc;
14+
15+
KUNIT_ASSERT_EQ(test, hweight32(encoder->possible_crtcs), 1);
16+
17+
drm_for_each_crtc(crtc, drm)
18+
if (encoder->possible_crtcs & drm_crtc_mask(crtc))
19+
return crtc;
20+
21+
return NULL;
22+
}
23+
24+
struct vc4_dummy_plane {
25+
struct vc4_plane plane;
26+
};
27+
28+
struct vc4_dummy_plane *vc4_dummy_plane(struct kunit *test,
29+
struct drm_device *drm,
30+
enum drm_plane_type type);
31+
32+
struct vc4_dummy_crtc {
33+
struct vc4_crtc crtc;
34+
};
35+
36+
struct vc4_dummy_crtc *vc4_mock_pv(struct kunit *test,
37+
struct drm_device *drm,
38+
struct drm_plane *plane,
39+
const struct vc4_crtc_data *data);
40+
41+
struct vc4_dummy_output {
42+
struct vc4_encoder encoder;
43+
struct drm_connector connector;
44+
};
45+
46+
struct vc4_dummy_output *vc4_dummy_output(struct kunit *test,
47+
struct drm_device *drm,
48+
struct drm_crtc *crtc,
49+
enum vc4_encoder_type vc4_encoder_type,
50+
unsigned int kms_encoder_type,
51+
unsigned int connector_type);
52+
53+
struct vc4_dev *vc4_mock_device(struct kunit *test);
54+
struct vc4_dev *vc5_mock_device(struct kunit *test);
55+
56+
int vc4_mock_atomic_add_output(struct kunit *test, struct drm_device *drm,
57+
enum vc4_encoder_type type,
58+
struct drm_atomic_state *state);
59+
60+
#endif // VC4_MOCK_H_
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <drm/drm_atomic_state_helper.h>
4+
#include <drm/drm_modeset_helper_vtables.h>
5+
6+
#include <kunit/test.h>
7+
8+
#include "vc4_mock.h"
9+
10+
static const struct drm_crtc_helper_funcs vc4_dummy_crtc_helper_funcs = {
11+
.atomic_check = vc4_crtc_atomic_check,
12+
};
13+
14+
static const struct drm_crtc_funcs vc4_dummy_crtc_funcs = {
15+
.atomic_destroy_state = vc4_crtc_destroy_state,
16+
.atomic_duplicate_state = vc4_crtc_duplicate_state,
17+
.reset = vc4_crtc_reset,
18+
};
19+
20+
struct vc4_dummy_crtc *vc4_mock_pv(struct kunit *test,
21+
struct drm_device *drm,
22+
struct drm_plane *plane,
23+
const struct vc4_crtc_data *data)
24+
{
25+
struct vc4_dummy_crtc *dummy_crtc;
26+
struct vc4_crtc *vc4_crtc;
27+
int ret;
28+
29+
dummy_crtc = kunit_kzalloc(test, sizeof(*dummy_crtc), GFP_KERNEL);
30+
KUNIT_ASSERT_NOT_NULL(test, dummy_crtc);
31+
32+
vc4_crtc = &dummy_crtc->crtc;
33+
ret = __vc4_crtc_init(drm, NULL,
34+
vc4_crtc, data, plane,
35+
&vc4_dummy_crtc_funcs,
36+
&vc4_dummy_crtc_helper_funcs,
37+
false);
38+
KUNIT_ASSERT_EQ(test, ret, 0);
39+
40+
return dummy_crtc;
41+
}

0 commit comments

Comments
 (0)