Unverified Commit f759f5b5 authored by Maxime Ripard's avatar Maxime Ripard
Browse files

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: default avatarJavier Martinez Canillas <javierm@redhat.com>
Reviewed-by: default avatarMaíra Canal <mcanal@igalia.com>
Link: https://lore.kernel.org/r/20221123-rpi-kunit-tests-v3-17-4615a663a84a@cerno.tech


Signed-off-by: default avatarMaxime Ripard <maxime@cerno.tech>
parent 640dbcc9
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -34,3 +34,19 @@ config DRM_VC4_HDMI_CEC
	help
	  Choose this option if you have a Broadcom VC4 GPU
	  and want to use CEC.

config DRM_VC4_KUNIT_TEST
	bool "KUnit tests for VC4" if !KUNIT_ALL_TESTS
	depends on DRM_VC4 && KUNIT
	select DRM_KUNIT_TEST_HELPERS
	default KUNIT_ALL_TESTS
	help
	  This builds unit tests for the VC4 DRM/KMS driver. This option is
	  not useful for distributions or general kernels, but only for kernel
	  developers working on the VC4 driver.

	  For more information on KUnit and unit tests in general,
	  please refer to the KUnit documentation in
	  Documentation/dev-tools/kunit/.

	  If in doubt, say "N".
+6 −0
Original line number Diff line number Diff line
@@ -25,6 +25,12 @@ vc4-y := \
	vc4_validate.o \
	vc4_validate_shaders.o

vc4-$(CONFIG_DRM_VC4_KUNIT_TEST) += \
	tests/vc4_mock.o \
	tests/vc4_mock_crtc.o \
	tests/vc4_mock_output.o \
	tests/vc4_mock_plane.o

vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o

obj-$(CONFIG_DRM_VC4)  += vc4.o
+13 −0
Original line number Diff line number Diff line
CONFIG_ARCH_BCM=y
CONFIG_ARCH_BCM2835=y
CONFIG_BCM2835_MBOX=y
CONFIG_KUNIT=y
CONFIG_DRM=y
CONFIG_DRM_VC4=y
CONFIG_DRM_VC4_KUNIT_TEST=y
CONFIG_MAILBOX=y
CONFIG_RASPBERRYPI_FIRMWARE=y
CONFIG_SND=y
CONFIG_SND_SOC=y
CONFIG_SOUND=y
CONFIG_COMMON_CLK=y
+200 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0

#include <drm/drm_drv.h>
#include <drm/drm_kunit_helpers.h>

#include <kunit/test.h>

#include "vc4_mock.h"

struct vc4_mock_output_desc {
	enum vc4_encoder_type	vc4_encoder_type;
	unsigned int		encoder_type;
	unsigned int		connector_type;
};

#define VC4_MOCK_OUTPUT_DESC(_vc4_type, _etype, _ctype)					\
	{										\
		.vc4_encoder_type = _vc4_type,						\
		.encoder_type = _etype,							\
		.connector_type = _ctype,						\
	}

struct vc4_mock_pipe_desc {
	const struct vc4_crtc_data *data;
	const struct vc4_mock_output_desc *outputs;
	unsigned int noutputs;
};

#define VC4_MOCK_CRTC_DESC(_data, ...)							\
	{										\
		.data = _data,								\
		.outputs = (struct vc4_mock_output_desc[]) { __VA_ARGS__ },		\
		.noutputs = sizeof((struct vc4_mock_output_desc[]) { __VA_ARGS__ }) /	\
			     sizeof(struct vc4_mock_output_desc),			\
	}

#define VC4_MOCK_PIXELVALVE_DESC(_data, ...)						\
	VC4_MOCK_CRTC_DESC(&(_data)->base, __VA_ARGS__)

struct vc4_mock_desc {
	const struct vc4_mock_pipe_desc *pipes;
	unsigned int npipes;
};

#define VC4_MOCK_DESC(...)								\
	{										\
		.pipes = (struct vc4_mock_pipe_desc[]) { __VA_ARGS__ },			\
		.npipes = sizeof((struct vc4_mock_pipe_desc[]) { __VA_ARGS__ }) /	\
			     sizeof(struct vc4_mock_pipe_desc),				\
	}

static const struct vc4_mock_desc vc4_mock =
	VC4_MOCK_DESC(
		VC4_MOCK_CRTC_DESC(&vc4_txp_crtc_data,
				   VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP,
							DRM_MODE_ENCODER_VIRTUAL,
							DRM_MODE_CONNECTOR_WRITEBACK)),
		VC4_MOCK_PIXELVALVE_DESC(&bcm2835_pv0_data,
					 VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_DSI0,
							      DRM_MODE_ENCODER_DSI,
							      DRM_MODE_CONNECTOR_DSI),
					 VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_DPI,
							      DRM_MODE_ENCODER_DPI,
							      DRM_MODE_CONNECTOR_DPI)),
		VC4_MOCK_PIXELVALVE_DESC(&bcm2835_pv1_data,
					 VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_DSI1,
							      DRM_MODE_ENCODER_DSI,
							      DRM_MODE_CONNECTOR_DSI)),
		VC4_MOCK_PIXELVALVE_DESC(&bcm2835_pv2_data,
					 VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_HDMI0,
							      DRM_MODE_ENCODER_TMDS,
							      DRM_MODE_CONNECTOR_HDMIA),
					 VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_VEC,
							      DRM_MODE_ENCODER_TVDAC,
							      DRM_MODE_CONNECTOR_Composite)),
);

static const struct vc4_mock_desc vc5_mock =
	VC4_MOCK_DESC(
		VC4_MOCK_CRTC_DESC(&vc4_txp_crtc_data,
				   VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP,
							DRM_MODE_ENCODER_VIRTUAL,
							DRM_MODE_CONNECTOR_WRITEBACK)),
		VC4_MOCK_PIXELVALVE_DESC(&bcm2711_pv0_data,
					 VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_DSI0,
							      DRM_MODE_ENCODER_DSI,
							      DRM_MODE_CONNECTOR_DSI),
					 VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_DPI,
							      DRM_MODE_ENCODER_DPI,
							      DRM_MODE_CONNECTOR_DPI)),
		VC4_MOCK_PIXELVALVE_DESC(&bcm2711_pv1_data,
					 VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_DSI1,
							      DRM_MODE_ENCODER_DSI,
							      DRM_MODE_CONNECTOR_DSI)),
		VC4_MOCK_PIXELVALVE_DESC(&bcm2711_pv2_data,
					 VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_HDMI0,
							      DRM_MODE_ENCODER_TMDS,
							      DRM_MODE_CONNECTOR_HDMIA)),
		VC4_MOCK_PIXELVALVE_DESC(&bcm2711_pv3_data,
					 VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_VEC,
							      DRM_MODE_ENCODER_TVDAC,
							      DRM_MODE_CONNECTOR_Composite)),
		VC4_MOCK_PIXELVALVE_DESC(&bcm2711_pv4_data,
					 VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_HDMI1,
							      DRM_MODE_ENCODER_TMDS,
							      DRM_MODE_CONNECTOR_HDMIA)),
);

static int __build_one_pipe(struct kunit *test, struct drm_device *drm,
			    const struct vc4_mock_pipe_desc *pipe)
{
	struct vc4_dummy_plane *dummy_plane;
	struct drm_plane *plane;
	struct vc4_dummy_crtc *dummy_crtc;
	struct drm_crtc *crtc;
	unsigned int i;

	dummy_plane = vc4_dummy_plane(test, drm, DRM_PLANE_TYPE_PRIMARY);
	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_plane);

	plane = &dummy_plane->plane.base;
	dummy_crtc = vc4_mock_pv(test, drm, plane, pipe->data);
	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_crtc);

	crtc = &dummy_crtc->crtc.base;
	for (i = 0; i < pipe->noutputs; i++) {
		const struct vc4_mock_output_desc *mock_output = &pipe->outputs[i];
		struct vc4_dummy_output *dummy_output;

		dummy_output = vc4_dummy_output(test, drm, crtc,
						mock_output->vc4_encoder_type,
						mock_output->encoder_type,
						mock_output->connector_type);
		KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_output);
	}

	return 0;
}

static int __build_mock(struct kunit *test, struct drm_device *drm,
			const struct vc4_mock_desc *mock)
{
	unsigned int i;

	for (i = 0; i < mock->npipes; i++) {
		const struct vc4_mock_pipe_desc *pipe = &mock->pipes[i];
		int ret;

		ret = __build_one_pipe(test, drm, pipe);
		KUNIT_ASSERT_EQ(test, ret, 0);
	}

	return 0;
}

static struct vc4_dev *__mock_device(struct kunit *test, bool is_vc5)
{
	struct drm_device *drm;
	const struct drm_driver *drv = is_vc5 ? &vc5_drm_driver : &vc4_drm_driver;
	const struct vc4_mock_desc *desc = is_vc5 ? &vc5_mock : &vc4_mock;
	struct vc4_dev *vc4;
	struct device *dev;
	int ret;

	dev = drm_kunit_helper_alloc_device(test);
	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);

	vc4 = drm_kunit_helper_alloc_drm_device_with_driver(test, dev,
							    struct vc4_dev, base,
							    drv);
	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);

	vc4->dev = dev;
	vc4->is_vc5 = is_vc5;

	vc4->hvs = __vc4_hvs_alloc(vc4, NULL);
	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4->hvs);

	drm = &vc4->base;
	ret = __build_mock(test, drm, desc);
	KUNIT_ASSERT_EQ(test, ret, 0);

	ret = vc4_kms_load(drm);
	KUNIT_ASSERT_EQ(test, ret, 0);

	ret = drm_dev_register(drm, 0);
	KUNIT_ASSERT_EQ(test, ret, 0);

	return vc4;
}

struct vc4_dev *vc4_mock_device(struct kunit *test)
{
	return __mock_device(test, false);
}

struct vc4_dev *vc5_mock_device(struct kunit *test)
{
	return __mock_device(test, true);
}
+60 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */

#ifndef VC4_MOCK_H_
#define VC4_MOCK_H_

#include "../vc4_drv.h"

static inline
struct drm_crtc *vc4_find_crtc_for_encoder(struct kunit *test,
					   struct drm_device *drm,
					   struct drm_encoder *encoder)
{
	struct drm_crtc *crtc;

	KUNIT_ASSERT_EQ(test, hweight32(encoder->possible_crtcs), 1);

	drm_for_each_crtc(crtc, drm)
		if (encoder->possible_crtcs & drm_crtc_mask(crtc))
			return crtc;

	return NULL;
}

struct vc4_dummy_plane {
	struct vc4_plane plane;
};

struct vc4_dummy_plane *vc4_dummy_plane(struct kunit *test,
					struct drm_device *drm,
					enum drm_plane_type type);

struct vc4_dummy_crtc {
	struct vc4_crtc crtc;
};

struct vc4_dummy_crtc *vc4_mock_pv(struct kunit *test,
				   struct drm_device *drm,
				   struct drm_plane *plane,
				   const struct vc4_crtc_data *data);

struct vc4_dummy_output {
	struct vc4_encoder encoder;
	struct drm_connector connector;
};

struct vc4_dummy_output *vc4_dummy_output(struct kunit *test,
					  struct drm_device *drm,
					  struct drm_crtc *crtc,
					  enum vc4_encoder_type vc4_encoder_type,
					  unsigned int kms_encoder_type,
					  unsigned int connector_type);

struct vc4_dev *vc4_mock_device(struct kunit *test);
struct vc4_dev *vc5_mock_device(struct kunit *test);

int vc4_mock_atomic_add_output(struct kunit *test, struct drm_device *drm,
			       enum vc4_encoder_type type,
			       struct drm_atomic_state *state);

#endif // VC4_MOCK_H_
Loading