Hi,
This series introduce Kunit tests to the vc4 KMS driver, but unlike what we have been doing so far in KMS, it actually tests the atomic modesetting code.
In order to do so, I've had to improve a fair bit on the Kunit helpers already found in the tree in order to register a full blown and somewhat functional KMS driver.
It's of course relying on a mock so that we can test it anywhere. The mocking approach created a number of issues, the main one being that we need to create a decent mock in the first place, see patch 22. The basic idea is that I created some structures to provide a decent approximation of the actual hardware, and that would support both major architectures supported by vc4.
This is of course meant to evolve over time and support more tests, but I've focused on testing the HVS FIFO assignment code which is fairly tricky (and the tests have actually revealed one more bug with our current implementation). I used to have a userspace implementation of those tests, where I would copy and paste the kernel code and run the tests on a regular basis. It's was obviously fairly suboptimal, so it seemed like the perfect testbed for that series.
It can be run using: ./tools/testing/kunit/kunit.py run \ --kunitconfig=drivers/gpu/drm/vc4/tests/.kunitconfig \ --cross_compile aarch64-linux-gnu- --arch arm64
Let me know what you think, Maxime
To: David Airlie airlied@gmail.com To: Daniel Vetter daniel@ffwll.ch To: Maarten Lankhorst maarten.lankhorst@linux.intel.com To: Maxime Ripard mripard@kernel.org To: Thomas Zimmermann tzimmermann@suse.de Cc: Dave Stevenson dave.stevenson@raspberrypi.com Cc: Javier Martinez Canillas javierm@redhat.com Cc: Greg Kroah-Hartman gregkh@linuxfoundation.org Cc: Maíra Canal mairacanal@riseup.net Cc: Brendan Higgins brendan.higgins@linux.dev Cc: David Gow davidgow@google.com Cc: linux-kselftest@vger.kernel.org Cc: kunit-dev@googlegroups.com Cc: dri-devel@lists.freedesktop.org Cc: linux-kernel@vger.kernel.org Cc: linux-media@vger.kernel.org Cc: linaro-mm-sig@lists.linaro.org Signed-off-by: Maxime Ripard maxime@cerno.tech
--- Changes in v3: - Add a Kconfig option for the KUnit helpers - Switch to EXPORT_SYMBOL_GPL for the helpers - Add some documentation on how to run the tests - Add some documentation for __vc4_crtc_init - Fix KUnit casing - Link to v2: https://lore.kernel.org/r/20221123-rpi-kunit-tests-v2-0-efe5ed518b63@cerno.t...
Changes in v2: - Added some documentation for public functions - Removed the fake device probe/remove workqueue - Made sure the tests could be compiled as modules - Moved the vc4 tests in the vc4 module - Applied some of the preliminary patches - Rebased on top of current drm-misc-next branch - Fixed checkpatch issues - Introduced BCM2835 (Pi0-3) tests for muxing - Introduced tests to cover past bugs we had - Link to v1: https://lore.kernel.org/r/20221123-rpi-kunit-tests-v1-0-051a0bb60a16@cerno.t...
--- Maxime Ripard (20): drm/tests: helpers: Move the helper header to include/drm drm/tests: Introduce a config option for the KUnit helpers drm/tests: helpers: Document drm_kunit_device_init() drm/tests: helpers: Switch to EXPORT_SYMBOL_GPL drm/tests: helpers: Rename the device init helper drm/tests: helpers: Remove the name parameter drm/tests: helpers: Create the device in another function drm/tests: helpers: Switch to a platform_device drm/tests: helpers: Make sure the device is bound drm/tests: helpers: Allow for a custom device struct to be allocated drm/tests: helpers: Allow to pass a custom drm_driver drm/tests: Add a test for DRM managed actions drm/vc4: Move HVS state to main header drm/vc4: crtc: Introduce a lower-level crtc init helper drm/vc4: crtc: Make encoder lookup helper public drm/vc4: hvs: Provide a function to initialize the HVS structure drm/vc4: tests: Introduce a mocking infrastructure drm/vc4: tests: Fail the current test if we access a register drm/vc4: tests: Add unit test suite for the PV muxing Documentation: gpu: vc4: Add KUnit Tests Section
Documentation/gpu/vc4.rst | 16 + drivers/gpu/drm/Kconfig | 7 + drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/tests/Makefile | 5 +- drivers/gpu/drm/tests/drm_client_modeset_test.c | 19 +- drivers/gpu/drm/tests/drm_kunit_helpers.c | 106 ++- drivers/gpu/drm/tests/drm_kunit_helpers.h | 11 - drivers/gpu/drm/tests/drm_managed_test.c | 71 ++ drivers/gpu/drm/tests/drm_modes_test.c | 19 +- drivers/gpu/drm/tests/drm_probe_helper_test.c | 20 +- drivers/gpu/drm/vc4/Kconfig | 16 + drivers/gpu/drm/vc4/Makefile | 7 + drivers/gpu/drm/vc4/tests/.kunitconfig | 13 + drivers/gpu/drm/vc4/tests/vc4_mock.c | 200 +++++ drivers/gpu/drm/vc4/tests/vc4_mock.h | 63 ++ drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c | 41 + drivers/gpu/drm/vc4/tests/vc4_mock_output.c | 138 +++ drivers/gpu/drm/vc4/tests/vc4_mock_plane.c | 47 + drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c | 1039 +++++++++++++++++++++++ drivers/gpu/drm/vc4/vc4_crtc.c | 120 ++- drivers/gpu/drm/vc4/vc4_dpi.c | 13 +- drivers/gpu/drm/vc4/vc4_drv.c | 4 +- drivers/gpu/drm/vc4/vc4_drv.h | 91 +- drivers/gpu/drm/vc4/vc4_dsi.c | 9 +- drivers/gpu/drm/vc4/vc4_hdmi_regs.h | 4 + drivers/gpu/drm/vc4/vc4_hvs.c | 81 +- drivers/gpu/drm/vc4/vc4_kms.c | 25 +- drivers/gpu/drm/vc4/vc4_txp.c | 15 +- drivers/gpu/drm/vc4/vc4_vec.c | 13 +- include/drm/drm_kunit_helpers.h | 91 ++ 30 files changed, 2132 insertions(+), 174 deletions(-) --- base-commit: 199557fab92548f8e9d5207e385097213abe0cab change-id: 20221123-rpi-kunit-tests-87a388492a73
Best regards,
We'll need to use those helpers from drivers too, so let's move it to a more visible location.
Reviewed-by: Javier Martinez Canillas javierm@redhat.com Signed-off-by: Maxime Ripard maxime@cerno.tech --- drivers/gpu/drm/tests/drm_client_modeset_test.c | 3 +-- drivers/gpu/drm/tests/drm_kunit_helpers.c | 3 +-- drivers/gpu/drm/tests/drm_modes_test.c | 3 +-- drivers/gpu/drm/tests/drm_probe_helper_test.c | 3 +-- {drivers/gpu/drm/tests => include/drm}/drm_kunit_helpers.h | 0 5 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c b/drivers/gpu/drm/tests/drm_client_modeset_test.c index 52929536a158..ed2f62e92fea 100644 --- a/drivers/gpu/drm/tests/drm_client_modeset_test.c +++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c @@ -8,12 +8,11 @@ #include <drm/drm_connector.h> #include <drm/drm_edid.h> #include <drm/drm_drv.h> +#include <drm/drm_kunit_helpers.h> #include <drm/drm_modes.h> #include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_probe_helper.h>
-#include "drm_kunit_helpers.h" - struct drm_client_modeset_test_priv { struct drm_device *drm; struct drm_connector connector; diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index 8c738384a992..6600a4db3158 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -1,14 +1,13 @@ // SPDX-License-Identifier: GPL-2.0
#include <drm/drm_drv.h> +#include <drm/drm_kunit_helpers.h> #include <drm/drm_managed.h>
#include <kunit/resource.h>
#include <linux/device.h>
-#include "drm_kunit_helpers.h" - struct kunit_dev { struct drm_device base; }; diff --git a/drivers/gpu/drm/tests/drm_modes_test.c b/drivers/gpu/drm/tests/drm_modes_test.c index 9358a885c58b..3953e478c4d0 100644 --- a/drivers/gpu/drm/tests/drm_modes_test.c +++ b/drivers/gpu/drm/tests/drm_modes_test.c @@ -4,14 +4,13 @@ */
#include <drm/drm_drv.h> +#include <drm/drm_kunit_helpers.h> #include <drm/drm_modes.h>
#include <kunit/test.h>
#include <linux/units.h>
-#include "drm_kunit_helpers.h" - struct drm_test_modes_priv { struct drm_device *drm; }; diff --git a/drivers/gpu/drm/tests/drm_probe_helper_test.c b/drivers/gpu/drm/tests/drm_probe_helper_test.c index 7e938258c742..1f3941c150ae 100644 --- a/drivers/gpu/drm/tests/drm_probe_helper_test.c +++ b/drivers/gpu/drm/tests/drm_probe_helper_test.c @@ -7,6 +7,7 @@ #include <drm/drm_connector.h> #include <drm/drm_device.h> #include <drm/drm_drv.h> +#include <drm/drm_kunit_helpers.h> #include <drm/drm_mode.h> #include <drm/drm_modes.h> #include <drm/drm_modeset_helper_vtables.h> @@ -14,8 +15,6 @@
#include <kunit/test.h>
-#include "drm_kunit_helpers.h" - struct drm_probe_helper_test_priv { struct drm_device *drm; struct drm_connector connector; diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h similarity index 100% rename from drivers/gpu/drm/tests/drm_kunit_helpers.h rename to include/drm/drm_kunit_helpers.h
Driver-specific tests will need access to the helpers without pulling every DRM framework test. Let's create an intermediate Kconfig options for the helpers.
Suggested-by: Maíra Canal mcanal@igalia.com Signed-off-by: Maxime Ripard maxime@cerno.tech --- drivers/gpu/drm/Kconfig | 7 +++++++ drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/tests/Makefile | 4 +++- 3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 315cbdf61979..9f019cd053e1 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -63,6 +63,12 @@ config DRM_USE_DYNAMIC_DEBUG bytes per callsite, the .data costs can be substantial, and are therefore configurable.
+config DRM_KUNIT_TEST_HELPERS + tristate + depends on DRM && KUNIT + help + KUnit Helpers for KMS drivers. + config DRM_KUNIT_TEST tristate "KUnit tests for DRM" if !KUNIT_ALL_TESTS depends on DRM && KUNIT @@ -73,6 +79,7 @@ config DRM_KUNIT_TEST select DRM_KMS_HELPER select DRM_BUDDY select DRM_EXPORT_FOR_TESTS if m + select DRM_KUNIT_TEST_HELPERS default KUNIT_ALL_TESTS help This builds unit tests for DRM. This option is not useful for diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index f92cd7892711..8d61fbdfdfac 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -125,7 +125,7 @@ obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o # Drivers and the rest #
-obj-$(CONFIG_DRM_KUNIT_TEST) += tests/ +obj-y += tests/
obj-$(CONFIG_DRM_MIPI_DBI) += drm_mipi_dbi.o obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index 94fe546d937d..ef14bd481139 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -1,5 +1,8 @@ # SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_DRM_KUNIT_TEST_HELPERS) += \ + drm_kunit_helpers.o + obj-$(CONFIG_DRM_KUNIT_TEST) += \ drm_buddy_test.o \ drm_cmdline_parser_test.o \ @@ -9,7 +12,6 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \ drm_format_helper_test.o \ drm_format_test.o \ drm_framebuffer_test.o \ - drm_kunit_helpers.o \ drm_mm_test.o \ drm_modes_test.o \ drm_plane_helper_test.o \
On 12/1/22 12:11, Maxime Ripard wrote:
Driver-specific tests will need access to the helpers without pulling every DRM framework test. Let's create an intermediate Kconfig options for the helpers.
Suggested-by: Maíra Canal mcanal@igalia.com Signed-off-by: Maxime Ripard maxime@cerno.tech
Reviewed-by: Maíra Canal mcanal@igalia.com
Best Regards, - Maíra Canal
drivers/gpu/drm/Kconfig | 7 +++++++ drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/tests/Makefile | 4 +++- 3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 315cbdf61979..9f019cd053e1 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -63,6 +63,12 @@ config DRM_USE_DYNAMIC_DEBUG bytes per callsite, the .data costs can be substantial, and are therefore configurable. +config DRM_KUNIT_TEST_HELPERS
- tristate
- depends on DRM && KUNIT
- help
KUnit Helpers for KMS drivers.
- config DRM_KUNIT_TEST tristate "KUnit tests for DRM" if !KUNIT_ALL_TESTS depends on DRM && KUNIT
@@ -73,6 +79,7 @@ config DRM_KUNIT_TEST select DRM_KMS_HELPER select DRM_BUDDY select DRM_EXPORT_FOR_TESTS if m
- select DRM_KUNIT_TEST_HELPERS default KUNIT_ALL_TESTS help This builds unit tests for DRM. This option is not useful for
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index f92cd7892711..8d61fbdfdfac 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -125,7 +125,7 @@ obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o # Drivers and the rest # -obj-$(CONFIG_DRM_KUNIT_TEST) += tests/ +obj-y += tests/ obj-$(CONFIG_DRM_MIPI_DBI) += drm_mipi_dbi.o obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index 94fe546d937d..ef14bd481139 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -1,5 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_DRM_KUNIT_TEST_HELPERS) += \
- drm_kunit_helpers.o
- obj-$(CONFIG_DRM_KUNIT_TEST) += \ drm_buddy_test.o \ drm_cmdline_parser_test.o \
@@ -9,7 +12,6 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \ drm_format_helper_test.o \ drm_format_test.o \ drm_framebuffer_test.o \
- drm_kunit_helpers.o \ drm_mm_test.o \ drm_modes_test.o \ drm_plane_helper_test.o \
Commit 44a3928324e9 ("drm/tests: Add Kunit Helpers") introduced the drm_kunit_device_init() function but didn't document it properly. Add that documentation.
Reviewed-by: Maíra Canal mcanal@igalia.com Signed-off-by: Maxime Ripard maxime@cerno.tech --- drivers/gpu/drm/tests/drm_kunit_helpers.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index 6600a4db3158..9ed3cfc2ac03 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -35,6 +35,23 @@ static void dev_free(struct kunit_resource *res) root_device_unregister(dev); }
+/** + * drm_kunit_device_init - Allocates a mock DRM device for KUnit tests + * @test: The test context object + * @features: Mocked DRM device driver features + * @name: Name of the struct &device to allocate + * + * This function allocates a new struct &device, creates a struct + * &drm_driver and will create a struct &drm_device using both. + * + * The device and driver are tied to the @test context and will get + * cleaned at the end of the test. The drm_device is allocated through + * devm_drm_dev_alloc() and will thus be freed through a device-managed + * resource. + * + * Returns: + * A pointer to the new drm_device, or an ERR_PTR() otherwise. + */ struct drm_device *drm_kunit_device_init(struct kunit *test, u32 features, char *name) { struct kunit_dev *kdev;
drm_kunit_device_init() among other things will allocate a device and wrap around root_device_register. This function is exported with EXPORT_SYMBOL_GPL, so we can't really change the license.
Fixes: 199557fab925 ("drm/tests: helpers: Add missing export") Suggested-by: Javier Martinez Canillas javierm@redhat.com Signed-off-by: Maxime Ripard maxime@cerno.tech --- drivers/gpu/drm/tests/drm_kunit_helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index 9ed3cfc2ac03..4fe131141718 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -82,7 +82,7 @@ struct drm_device *drm_kunit_device_init(struct kunit *test, u32 features, char
return drm; } -EXPORT_SYMBOL(drm_kunit_device_init); +EXPORT_SYMBOL_GPL(drm_kunit_device_init);
MODULE_AUTHOR("Maxime Ripard maxime@cerno.tech"); MODULE_LICENSE("GPL");
On 12/1/22 12:11, Maxime Ripard wrote:
drm_kunit_device_init() among other things will allocate a device and wrap around root_device_register. This function is exported with EXPORT_SYMBOL_GPL, so we can't really change the license.
Fixes: 199557fab925 ("drm/tests: helpers: Add missing export") Suggested-by: Javier Martinez Canillas javierm@redhat.com Signed-off-by: Maxime Ripard maxime@cerno.tech
Reviewed-by: Maíra Canal mcanal@igalia.com
Best Regards, - Maíra Canal
drivers/gpu/drm/tests/drm_kunit_helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index 9ed3cfc2ac03..4fe131141718 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -82,7 +82,7 @@ struct drm_device *drm_kunit_device_init(struct kunit *test, u32 features, char return drm; } -EXPORT_SYMBOL(drm_kunit_device_init); +EXPORT_SYMBOL_GPL(drm_kunit_device_init); MODULE_AUTHOR("Maxime Ripard maxime@cerno.tech"); MODULE_LICENSE("GPL");
The name doesn't really fit the conventions for the other helpers in DRM/KMS, so let's rename it to make it obvious that we allocate a new DRM device.
Reviewed-by: Maíra Canal mcanal@igalia.com Signed-off-by: Maxime Ripard maxime@cerno.tech --- drivers/gpu/drm/tests/drm_client_modeset_test.c | 3 ++- drivers/gpu/drm/tests/drm_kunit_helpers.c | 8 +++++--- drivers/gpu/drm/tests/drm_modes_test.c | 3 ++- drivers/gpu/drm/tests/drm_probe_helper_test.c | 5 +++-- include/drm/drm_kunit_helpers.h | 5 ++++- 5 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c b/drivers/gpu/drm/tests/drm_client_modeset_test.c index ed2f62e92fea..6cdf08f582ce 100644 --- a/drivers/gpu/drm/tests/drm_client_modeset_test.c +++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c @@ -59,7 +59,8 @@ static int drm_client_modeset_test_init(struct kunit *test)
test->priv = priv;
- priv->drm = drm_kunit_device_init(test, DRIVER_MODESET, "drm-client-modeset-test"); + priv->drm = drm_kunit_helper_alloc_drm_device(test, DRIVER_MODESET, + "drm-client-modeset-test"); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
ret = drmm_connector_init(priv->drm, &priv->connector, diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index 4fe131141718..e718073ba6e9 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -36,7 +36,7 @@ static void dev_free(struct kunit_resource *res) }
/** - * drm_kunit_device_init - Allocates a mock DRM device for KUnit tests + * drm_kunit_helper_alloc_drm_device - Allocates a mock DRM device for KUnit tests * @test: The test context object * @features: Mocked DRM device driver features * @name: Name of the struct &device to allocate @@ -52,7 +52,9 @@ static void dev_free(struct kunit_resource *res) * Returns: * A pointer to the new drm_device, or an ERR_PTR() otherwise. */ -struct drm_device *drm_kunit_device_init(struct kunit *test, u32 features, char *name) +struct drm_device * +drm_kunit_helper_alloc_drm_device(struct kunit *test, + u32 features, char *name) { struct kunit_dev *kdev; struct drm_device *drm; @@ -82,7 +84,7 @@ struct drm_device *drm_kunit_device_init(struct kunit *test, u32 features, char
return drm; } -EXPORT_SYMBOL_GPL(drm_kunit_device_init); +EXPORT_SYMBOL_GPL(drm_kunit_helper_alloc_drm_device);
MODULE_AUTHOR("Maxime Ripard maxime@cerno.tech"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tests/drm_modes_test.c b/drivers/gpu/drm/tests/drm_modes_test.c index 3953e478c4d0..6723089dff9f 100644 --- a/drivers/gpu/drm/tests/drm_modes_test.c +++ b/drivers/gpu/drm/tests/drm_modes_test.c @@ -22,7 +22,8 @@ static int drm_test_modes_init(struct kunit *test) priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, priv);
- priv->drm = drm_kunit_device_init(test, DRIVER_MODESET, "drm-modes-test"); + priv->drm = drm_kunit_helper_alloc_drm_device(test, DRIVER_MODESET, + "drm-modes-test"); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
test->priv = priv; diff --git a/drivers/gpu/drm/tests/drm_probe_helper_test.c b/drivers/gpu/drm/tests/drm_probe_helper_test.c index 1f3941c150ae..85236ff4744f 100644 --- a/drivers/gpu/drm/tests/drm_probe_helper_test.c +++ b/drivers/gpu/drm/tests/drm_probe_helper_test.c @@ -39,8 +39,9 @@ static int drm_probe_helper_test_init(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, priv); test->priv = priv;
- priv->drm = drm_kunit_device_init(test, DRIVER_MODESET | DRIVER_ATOMIC, - "drm-probe-helper-test"); + priv->drm = drm_kunit_helper_alloc_drm_device(test, + DRIVER_MODESET | DRIVER_ATOMIC, + "drm-probe-helper-test"); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
connector = &priv->connector; diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h index 20ab6eec4c89..e9870c7911fe 100644 --- a/include/drm/drm_kunit_helpers.h +++ b/include/drm/drm_kunit_helpers.h @@ -6,6 +6,9 @@ struct drm_device; struct kunit;
-struct drm_device *drm_kunit_device_init(struct kunit *test, u32 features, char *name); +struct drm_device * +drm_kunit_helper_alloc_drm_device(struct kunit *test, + u32 features, + char *name);
#endif // DRM_KUNIT_HELPERS_H_
The device name isn't really useful, we can just define it instead of exposing it in the API.
Reviewed-by: Maíra Canal mcanal@igalia.com Signed-off-by: Maxime Ripard maxime@cerno.tech --- drivers/gpu/drm/tests/drm_client_modeset_test.c | 3 +-- drivers/gpu/drm/tests/drm_kunit_helpers.c | 7 ++++--- drivers/gpu/drm/tests/drm_modes_test.c | 3 +-- drivers/gpu/drm/tests/drm_probe_helper_test.c | 3 +-- include/drm/drm_kunit_helpers.h | 3 +-- 5 files changed, 8 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c b/drivers/gpu/drm/tests/drm_client_modeset_test.c index 6cdf08f582ce..4d475ae6dbb6 100644 --- a/drivers/gpu/drm/tests/drm_client_modeset_test.c +++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c @@ -59,8 +59,7 @@ static int drm_client_modeset_test_init(struct kunit *test)
test->priv = priv;
- priv->drm = drm_kunit_helper_alloc_drm_device(test, DRIVER_MODESET, - "drm-client-modeset-test"); + priv->drm = drm_kunit_helper_alloc_drm_device(test, DRIVER_MODESET); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
ret = drmm_connector_init(priv->drm, &priv->connector, diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index e718073ba6e9..ec33fcbd092d 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -8,6 +8,8 @@
#include <linux/device.h>
+#define KUNIT_DEVICE_NAME "drm-kunit-mock-device" + struct kunit_dev { struct drm_device base; }; @@ -39,7 +41,6 @@ static void dev_free(struct kunit_resource *res) * drm_kunit_helper_alloc_drm_device - Allocates a mock DRM device for KUnit tests * @test: The test context object * @features: Mocked DRM device driver features - * @name: Name of the struct &device to allocate * * This function allocates a new struct &device, creates a struct * &drm_driver and will create a struct &drm_device using both. @@ -54,7 +55,7 @@ static void dev_free(struct kunit_resource *res) */ struct drm_device * drm_kunit_helper_alloc_drm_device(struct kunit *test, - u32 features, char *name) + u32 features) { struct kunit_dev *kdev; struct drm_device *drm; @@ -62,7 +63,7 @@ drm_kunit_helper_alloc_drm_device(struct kunit *test, struct device *dev; int ret;
- dev = kunit_alloc_resource(test, dev_init, dev_free, GFP_KERNEL, name); + dev = kunit_alloc_resource(test, dev_init, dev_free, GFP_KERNEL, KUNIT_DEVICE_NAME); if (!dev) return ERR_PTR(-ENOMEM);
diff --git a/drivers/gpu/drm/tests/drm_modes_test.c b/drivers/gpu/drm/tests/drm_modes_test.c index 6723089dff9f..35965ad86188 100644 --- a/drivers/gpu/drm/tests/drm_modes_test.c +++ b/drivers/gpu/drm/tests/drm_modes_test.c @@ -22,8 +22,7 @@ static int drm_test_modes_init(struct kunit *test) priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, priv);
- priv->drm = drm_kunit_helper_alloc_drm_device(test, DRIVER_MODESET, - "drm-modes-test"); + priv->drm = drm_kunit_helper_alloc_drm_device(test, DRIVER_MODESET); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
test->priv = priv; diff --git a/drivers/gpu/drm/tests/drm_probe_helper_test.c b/drivers/gpu/drm/tests/drm_probe_helper_test.c index 85236ff4744f..be61a92b79d2 100644 --- a/drivers/gpu/drm/tests/drm_probe_helper_test.c +++ b/drivers/gpu/drm/tests/drm_probe_helper_test.c @@ -40,8 +40,7 @@ static int drm_probe_helper_test_init(struct kunit *test) test->priv = priv;
priv->drm = drm_kunit_helper_alloc_drm_device(test, - DRIVER_MODESET | DRIVER_ATOMIC, - "drm-probe-helper-test"); + DRIVER_MODESET | DRIVER_ATOMIC); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
connector = &priv->connector; diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h index e9870c7911fe..6c12b1426ba0 100644 --- a/include/drm/drm_kunit_helpers.h +++ b/include/drm/drm_kunit_helpers.h @@ -8,7 +8,6 @@ struct kunit;
struct drm_device * drm_kunit_helper_alloc_drm_device(struct kunit *test, - u32 features, - char *name); + u32 features);
#endif // DRM_KUNIT_HELPERS_H_
We'll need in some tests to control when the device needs to be added and removed, so let's split the device creation from the DRM device creation function.
Reviewed-by: Maíra Canal mcanal@igalia.com Signed-off-by: Maxime Ripard maxime@cerno.tech --- drivers/gpu/drm/tests/drm_client_modeset_test.c | 14 ++++++- drivers/gpu/drm/tests/drm_kunit_helpers.c | 56 +++++++++++++++---------- drivers/gpu/drm/tests/drm_modes_test.c | 15 ++++++- drivers/gpu/drm/tests/drm_probe_helper_test.c | 14 ++++++- include/drm/drm_kunit_helpers.h | 5 ++- 5 files changed, 77 insertions(+), 27 deletions(-)
diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c b/drivers/gpu/drm/tests/drm_client_modeset_test.c index 4d475ae6dbb6..053dbc0106d9 100644 --- a/drivers/gpu/drm/tests/drm_client_modeset_test.c +++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c @@ -15,6 +15,7 @@
struct drm_client_modeset_test_priv { struct drm_device *drm; + struct device *dev; struct drm_connector connector; };
@@ -59,7 +60,10 @@ static int drm_client_modeset_test_init(struct kunit *test)
test->priv = priv;
- priv->drm = drm_kunit_helper_alloc_drm_device(test, DRIVER_MODESET); + priv->dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev); + + priv->drm = drm_kunit_helper_alloc_drm_device(test, priv->dev, DRIVER_MODESET); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
ret = drmm_connector_init(priv->drm, &priv->connector, @@ -76,6 +80,13 @@ static int drm_client_modeset_test_init(struct kunit *test) return 0; }
+static void drm_client_modeset_test_exit(struct kunit *test) +{ + struct drm_client_modeset_test_priv *priv = test->priv; + + drm_kunit_helper_free_device(test, priv->dev); +} + static void drm_test_pick_cmdline_res_1920_1080_60(struct kunit *test) { struct drm_client_modeset_test_priv *priv = test->priv; @@ -175,6 +186,7 @@ static struct kunit_case drm_test_pick_cmdline_tests[] = { static struct kunit_suite drm_test_pick_cmdline_test_suite = { .name = "drm_test_pick_cmdline", .init = drm_client_modeset_test_init, + .exit = drm_client_modeset_test_exit, .test_cases = drm_test_pick_cmdline_tests };
diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index ec33fcbd092d..4bf98bd0a8c6 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -17,36 +17,51 @@ struct kunit_dev { static const struct drm_mode_config_funcs drm_mode_config_funcs = { };
-static int dev_init(struct kunit_resource *res, void *ptr) +/** + * drm_kunit_helper_alloc_device - Allocate a mock device for a KUnit test + * @test: The test context object + * + * This allocates a fake struct &device to create a mock for a KUnit + * test. + * + * Callers need to make sure drm_kunit_helper_free_device() on the + * device when done. + * + * Returns: + * A pointer to the new device, or an ERR_PTR() otherwise. + */ +struct device *drm_kunit_helper_alloc_device(struct kunit *test) { - char *name = ptr; - struct device *dev; - - dev = root_device_register(name); - if (IS_ERR(dev)) - return PTR_ERR(dev); - - res->data = dev; - return 0; + return root_device_register(KUNIT_DEVICE_NAME); } +EXPORT_SYMBOL_GPL(drm_kunit_helper_alloc_device);
-static void dev_free(struct kunit_resource *res) +/** + * drm_kunit_helper_free_device - Frees a mock device + * @test: The test context object + * @dev: The device to free + * + * Frees a device allocated with drm_kunit_helper_alloc_device(). + */ +void drm_kunit_helper_free_device(struct kunit *test, struct device *dev) { - struct device *dev = res->data; - root_device_unregister(dev); } +EXPORT_SYMBOL_GPL(drm_kunit_helper_free_device);
/** * drm_kunit_helper_alloc_drm_device - Allocates a mock DRM device for KUnit tests * @test: The test context object + * @dev: The parent device object * @features: Mocked DRM device driver features * - * This function allocates a new struct &device, creates a struct - * &drm_driver and will create a struct &drm_device using both. + * This function creates a struct &drm_driver and will create a struct + * &drm_device from @dev and that driver. * - * The device and driver are tied to the @test context and will get - * cleaned at the end of the test. The drm_device is allocated through + * @dev should be allocated using drm_kunit_helper_alloc_device(). + * + * The driver is tied to the @test context and will get cleaned at the + * end of the test. The drm_device is allocated through * devm_drm_dev_alloc() and will thus be freed through a device-managed * resource. * @@ -54,19 +69,14 @@ static void dev_free(struct kunit_resource *res) * A pointer to the new drm_device, or an ERR_PTR() otherwise. */ struct drm_device * -drm_kunit_helper_alloc_drm_device(struct kunit *test, +drm_kunit_helper_alloc_drm_device(struct kunit *test, struct device *dev, u32 features) { struct kunit_dev *kdev; struct drm_device *drm; struct drm_driver *driver; - struct device *dev; int ret;
- dev = kunit_alloc_resource(test, dev_init, dev_free, GFP_KERNEL, KUNIT_DEVICE_NAME); - if (!dev) - return ERR_PTR(-ENOMEM); - driver = kunit_kzalloc(test, sizeof(*driver), GFP_KERNEL); if (!driver) return ERR_PTR(-ENOMEM); diff --git a/drivers/gpu/drm/tests/drm_modes_test.c b/drivers/gpu/drm/tests/drm_modes_test.c index 35965ad86188..d1e9f3c0433a 100644 --- a/drivers/gpu/drm/tests/drm_modes_test.c +++ b/drivers/gpu/drm/tests/drm_modes_test.c @@ -13,6 +13,7 @@
struct drm_test_modes_priv { struct drm_device *drm; + struct device *dev; };
static int drm_test_modes_init(struct kunit *test) @@ -22,7 +23,11 @@ static int drm_test_modes_init(struct kunit *test) priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, priv);
- priv->drm = drm_kunit_helper_alloc_drm_device(test, DRIVER_MODESET); + priv->dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev); + + priv->drm = drm_kunit_helper_alloc_drm_device(test, priv->dev, + DRIVER_MODESET); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
test->priv = priv; @@ -30,6 +35,13 @@ static int drm_test_modes_init(struct kunit *test) return 0; }
+static void drm_test_modes_exit(struct kunit *test) +{ + struct drm_test_modes_priv *priv = test->priv; + + drm_kunit_helper_free_device(test, priv->dev); +} + static void drm_test_modes_analog_tv_ntsc_480i(struct kunit *test) { struct drm_test_modes_priv *priv = test->priv; @@ -135,6 +147,7 @@ static struct kunit_case drm_modes_analog_tv_tests[] = { static struct kunit_suite drm_modes_analog_tv_test_suite = { .name = "drm_modes_analog_tv", .init = drm_test_modes_init, + .exit = drm_test_modes_exit, .test_cases = drm_modes_analog_tv_tests, };
diff --git a/drivers/gpu/drm/tests/drm_probe_helper_test.c b/drivers/gpu/drm/tests/drm_probe_helper_test.c index be61a92b79d2..63a3bd1a6e4d 100644 --- a/drivers/gpu/drm/tests/drm_probe_helper_test.c +++ b/drivers/gpu/drm/tests/drm_probe_helper_test.c @@ -17,6 +17,7 @@
struct drm_probe_helper_test_priv { struct drm_device *drm; + struct device *dev; struct drm_connector connector; };
@@ -39,7 +40,10 @@ static int drm_probe_helper_test_init(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, priv); test->priv = priv;
- priv->drm = drm_kunit_helper_alloc_drm_device(test, + priv->dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev); + + priv->drm = drm_kunit_helper_alloc_drm_device(test, priv->dev, DRIVER_MODESET | DRIVER_ATOMIC); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
@@ -55,6 +59,13 @@ static int drm_probe_helper_test_init(struct kunit *test) return 0; }
+static void drm_probe_helper_test_exit(struct kunit *test) +{ + struct drm_probe_helper_test_priv *priv = test->priv; + + drm_kunit_helper_free_device(test, priv->dev); +} + typedef struct drm_display_mode *(*expected_mode_func_t)(struct drm_device *);
struct drm_connector_helper_tv_get_modes_test { @@ -195,6 +206,7 @@ static struct kunit_case drm_test_connector_helper_tv_get_modes_tests[] = { static struct kunit_suite drm_test_connector_helper_tv_get_modes_suite = { .name = "drm_connector_helper_tv_get_modes", .init = drm_probe_helper_test_init, + .exit = drm_probe_helper_test_exit, .test_cases = drm_test_connector_helper_tv_get_modes_tests, };
diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h index 6c12b1426ba0..b4277fe92c38 100644 --- a/include/drm/drm_kunit_helpers.h +++ b/include/drm/drm_kunit_helpers.h @@ -6,8 +6,11 @@ struct drm_device; struct kunit;
+struct device *drm_kunit_helper_alloc_device(struct kunit *test); +void drm_kunit_helper_free_device(struct kunit *test, struct device *dev); + struct drm_device * -drm_kunit_helper_alloc_drm_device(struct kunit *test, +drm_kunit_helper_alloc_drm_device(struct kunit *test, struct device *dev, u32 features);
#endif // DRM_KUNIT_HELPERS_H_
The device managed resources are ran if the device has bus, which is not the case of a root_device.
Let's use a platform_device instead.
Reviewed-by: Javier Martinez Canillas javierm@redhat.com Reviewed-by: Maíra Canal mcanal@igalia.com Signed-off-by: Maxime Ripard maxime@cerno.tech --- drivers/gpu/drm/tests/drm_kunit_helpers.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index 4bf98bd0a8c6..b66ce779511b 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -7,6 +7,7 @@ #include <kunit/resource.h>
#include <linux/device.h> +#include <linux/platform_device.h>
#define KUNIT_DEVICE_NAME "drm-kunit-mock-device"
@@ -32,7 +33,16 @@ static const struct drm_mode_config_funcs drm_mode_config_funcs = { */ struct device *drm_kunit_helper_alloc_device(struct kunit *test) { - return root_device_register(KUNIT_DEVICE_NAME); + struct platform_device *pdev; + int ret; + + pdev = platform_device_alloc(KUNIT_DEVICE_NAME, PLATFORM_DEVID_NONE); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + ret = platform_device_add(pdev); + KUNIT_ASSERT_EQ(test, ret, 0); + + return &pdev->dev; } EXPORT_SYMBOL_GPL(drm_kunit_helper_alloc_device);
@@ -45,7 +55,9 @@ EXPORT_SYMBOL_GPL(drm_kunit_helper_alloc_device); */ void drm_kunit_helper_free_device(struct kunit *test, struct device *dev) { - root_device_unregister(dev); + struct platform_device *pdev = to_platform_device(dev); + + platform_device_unregister(pdev); } EXPORT_SYMBOL_GPL(drm_kunit_helper_free_device);
The device managed resources are freed when the device is detached, so it has to be bound in the first place.
Let's create a fake driver that we will bind to our fake device to benefit from the device managed cleanups in our tests.
Reviewed-by: Maíra Canal mcanal@igalia.com Signed-off-by: Maxime Ripard maxime@cerno.tech --- drivers/gpu/drm/tests/drm_kunit_helpers.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index b66ce779511b..9bfd3cb9cde1 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -18,12 +18,32 @@ struct kunit_dev { static const struct drm_mode_config_funcs drm_mode_config_funcs = { };
+static int fake_probe(struct platform_device *pdev) +{ + return 0; +} + +static int fake_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver fake_platform_driver = { + .probe = fake_probe, + .remove = fake_remove, + .driver = { + .name = KUNIT_DEVICE_NAME, + }, +}; + /** * drm_kunit_helper_alloc_device - Allocate a mock device for a KUnit test * @test: The test context object * * This allocates a fake struct &device to create a mock for a KUnit - * test. + * test. The device will also be bound to a fake driver. It will thus be + * able to leverage the usual infrastructure and most notably the + * device-managed resources just like a "real" device. * * Callers need to make sure drm_kunit_helper_free_device() on the * device when done. @@ -36,6 +56,9 @@ struct device *drm_kunit_helper_alloc_device(struct kunit *test) struct platform_device *pdev; int ret;
+ ret = platform_driver_register(&fake_platform_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + pdev = platform_device_alloc(KUNIT_DEVICE_NAME, PLATFORM_DEVID_NONE); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
@@ -58,6 +81,7 @@ void drm_kunit_helper_free_device(struct kunit *test, struct device *dev) struct platform_device *pdev = to_platform_device(dev);
platform_device_unregister(pdev); + platform_driver_unregister(&fake_platform_driver); } EXPORT_SYMBOL_GPL(drm_kunit_helper_free_device);
The current helper to allocate a DRM device doesn't allow for any subclassing by drivers, which is going to be troublesome as we work on getting some kunit testing on atomic modesetting code.
Let's use a similar pattern to the other allocation helpers by providing the structure size and offset as arguments.
Reviewed-by: Javier Martinez Canillas javierm@redhat.com Signed-off-by: Maxime Ripard maxime@cerno.tech --- drivers/gpu/drm/tests/drm_client_modeset_test.c | 4 ++- drivers/gpu/drm/tests/drm_kunit_helpers.c | 40 ++++++------------------- drivers/gpu/drm/tests/drm_modes_test.c | 5 ++-- drivers/gpu/drm/tests/drm_probe_helper_test.c | 5 ++-- include/drm/drm_kunit_helpers.h | 32 ++++++++++++++++++-- 5 files changed, 48 insertions(+), 38 deletions(-)
diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c b/drivers/gpu/drm/tests/drm_client_modeset_test.c index 053dbc0106d9..416a279b6dae 100644 --- a/drivers/gpu/drm/tests/drm_client_modeset_test.c +++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c @@ -63,7 +63,9 @@ static int drm_client_modeset_test_init(struct kunit *test) priv->dev = drm_kunit_helper_alloc_device(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev);
- priv->drm = drm_kunit_helper_alloc_drm_device(test, priv->dev, DRIVER_MODESET); + priv->drm = __drm_kunit_helper_alloc_drm_device(test, priv->dev, + sizeof(*priv->drm), 0, + DRIVER_MODESET); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
ret = drmm_connector_init(priv->drm, &priv->connector, diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index 9bfd3cb9cde1..b5485ab8fbf9 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -11,10 +11,6 @@
#define KUNIT_DEVICE_NAME "drm-kunit-mock-device"
-struct kunit_dev { - struct drm_device base; -}; - static const struct drm_mode_config_funcs drm_mode_config_funcs = { };
@@ -85,32 +81,14 @@ void drm_kunit_helper_free_device(struct kunit *test, struct device *dev) } EXPORT_SYMBOL_GPL(drm_kunit_helper_free_device);
-/** - * drm_kunit_helper_alloc_drm_device - Allocates a mock DRM device for KUnit tests - * @test: The test context object - * @dev: The parent device object - * @features: Mocked DRM device driver features - * - * This function creates a struct &drm_driver and will create a struct - * &drm_device from @dev and that driver. - * - * @dev should be allocated using drm_kunit_helper_alloc_device(). - * - * The driver is tied to the @test context and will get cleaned at the - * end of the test. The drm_device is allocated through - * devm_drm_dev_alloc() and will thus be freed through a device-managed - * resource. - * - * Returns: - * A pointer to the new drm_device, or an ERR_PTR() otherwise. - */ struct drm_device * -drm_kunit_helper_alloc_drm_device(struct kunit *test, struct device *dev, - u32 features) +__drm_kunit_helper_alloc_drm_device(struct kunit *test, struct device *dev, + size_t size, size_t offset, + u32 features) { - struct kunit_dev *kdev; struct drm_device *drm; struct drm_driver *driver; + void *container; int ret;
driver = kunit_kzalloc(test, sizeof(*driver), GFP_KERNEL); @@ -118,11 +96,11 @@ drm_kunit_helper_alloc_drm_device(struct kunit *test, struct device *dev, return ERR_PTR(-ENOMEM);
driver->driver_features = features; - kdev = devm_drm_dev_alloc(dev, driver, struct kunit_dev, base); - if (IS_ERR(kdev)) - return ERR_CAST(kdev); + container = __devm_drm_dev_alloc(dev, driver, size, offset); + if (IS_ERR(container)) + return ERR_CAST(container);
- drm = &kdev->base; + drm = container + offset; drm->mode_config.funcs = &drm_mode_config_funcs;
ret = drmm_mode_config_init(drm); @@ -131,7 +109,7 @@ drm_kunit_helper_alloc_drm_device(struct kunit *test, struct device *dev,
return drm; } -EXPORT_SYMBOL_GPL(drm_kunit_helper_alloc_drm_device); +EXPORT_SYMBOL_GPL(__drm_kunit_helper_alloc_drm_device);
MODULE_AUTHOR("Maxime Ripard maxime@cerno.tech"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tests/drm_modes_test.c b/drivers/gpu/drm/tests/drm_modes_test.c index d1e9f3c0433a..bc4aa2ce78be 100644 --- a/drivers/gpu/drm/tests/drm_modes_test.c +++ b/drivers/gpu/drm/tests/drm_modes_test.c @@ -26,8 +26,9 @@ static int drm_test_modes_init(struct kunit *test) priv->dev = drm_kunit_helper_alloc_device(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev);
- priv->drm = drm_kunit_helper_alloc_drm_device(test, priv->dev, - DRIVER_MODESET); + priv->drm = __drm_kunit_helper_alloc_drm_device(test, priv->dev, + sizeof(*priv->drm), 0, + DRIVER_MODESET); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
test->priv = priv; diff --git a/drivers/gpu/drm/tests/drm_probe_helper_test.c b/drivers/gpu/drm/tests/drm_probe_helper_test.c index 63a3bd1a6e4d..e5bd967fe8af 100644 --- a/drivers/gpu/drm/tests/drm_probe_helper_test.c +++ b/drivers/gpu/drm/tests/drm_probe_helper_test.c @@ -43,8 +43,9 @@ static int drm_probe_helper_test_init(struct kunit *test) priv->dev = drm_kunit_helper_alloc_device(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev);
- priv->drm = drm_kunit_helper_alloc_drm_device(test, priv->dev, - DRIVER_MODESET | DRIVER_ATOMIC); + priv->drm = __drm_kunit_helper_alloc_drm_device(test, priv->dev, + sizeof(*priv->drm), 0, + DRIVER_MODESET | DRIVER_ATOMIC); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
connector = &priv->connector; diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h index b4277fe92c38..df99fda95e89 100644 --- a/include/drm/drm_kunit_helpers.h +++ b/include/drm/drm_kunit_helpers.h @@ -10,7 +10,35 @@ struct device *drm_kunit_helper_alloc_device(struct kunit *test); void drm_kunit_helper_free_device(struct kunit *test, struct device *dev);
struct drm_device * -drm_kunit_helper_alloc_drm_device(struct kunit *test, struct device *dev, - u32 features); +__drm_kunit_helper_alloc_drm_device(struct kunit *test, struct device *dev, + size_t size, size_t offset, + u32 features); + +/** + * drm_kunit_helper_alloc_drm_device - Allocates a mock DRM device for KUnit tests + * @_test: The test context object + * @_dev: The parent device object + * @_type: the type of the struct which contains struct &drm_device + * @_member: the name of the &drm_device within @_type. + * @_features: Mocked DRM device driver features + * + * This function creates a struct &drm_driver and will create a struct + * &drm_device from @_dev and that driver. + * + * @_dev should be allocated using drm_kunit_helper_alloc_device(). + * + * The driver is tied to the @_test context and will get cleaned at the + * end of the test. The drm_device is allocated through + * devm_drm_dev_alloc() and will thus be freed through a device-managed + * resource. + * + * Returns: + * A pointer to the new drm_device, or an ERR_PTR() otherwise. + */ +#define drm_kunit_helper_alloc_drm_device(_test, _dev, _type, _member, _feat) \ + ((_type *)__drm_kunit_helper_alloc_drm_device(_test, _dev, \ + sizeof(_type), \ + offsetof(_type, _member), \ + _feat))
#endif // DRM_KUNIT_HELPERS_H_
Some tests will need to provide their own drm_driver instead of relying on the dumb one in the helpers, so let's create a helper that allows to do so.
Reviewed-by: Javier Martinez Canillas javierm@redhat.com Signed-off-by: Maxime Ripard maxime@cerno.tech --- drivers/gpu/drm/tests/drm_kunit_helpers.c | 15 +++------ include/drm/drm_kunit_helpers.h | 51 +++++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index b5485ab8fbf9..e98b4150f556 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -82,20 +82,15 @@ void drm_kunit_helper_free_device(struct kunit *test, struct device *dev) EXPORT_SYMBOL_GPL(drm_kunit_helper_free_device);
struct drm_device * -__drm_kunit_helper_alloc_drm_device(struct kunit *test, struct device *dev, - size_t size, size_t offset, - u32 features) +__drm_kunit_helper_alloc_drm_device_with_driver(struct kunit *test, + struct device *dev, + size_t size, size_t offset, + const struct drm_driver *driver) { struct drm_device *drm; - struct drm_driver *driver; void *container; int ret;
- driver = kunit_kzalloc(test, sizeof(*driver), GFP_KERNEL); - if (!driver) - return ERR_PTR(-ENOMEM); - - driver->driver_features = features; container = __devm_drm_dev_alloc(dev, driver, size, offset); if (IS_ERR(container)) return ERR_CAST(container); @@ -109,7 +104,7 @@ __drm_kunit_helper_alloc_drm_device(struct kunit *test, struct device *dev,
return drm; } -EXPORT_SYMBOL_GPL(__drm_kunit_helper_alloc_drm_device); +EXPORT_SYMBOL_GPL(__drm_kunit_helper_alloc_drm_device_with_driver);
MODULE_AUTHOR("Maxime Ripard maxime@cerno.tech"); MODULE_LICENSE("GPL"); diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h index df99fda95e89..ed013fdcc1ff 100644 --- a/include/drm/drm_kunit_helpers.h +++ b/include/drm/drm_kunit_helpers.h @@ -3,6 +3,8 @@ #ifndef DRM_KUNIT_HELPERS_H_ #define DRM_KUNIT_HELPERS_H_
+#include <kunit/test.h> + struct drm_device; struct kunit;
@@ -10,9 +12,54 @@ struct device *drm_kunit_helper_alloc_device(struct kunit *test); void drm_kunit_helper_free_device(struct kunit *test, struct device *dev);
struct drm_device * -__drm_kunit_helper_alloc_drm_device(struct kunit *test, struct device *dev, +__drm_kunit_helper_alloc_drm_device_with_driver(struct kunit *test, + struct device *dev, + size_t size, size_t offset, + const struct drm_driver *driver); + +/** + * drm_kunit_helper_alloc_drm_device_with_driver - Allocates a mock DRM device for KUnit tests + * @_test: The test context object + * @_dev: The parent device object + * @_type: the type of the struct which contains struct &drm_device + * @_member: the name of the &drm_device within @_type. + * @_drv: Mocked DRM device driver features + * + * This function creates a struct &drm_device from @_dev and @_drv. + * + * @_dev should be allocated using drm_kunit_helper_alloc_device(). + * + * The driver is tied to the @_test context and will get cleaned at the + * end of the test. The drm_device is allocated through + * devm_drm_dev_alloc() and will thus be freed through a device-managed + * resource. + * + * Returns: + * A pointer to the new drm_device, or an ERR_PTR() otherwise. + */ +#define drm_kunit_helper_alloc_drm_device_with_driver(_test, _dev, _type, _member, _drv) \ + ((_type *)__drm_kunit_helper_alloc_drm_device_with_driver(_test, _dev, \ + sizeof(_type), \ + offsetof(_type, _member), \ + _drv)) + +static inline struct drm_device * +__drm_kunit_helper_alloc_drm_device(struct kunit *test, + struct device *dev, size_t size, size_t offset, - u32 features); + u32 features) +{ + struct drm_driver *driver; + + driver = kunit_kzalloc(test, sizeof(*driver), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, driver); + + driver->driver_features = features; + + return __drm_kunit_helper_alloc_drm_device_with_driver(test, dev, + size, offset, + driver); +}
/** * drm_kunit_helper_alloc_drm_device - Allocates a mock DRM device for KUnit tests
DRM-managed actions are supposed to be ran whenever the device is released. Let's introduce a basic unit test to make sure it happens.
Reviewed-by: Javier Martinez Canillas javierm@redhat.com Reviewed-by: Maíra Canal mcanal@igalia.com Signed-off-by: Maxime Ripard maxime@cerno.tech --- drivers/gpu/drm/tests/Makefile | 1 + drivers/gpu/drm/tests/drm_managed_test.c | 71 ++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+)
diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index ef14bd481139..aaf357e29c65 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \ drm_format_helper_test.o \ drm_format_test.o \ drm_framebuffer_test.o \ + drm_managed_test.o \ drm_mm_test.o \ drm_modes_test.o \ drm_plane_helper_test.o \ diff --git a/drivers/gpu/drm/tests/drm_managed_test.c b/drivers/gpu/drm/tests/drm_managed_test.c new file mode 100644 index 000000000000..1652dca11d30 --- /dev/null +++ b/drivers/gpu/drm/tests/drm_managed_test.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <drm/drm_drv.h> +#include <drm/drm_kunit_helpers.h> +#include <drm/drm_managed.h> + +#include <kunit/resource.h> + +#include <linux/device.h> + +/* Ought to be enough for anybody */ +#define TEST_TIMEOUT_MS 100 + +struct managed_test_priv { + bool action_done; + wait_queue_head_t action_wq; +}; + +static void drm_action(struct drm_device *drm, void *ptr) +{ + struct managed_test_priv *priv = ptr; + + priv->action_done = true; + wake_up_interruptible(&priv->action_wq); +} + +static void drm_test_managed_run_action(struct kunit *test) +{ + struct managed_test_priv *priv; + struct drm_device *drm; + struct device *dev; + int ret; + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); + init_waitqueue_head(&priv->action_wq); + + dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(*drm), 0, DRIVER_MODESET); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); + + ret = drmm_add_action_or_reset(drm, drm_action, priv); + KUNIT_EXPECT_EQ(test, ret, 0); + + ret = drm_dev_register(drm, 0); + KUNIT_ASSERT_EQ(test, ret, 0); + + drm_dev_unregister(drm); + drm_kunit_helper_free_device(test, dev); + + ret = wait_event_interruptible_timeout(priv->action_wq, priv->action_done, + msecs_to_jiffies(TEST_TIMEOUT_MS)); + KUNIT_EXPECT_GT(test, ret, 0); +} + +static struct kunit_case drm_managed_tests[] = { + KUNIT_CASE(drm_test_managed_run_action), + {} +}; + +static struct kunit_suite drm_managed_test_suite = { + .name = "drm-test-managed", + .test_cases = drm_managed_tests +}; + +kunit_test_suite(drm_managed_test_suite); + +MODULE_AUTHOR("Maxime Ripard maxime@cerno.tech"); +MODULE_LICENSE("GPL");
In order to introduce unit tests for the HVS state computation, we'll need access to the vc4_hvs_state struct definition and its associated helpers.
Let's move them in our driver header.
Reviewed-by: Javier Martinez Canillas javierm@redhat.com Reviewed-by: Maíra Canal mcanal@igalia.com Signed-off-by: Maxime Ripard maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_drv.h | 23 +++++++++++++++++++++++ drivers/gpu/drm/vc4/vc4_kms.c | 25 +++---------------------- 2 files changed, 26 insertions(+), 22 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 6af615c2eb65..051c2e3b6d43 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -355,6 +355,29 @@ struct vc4_hvs { bool vc5_hdmi_enable_4096by2160; };
+#define HVS_NUM_CHANNELS 3 + +struct vc4_hvs_state { + struct drm_private_state base; + unsigned long core_clock_rate; + + struct { + unsigned in_use: 1; + unsigned long fifo_load; + struct drm_crtc_commit *pending_commit; + } fifo_state[HVS_NUM_CHANNELS]; +}; + +static inline struct vc4_hvs_state * +to_vc4_hvs_state(const struct drm_private_state *priv) +{ + return container_of(priv, struct vc4_hvs_state, base); +} + +struct vc4_hvs_state *vc4_hvs_get_global_state(struct drm_atomic_state *state); +struct vc4_hvs_state *vc4_hvs_get_old_global_state(const struct drm_atomic_state *state); +struct vc4_hvs_state *vc4_hvs_get_new_global_state(const struct drm_atomic_state *state); + struct vc4_plane { struct drm_plane base; }; diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 7282545c54a1..53d9f30460cf 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -25,8 +25,6 @@ #include "vc4_drv.h" #include "vc4_regs.h"
-#define HVS_NUM_CHANNELS 3 - struct vc4_ctm_state { struct drm_private_state base; struct drm_color_ctm *ctm; @@ -39,23 +37,6 @@ to_vc4_ctm_state(const struct drm_private_state *priv) return container_of(priv, struct vc4_ctm_state, base); }
-struct vc4_hvs_state { - struct drm_private_state base; - unsigned long core_clock_rate; - - struct { - unsigned in_use: 1; - unsigned long fifo_load; - struct drm_crtc_commit *pending_commit; - } fifo_state[HVS_NUM_CHANNELS]; -}; - -static struct vc4_hvs_state * -to_vc4_hvs_state(const struct drm_private_state *priv) -{ - return container_of(priv, struct vc4_hvs_state, base); -} - struct vc4_load_tracker_state { struct drm_private_state base; u64 hvs_load; @@ -191,7 +172,7 @@ vc4_ctm_commit(struct vc4_dev *vc4, struct drm_atomic_state *state) VC4_SET_FIELD(ctm_state->fifo, SCALER_OLEDOFFS_DISPFIFO)); }
-static struct vc4_hvs_state * +struct vc4_hvs_state * vc4_hvs_get_new_global_state(const struct drm_atomic_state *state) { struct vc4_dev *vc4 = to_vc4_dev(state->dev); @@ -204,7 +185,7 @@ vc4_hvs_get_new_global_state(const struct drm_atomic_state *state) return to_vc4_hvs_state(priv_state); }
-static struct vc4_hvs_state * +struct vc4_hvs_state * vc4_hvs_get_old_global_state(const struct drm_atomic_state *state) { struct vc4_dev *vc4 = to_vc4_dev(state->dev); @@ -217,7 +198,7 @@ vc4_hvs_get_old_global_state(const struct drm_atomic_state *state) return to_vc4_hvs_state(priv_state); }
-static struct vc4_hvs_state * +struct vc4_hvs_state * vc4_hvs_get_global_state(struct drm_atomic_state *state) { struct vc4_dev *vc4 = to_vc4_dev(state->dev);
The current vc4_crtc_init() helper assumes that we will be using hardware planes and calls vc4_plane_init().
While it's a reasonable assumption, we'll want to mock the plane and thus provide our own. Let's create a helper that will take the plane as an argument.
Reviewed-by: Javier Martinez Canillas javierm@redhat.com Reviewed-by: Maíra Canal mcanal@igalia.com Signed-off-by: Maxime Ripard maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_crtc.c | 70 ++++++++++++++++++++++++++++++------------ drivers/gpu/drm/vc4/vc4_drv.h | 6 ++++ 2 files changed, 57 insertions(+), 19 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 333529ed3a0d..5f7f50add46e 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -1286,31 +1286,38 @@ static void vc4_set_crtc_possible_masks(struct drm_device *drm, } }
-int vc4_crtc_init(struct drm_device *drm, struct platform_device *pdev, - struct vc4_crtc *vc4_crtc, - const struct vc4_crtc_data *data, - const struct drm_crtc_funcs *crtc_funcs, - const struct drm_crtc_helper_funcs *crtc_helper_funcs, - bool feeds_txp) +/** + * __vc4_crtc_init - Initializes a CRTC + * @drm: DRM Device + * @pdev: CRTC Platform Device + * @vc4_crtc: CRTC Object to Initialize + * @data: Configuration data associated with this CRTC + * @primary_plane: Primary plane for CRTC + * @crtc_funcs: Callbacks for the new CRTC + * @crtc_helper_funcs: Helper Callbacks for the new CRTC + * @feeds_txp: Is this CRTC connected to the TXP? + * + * Initializes our private CRTC structure. This function is mostly + * relevant for KUnit testing, all other users should use + * vc4_crtc_init() instead. + * + * Returns: + * 0 on success, a negative error code on failure. + */ +int __vc4_crtc_init(struct drm_device *drm, + struct platform_device *pdev, + struct vc4_crtc *vc4_crtc, + const struct vc4_crtc_data *data, + struct drm_plane *primary_plane, + const struct drm_crtc_funcs *crtc_funcs, + const struct drm_crtc_helper_funcs *crtc_helper_funcs, + bool feeds_txp) { struct vc4_dev *vc4 = to_vc4_dev(drm); struct drm_crtc *crtc = &vc4_crtc->base; - struct drm_plane *primary_plane; unsigned int i; int ret;
- /* For now, we create just the primary and the legacy cursor - * planes. We should be able to stack more planes on easily, - * but to do that we would need to compute the bandwidth - * requirement of the plane configuration, and reject ones - * that will take too much. - */ - primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY, 0); - if (IS_ERR(primary_plane)) { - dev_err(drm->dev, "failed to construct primary plane\n"); - return PTR_ERR(primary_plane); - } - vc4_crtc->data = data; vc4_crtc->pdev = pdev; vc4_crtc->feeds_txp = feeds_txp; @@ -1342,6 +1349,31 @@ int vc4_crtc_init(struct drm_device *drm, struct platform_device *pdev, return 0; }
+int vc4_crtc_init(struct drm_device *drm, struct platform_device *pdev, + struct vc4_crtc *vc4_crtc, + const struct vc4_crtc_data *data, + const struct drm_crtc_funcs *crtc_funcs, + const struct drm_crtc_helper_funcs *crtc_helper_funcs, + bool feeds_txp) +{ + struct drm_plane *primary_plane; + + /* For now, we create just the primary and the legacy cursor + * planes. We should be able to stack more planes on easily, + * but to do that we would need to compute the bandwidth + * requirement of the plane configuration, and reject ones + * that will take too much. + */ + primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY, 0); + if (IS_ERR(primary_plane)) { + dev_err(drm->dev, "failed to construct primary plane\n"); + return PTR_ERR(primary_plane); + } + + return __vc4_crtc_init(drm, pdev, vc4_crtc, data, primary_plane, + crtc_funcs, crtc_helper_funcs, feeds_txp); +} + static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 051c2e3b6d43..cd2002fff115 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -888,6 +888,12 @@ int vc4_bo_debugfs_init(struct drm_minor *minor); /* vc4_crtc.c */ extern struct platform_driver vc4_crtc_driver; int vc4_crtc_disable_at_boot(struct drm_crtc *crtc); +int __vc4_crtc_init(struct drm_device *drm, struct platform_device *pdev, + struct vc4_crtc *vc4_crtc, const struct vc4_crtc_data *data, + struct drm_plane *primary_plane, + const struct drm_crtc_funcs *crtc_funcs, + const struct drm_crtc_helper_funcs *crtc_helper_funcs, + bool feeds_txp); int vc4_crtc_init(struct drm_device *drm, struct platform_device *pdev, struct vc4_crtc *vc4_crtc, const struct vc4_crtc_data *data, const struct drm_crtc_funcs *crtc_funcs,
We'll need a function that looks up an encoder by its vc4_encoder_type. Such a function is already present in the CRTC code, so let's make it public so that we can reuse it in the unit tests.
Reviewed-by: Javier Martinez Canillas javierm@redhat.com Reviewed-by: Maíra Canal mcanal@igalia.com Signed-off-by: Maxime Ripard maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_crtc.c | 17 +---------------- drivers/gpu/drm/vc4/vc4_drv.h | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 5f7f50add46e..195d2b61839d 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -486,21 +486,6 @@ static int vc4_crtc_disable(struct drm_crtc *crtc, return 0; }
-static struct drm_encoder *vc4_crtc_get_encoder_by_type(struct drm_crtc *crtc, - enum vc4_encoder_type type) -{ - struct drm_encoder *encoder; - - drm_for_each_encoder(encoder, crtc->dev) { - struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); - - if (vc4_encoder->type == type) - return encoder; - } - - return NULL; -} - int vc4_crtc_disable_at_boot(struct drm_crtc *crtc) { struct drm_device *drm = crtc->dev; @@ -536,7 +521,7 @@ int vc4_crtc_disable_at_boot(struct drm_crtc *crtc)
pv_data = vc4_crtc_to_vc4_pv_data(vc4_crtc); encoder_type = pv_data->encoder_types[encoder_sel]; - encoder = vc4_crtc_get_encoder_by_type(crtc, encoder_type); + encoder = vc4_find_encoder_by_type(drm, encoder_type); if (WARN_ON(!encoder)) return 0;
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index cd2002fff115..54352db48476 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -495,6 +495,22 @@ to_vc4_encoder(const struct drm_encoder *encoder) return container_of(encoder, struct vc4_encoder, base); }
+static inline +struct drm_encoder *vc4_find_encoder_by_type(struct drm_device *drm, + enum vc4_encoder_type type) +{ + struct drm_encoder *encoder; + + drm_for_each_encoder(encoder, drm) { + struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); + + if (vc4_encoder->type == type) + return encoder; + } + + return NULL; +} + struct vc4_crtc_data { const char *name;
We'll need to initialize the HVS structure without a backing device to create a mock we'll use for testing.
Split the structure initialization part into a separate function.
Reviewed-by: Javier Martinez Canillas javierm@redhat.com Signed-off-by: Maxime Ripard maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_drv.h | 1 + drivers/gpu/drm/vc4/vc4_hvs.c | 81 +++++++++++++++++++++++++------------------ 2 files changed, 48 insertions(+), 34 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 54352db48476..e0be7a81a24a 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -1009,6 +1009,7 @@ void vc4_irq_reset(struct drm_device *dev);
/* vc4_hvs.c */ extern struct platform_driver vc4_hvs_driver; +struct vc4_hvs *__vc4_hvs_alloc(struct vc4_dev *vc4, struct platform_device *pdev); void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int output); int vc4_hvs_get_fifo_from_output(struct vc4_hvs *hvs, unsigned int output); u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo); diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index c4453a5ae163..94c29f8547bb 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -768,22 +768,60 @@ int vc4_hvs_debugfs_init(struct drm_minor *minor) return 0; }
-static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) +struct vc4_hvs *__vc4_hvs_alloc(struct vc4_dev *vc4, struct platform_device *pdev) { - struct platform_device *pdev = to_platform_device(dev); - struct drm_device *drm = dev_get_drvdata(master); - struct vc4_dev *vc4 = to_vc4_dev(drm); - struct vc4_hvs *hvs = NULL; - int ret; - u32 dispctrl; - u32 reg; + struct drm_device *drm = &vc4->base; + struct vc4_hvs *hvs;
hvs = drmm_kzalloc(drm, sizeof(*hvs), GFP_KERNEL); if (!hvs) - return -ENOMEM; + return ERR_PTR(-ENOMEM); + hvs->vc4 = vc4; hvs->pdev = pdev;
+ spin_lock_init(&hvs->mm_lock); + + /* Set up the HVS display list memory manager. We never + * overwrite the setup from the bootloader (just 128b out of + * our 16K), since we don't want to scramble the screen when + * transitioning from the firmware's boot setup to runtime. + */ + drm_mm_init(&hvs->dlist_mm, + HVS_BOOTLOADER_DLIST_END, + (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END); + + /* Set up the HVS LBM memory manager. We could have some more + * complicated data structure that allowed reuse of LBM areas + * between planes when they don't overlap on the screen, but + * for now we just allocate globally. + */ + if (!vc4->is_vc5) + /* 48k words of 2x12-bit pixels */ + drm_mm_init(&hvs->lbm_mm, 0, 48 * 1024); + else + /* 60k words of 4x12-bit pixels */ + drm_mm_init(&hvs->lbm_mm, 0, 60 * 1024); + + vc4->hvs = hvs; + + return hvs; +} + +static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = to_vc4_dev(drm); + struct vc4_hvs *hvs = NULL; + int ret; + u32 dispctrl; + u32 reg; + + hvs = __vc4_hvs_alloc(vc4, NULL); + if (IS_ERR(hvs)) + return PTR_ERR(hvs); + hvs->regs = vc4_ioremap_regs(pdev, 0); if (IS_ERR(hvs->regs)) return PTR_ERR(hvs->regs); @@ -835,29 +873,6 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) else hvs->dlist = hvs->regs + SCALER5_DLIST_START;
- spin_lock_init(&hvs->mm_lock); - - /* Set up the HVS display list memory manager. We never - * overwrite the setup from the bootloader (just 128b out of - * our 16K), since we don't want to scramble the screen when - * transitioning from the firmware's boot setup to runtime. - */ - drm_mm_init(&hvs->dlist_mm, - HVS_BOOTLOADER_DLIST_END, - (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END); - - /* Set up the HVS LBM memory manager. We could have some more - * complicated data structure that allowed reuse of LBM areas - * between planes when they don't overlap on the screen, but - * for now we just allocate globally. - */ - if (!vc4->is_vc5) - /* 48k words of 2x12-bit pixels */ - drm_mm_init(&hvs->lbm_mm, 0, 48 * 1024); - else - /* 60k words of 4x12-bit pixels */ - drm_mm_init(&hvs->lbm_mm, 0, 60 * 1024); - /* Upload filter kernels. We only have the one for now, so we * keep it around for the lifetime of the driver. */ @@ -867,8 +882,6 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) if (ret) return ret;
- vc4->hvs = hvs; - reg = HVS_READ(SCALER_DISPECTRL); reg &= ~SCALER_DISPECTRL_DSP2_MUX_MASK; HVS_WRITE(SCALER_DISPECTRL,
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 Signed-off-by: Maxime Ripard maxime@cerno.tech --- drivers/gpu/drm/vc4/Kconfig | 16 +++ drivers/gpu/drm/vc4/Makefile | 6 + drivers/gpu/drm/vc4/tests/.kunitconfig | 13 ++ drivers/gpu/drm/vc4/tests/vc4_mock.c | 200 ++++++++++++++++++++++++++++ drivers/gpu/drm/vc4/tests/vc4_mock.h | 60 +++++++++ drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c | 41 ++++++ drivers/gpu/drm/vc4/tests/vc4_mock_output.c | 99 ++++++++++++++ drivers/gpu/drm/vc4/tests/vc4_mock_plane.c | 47 +++++++ drivers/gpu/drm/vc4/vc4_crtc.c | 20 +-- drivers/gpu/drm/vc4/vc4_drv.c | 4 +- drivers/gpu/drm/vc4/vc4_drv.h | 16 +++ drivers/gpu/drm/vc4/vc4_txp.c | 2 +- 12 files changed, 511 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig index 246305d17a52..f423941c028d 100644 --- a/drivers/gpu/drm/vc4/Kconfig +++ b/drivers/gpu/drm/vc4/Kconfig @@ -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". diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile index d0163e18e9ca..f974a8c80e2f 100644 --- a/drivers/gpu/drm/vc4/Makefile +++ b/drivers/gpu/drm/vc4/Makefile @@ -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 diff --git a/drivers/gpu/drm/vc4/tests/.kunitconfig b/drivers/gpu/drm/vc4/tests/.kunitconfig new file mode 100644 index 000000000000..b503e9036c7f --- /dev/null +++ b/drivers/gpu/drm/vc4/tests/.kunitconfig @@ -0,0 +1,13 @@ +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 diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock.c b/drivers/gpu/drm/vc4/tests/vc4_mock.c new file mode 100644 index 000000000000..a4bed26af32f --- /dev/null +++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c @@ -0,0 +1,200 @@ +// 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); +} diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock.h b/drivers/gpu/drm/vc4/tests/vc4_mock.h new file mode 100644 index 000000000000..ace5b2e00f4a --- /dev/null +++ b/drivers/gpu/drm/vc4/tests/vc4_mock.h @@ -0,0 +1,60 @@ +/* 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_ diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c b/drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c new file mode 100644 index 000000000000..5d12d7beef0e --- /dev/null +++ b/drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_modeset_helper_vtables.h> + +#include <kunit/test.h> + +#include "vc4_mock.h" + +static const struct drm_crtc_helper_funcs vc4_dummy_crtc_helper_funcs = { + .atomic_check = vc4_crtc_atomic_check, +}; + +static const struct drm_crtc_funcs vc4_dummy_crtc_funcs = { + .atomic_destroy_state = vc4_crtc_destroy_state, + .atomic_duplicate_state = vc4_crtc_duplicate_state, + .reset = vc4_crtc_reset, +}; + +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_crtc *dummy_crtc; + struct vc4_crtc *vc4_crtc; + int ret; + + dummy_crtc = kunit_kzalloc(test, sizeof(*dummy_crtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dummy_crtc); + + vc4_crtc = &dummy_crtc->crtc; + ret = __vc4_crtc_init(drm, NULL, + vc4_crtc, data, plane, + &vc4_dummy_crtc_funcs, + &vc4_dummy_crtc_helper_funcs, + false); + KUNIT_ASSERT_EQ(test, ret, 0); + + return dummy_crtc; +} diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock_output.c b/drivers/gpu/drm/vc4/tests/vc4_mock_output.c new file mode 100644 index 000000000000..cb16ab4451f7 --- /dev/null +++ b/drivers/gpu/drm/vc4/tests/vc4_mock_output.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_atomic_uapi.h> +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> +#include <drm/drm_encoder.h> +#include <drm/drm_modeset_helper_vtables.h> + +#include <kunit/test.h> + +#include "vc4_mock.h" + +static const struct drm_connector_helper_funcs vc4_dummy_connector_helper_funcs = { +}; + +static const struct drm_connector_funcs vc4_dummy_connector_funcs = { + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .reset = drm_atomic_helper_connector_reset, +}; + +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_dummy_output *dummy_output; + struct drm_connector *conn; + struct drm_encoder *enc; + int ret; + + dummy_output = kunit_kzalloc(test, sizeof(*dummy_output), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_output); + dummy_output->encoder.type = vc4_encoder_type; + + enc = &dummy_output->encoder.base; + ret = drmm_encoder_init(drm, enc, + NULL, + kms_encoder_type, + NULL); + KUNIT_ASSERT_EQ(test, ret, 0); + enc->possible_crtcs = drm_crtc_mask(crtc); + + conn = &dummy_output->connector; + ret = drmm_connector_init(drm, conn, + &vc4_dummy_connector_funcs, + connector_type, + NULL); + KUNIT_ASSERT_EQ(test, ret, 0); + + drm_connector_helper_add(conn, &vc4_dummy_connector_helper_funcs); + drm_connector_attach_encoder(conn, enc); + + return dummy_output; +} + +static const struct drm_display_mode default_mode = { + DRM_SIMPLE_MODE(640, 480, 64, 48) +}; + +int vc4_mock_atomic_add_output(struct kunit *test, struct drm_device *drm, + enum vc4_encoder_type type, + struct drm_atomic_state *state) +{ + struct vc4_dummy_output *output; + struct drm_connector *conn; + struct drm_connector_state *conn_state; + struct drm_encoder *encoder; + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + int ret; + + encoder = vc4_find_encoder_by_type(drm, type); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, encoder); + + crtc = vc4_find_crtc_for_encoder(test, drm, encoder); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc); + + output = container_of(encoder, struct vc4_dummy_output, encoder.base); + conn = &output->connector; + conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + ret = drm_atomic_set_crtc_for_connector(conn_state, crtc); + KUNIT_EXPECT_EQ(test, ret, 0); + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + + ret = drm_atomic_set_mode_for_crtc(crtc_state, &default_mode); + KUNIT_EXPECT_EQ(test, ret, 0); + + crtc_state->active = true; + + return 0; +} diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c new file mode 100644 index 000000000000..62b18f5f41db --- /dev/null +++ b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_plane.h> + +#include <kunit/test.h> + +#include "vc4_mock.h" + +static const struct drm_plane_helper_funcs vc4_dummy_plane_helper_funcs = { +}; + +static const struct drm_plane_funcs vc4_dummy_plane_funcs = { + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .reset = drm_atomic_helper_plane_reset, +}; + +static const uint32_t vc4_dummy_plane_formats[] = { + DRM_FORMAT_XRGB8888, +}; + +struct vc4_dummy_plane *vc4_dummy_plane(struct kunit *test, + struct drm_device *drm, + enum drm_plane_type type) +{ + struct vc4_dummy_plane *dummy_plane; + struct drm_plane *plane; + + dummy_plane = drmm_universal_plane_alloc(drm, + struct vc4_dummy_plane, plane.base, + 0, + &vc4_dummy_plane_funcs, + vc4_dummy_plane_formats, + ARRAY_SIZE(vc4_dummy_plane_formats), + NULL, + DRM_PLANE_TYPE_PRIMARY, + NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_plane); + + plane = &dummy_plane->plane.base; + drm_plane_helper_add(plane, &vc4_dummy_plane_helper_funcs); + + return dummy_plane; +} diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 195d2b61839d..7d1a696477ce 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -675,8 +675,8 @@ void vc4_crtc_get_margins(struct drm_crtc_state *state, } }
-static int vc4_crtc_atomic_check(struct drm_crtc *crtc, - struct drm_atomic_state *state) +int vc4_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_atomic_state *state) { struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); @@ -1116,7 +1116,7 @@ static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = { .get_scanout_position = vc4_crtc_get_scanout_position, };
-static const struct vc4_pv_data bcm2835_pv0_data = { +const struct vc4_pv_data bcm2835_pv0_data = { .base = { .name = "pixelvalve-0", .debugfs_name = "crtc0_regs", @@ -1131,7 +1131,7 @@ static const struct vc4_pv_data bcm2835_pv0_data = { }, };
-static const struct vc4_pv_data bcm2835_pv1_data = { +const struct vc4_pv_data bcm2835_pv1_data = { .base = { .name = "pixelvalve-1", .debugfs_name = "crtc1_regs", @@ -1146,7 +1146,7 @@ static const struct vc4_pv_data bcm2835_pv1_data = { }, };
-static const struct vc4_pv_data bcm2835_pv2_data = { +const struct vc4_pv_data bcm2835_pv2_data = { .base = { .name = "pixelvalve-2", .debugfs_name = "crtc2_regs", @@ -1161,7 +1161,7 @@ static const struct vc4_pv_data bcm2835_pv2_data = { }, };
-static const struct vc4_pv_data bcm2711_pv0_data = { +const struct vc4_pv_data bcm2711_pv0_data = { .base = { .name = "pixelvalve-0", .debugfs_name = "crtc0_regs", @@ -1176,7 +1176,7 @@ static const struct vc4_pv_data bcm2711_pv0_data = { }, };
-static const struct vc4_pv_data bcm2711_pv1_data = { +const struct vc4_pv_data bcm2711_pv1_data = { .base = { .name = "pixelvalve-1", .debugfs_name = "crtc1_regs", @@ -1191,7 +1191,7 @@ static const struct vc4_pv_data bcm2711_pv1_data = { }, };
-static const struct vc4_pv_data bcm2711_pv2_data = { +const struct vc4_pv_data bcm2711_pv2_data = { .base = { .name = "pixelvalve-2", .debugfs_name = "crtc2_regs", @@ -1205,7 +1205,7 @@ static const struct vc4_pv_data bcm2711_pv2_data = { }, };
-static const struct vc4_pv_data bcm2711_pv3_data = { +const struct vc4_pv_data bcm2711_pv3_data = { .base = { .name = "pixelvalve-3", .debugfs_name = "crtc3_regs", @@ -1219,7 +1219,7 @@ static const struct vc4_pv_data bcm2711_pv3_data = { }, };
-static const struct vc4_pv_data bcm2711_pv4_data = { +const struct vc4_pv_data bcm2711_pv4_data = { .base = { .name = "pixelvalve-4", .debugfs_name = "crtc4_regs", diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 5990d8f8c363..3b0ae2c3e33c 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -196,7 +196,7 @@ static const struct drm_ioctl_desc vc4_drm_ioctls[] = { DRM_IOCTL_DEF_DRV(VC4_PERFMON_GET_VALUES, vc4_perfmon_get_values_ioctl, DRM_RENDER_ALLOW), };
-static const struct drm_driver vc4_drm_driver = { +const struct drm_driver vc4_drm_driver = { .driver_features = (DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM | @@ -225,7 +225,7 @@ static const struct drm_driver vc4_drm_driver = { .patchlevel = DRIVER_PATCHLEVEL, };
-static const struct drm_driver vc5_drm_driver = { +const struct drm_driver vc5_drm_driver = { .driver_features = (DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM), diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index e0be7a81a24a..418f4f308e36 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -24,6 +24,9 @@ struct drm_device; struct drm_gem_object;
+extern const struct drm_driver vc4_drm_driver; +extern const struct drm_driver vc5_drm_driver; + /* Don't forget to update vc4_bo.c: bo_type_names[] when adding to * this. */ @@ -523,6 +526,8 @@ struct vc4_crtc_data { int hvs_output; };
+extern const struct vc4_crtc_data vc4_txp_crtc_data; + struct vc4_pv_data { struct vc4_crtc_data base;
@@ -535,6 +540,15 @@ struct vc4_pv_data { enum vc4_encoder_type encoder_types[4]; };
+extern const struct vc4_pv_data bcm2835_pv0_data; +extern const struct vc4_pv_data bcm2835_pv1_data; +extern const struct vc4_pv_data bcm2835_pv2_data; +extern const struct vc4_pv_data bcm2711_pv0_data; +extern const struct vc4_pv_data bcm2711_pv1_data; +extern const struct vc4_pv_data bcm2711_pv2_data; +extern const struct vc4_pv_data bcm2711_pv3_data; +extern const struct vc4_pv_data bcm2711_pv4_data; + struct vc4_crtc { struct drm_crtc base; struct platform_device *pdev; @@ -920,6 +934,8 @@ int vc4_page_flip(struct drm_crtc *crtc, struct drm_pending_vblank_event *event, uint32_t flags, struct drm_modeset_acquire_ctx *ctx); +int vc4_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_atomic_state *state); struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc); void vc4_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state); diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index 4f7ce5d3e8ad..2b69454b8534 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -479,7 +479,7 @@ static irqreturn_t vc4_txp_interrupt(int irq, void *data) return IRQ_HANDLED; }
-static const struct vc4_crtc_data vc4_txp_crtc_data = { +const struct vc4_crtc_data vc4_txp_crtc_data = { .name = "txp", .debugfs_name = "txp_regs", .hvs_available_channels = BIT(2),
On 12/1/22 12:11, Maxime Ripard wrote:
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 Signed-off-by: Maxime Ripard maxime@cerno.tech
Reviewed-by: Maíra Canal mcanal@igalia.com
But it would be nice if you could fix the checkpatch problems in the vc4/tests folder.
Best Regards, - Maíra Canal
drivers/gpu/drm/vc4/Kconfig | 16 +++ drivers/gpu/drm/vc4/Makefile | 6 + drivers/gpu/drm/vc4/tests/.kunitconfig | 13 ++ drivers/gpu/drm/vc4/tests/vc4_mock.c | 200 ++++++++++++++++++++++++++++ drivers/gpu/drm/vc4/tests/vc4_mock.h | 60 +++++++++ drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c | 41 ++++++ drivers/gpu/drm/vc4/tests/vc4_mock_output.c | 99 ++++++++++++++ drivers/gpu/drm/vc4/tests/vc4_mock_plane.c | 47 +++++++ drivers/gpu/drm/vc4/vc4_crtc.c | 20 +-- drivers/gpu/drm/vc4/vc4_drv.c | 4 +- drivers/gpu/drm/vc4/vc4_drv.h | 16 +++ drivers/gpu/drm/vc4/vc4_txp.c | 2 +- 12 files changed, 511 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig index 246305d17a52..f423941c028d 100644 --- a/drivers/gpu/drm/vc4/Kconfig +++ b/drivers/gpu/drm/vc4/Kconfig @@ -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".
diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile index d0163e18e9ca..f974a8c80e2f 100644 --- a/drivers/gpu/drm/vc4/Makefile +++ b/drivers/gpu/drm/vc4/Makefile @@ -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 diff --git a/drivers/gpu/drm/vc4/tests/.kunitconfig b/drivers/gpu/drm/vc4/tests/.kunitconfig new file mode 100644 index 000000000000..b503e9036c7f --- /dev/null +++ b/drivers/gpu/drm/vc4/tests/.kunitconfig @@ -0,0 +1,13 @@ +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 diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock.c b/drivers/gpu/drm/vc4/tests/vc4_mock.c new file mode 100644 index 000000000000..a4bed26af32f --- /dev/null +++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c @@ -0,0 +1,200 @@ +// 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);
+} diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock.h b/drivers/gpu/drm/vc4/tests/vc4_mock.h new file mode 100644 index 000000000000..ace5b2e00f4a --- /dev/null +++ b/drivers/gpu/drm/vc4/tests/vc4_mock.h @@ -0,0 +1,60 @@ +/* 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_ diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c b/drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c new file mode 100644 index 000000000000..5d12d7beef0e --- /dev/null +++ b/drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0
+#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_modeset_helper_vtables.h>
+#include <kunit/test.h>
+#include "vc4_mock.h"
+static const struct drm_crtc_helper_funcs vc4_dummy_crtc_helper_funcs = {
- .atomic_check = vc4_crtc_atomic_check,
+};
+static const struct drm_crtc_funcs vc4_dummy_crtc_funcs = {
- .atomic_destroy_state = vc4_crtc_destroy_state,
- .atomic_duplicate_state = vc4_crtc_duplicate_state,
- .reset = vc4_crtc_reset,
+};
+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_crtc *dummy_crtc;
- struct vc4_crtc *vc4_crtc;
- int ret;
- dummy_crtc = kunit_kzalloc(test, sizeof(*dummy_crtc), GFP_KERNEL);
- KUNIT_ASSERT_NOT_NULL(test, dummy_crtc);
- vc4_crtc = &dummy_crtc->crtc;
- ret = __vc4_crtc_init(drm, NULL,
vc4_crtc, data, plane,
&vc4_dummy_crtc_funcs,
&vc4_dummy_crtc_helper_funcs,
false);
- KUNIT_ASSERT_EQ(test, ret, 0);
- return dummy_crtc;
+} diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock_output.c b/drivers/gpu/drm/vc4/tests/vc4_mock_output.c new file mode 100644 index 000000000000..cb16ab4451f7 --- /dev/null +++ b/drivers/gpu/drm/vc4/tests/vc4_mock_output.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0
+#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_atomic_uapi.h> +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> +#include <drm/drm_encoder.h> +#include <drm/drm_modeset_helper_vtables.h>
+#include <kunit/test.h>
+#include "vc4_mock.h"
+static const struct drm_connector_helper_funcs vc4_dummy_connector_helper_funcs = { +};
+static const struct drm_connector_funcs vc4_dummy_connector_funcs = {
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .reset = drm_atomic_helper_connector_reset,
+};
+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_dummy_output *dummy_output;
- struct drm_connector *conn;
- struct drm_encoder *enc;
- int ret;
- dummy_output = kunit_kzalloc(test, sizeof(*dummy_output), GFP_KERNEL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_output);
- dummy_output->encoder.type = vc4_encoder_type;
- enc = &dummy_output->encoder.base;
- ret = drmm_encoder_init(drm, enc,
NULL,
kms_encoder_type,
NULL);
- KUNIT_ASSERT_EQ(test, ret, 0);
- enc->possible_crtcs = drm_crtc_mask(crtc);
- conn = &dummy_output->connector;
- ret = drmm_connector_init(drm, conn,
&vc4_dummy_connector_funcs,
connector_type,
NULL);
- KUNIT_ASSERT_EQ(test, ret, 0);
- drm_connector_helper_add(conn, &vc4_dummy_connector_helper_funcs);
- drm_connector_attach_encoder(conn, enc);
- return dummy_output;
+}
+static const struct drm_display_mode default_mode = {
- DRM_SIMPLE_MODE(640, 480, 64, 48)
+};
+int vc4_mock_atomic_add_output(struct kunit *test, struct drm_device *drm,
enum vc4_encoder_type type,
struct drm_atomic_state *state)
+{
- struct vc4_dummy_output *output;
- struct drm_connector *conn;
- struct drm_connector_state *conn_state;
- struct drm_encoder *encoder;
- struct drm_crtc *crtc;
- struct drm_crtc_state *crtc_state;
- int ret;
- encoder = vc4_find_encoder_by_type(drm, type);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, encoder);
- crtc = vc4_find_crtc_for_encoder(test, drm, encoder);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc);
- output = container_of(encoder, struct vc4_dummy_output, encoder.base);
- conn = &output->connector;
- conn_state = drm_atomic_get_connector_state(state, conn);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
- ret = drm_atomic_set_crtc_for_connector(conn_state, crtc);
- KUNIT_EXPECT_EQ(test, ret, 0);
- crtc_state = drm_atomic_get_crtc_state(state, crtc);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
- ret = drm_atomic_set_mode_for_crtc(crtc_state, &default_mode);
- KUNIT_EXPECT_EQ(test, ret, 0);
- crtc_state->active = true;
- return 0;
+} diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c new file mode 100644 index 000000000000..62b18f5f41db --- /dev/null +++ b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0
+#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_plane.h>
+#include <kunit/test.h>
+#include "vc4_mock.h"
+static const struct drm_plane_helper_funcs vc4_dummy_plane_helper_funcs = { +};
+static const struct drm_plane_funcs vc4_dummy_plane_funcs = {
- .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
- .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
- .reset = drm_atomic_helper_plane_reset,
+};
+static const uint32_t vc4_dummy_plane_formats[] = {
- DRM_FORMAT_XRGB8888,
+};
+struct vc4_dummy_plane *vc4_dummy_plane(struct kunit *test,
struct drm_device *drm,
enum drm_plane_type type)
+{
- struct vc4_dummy_plane *dummy_plane;
- struct drm_plane *plane;
- dummy_plane = drmm_universal_plane_alloc(drm,
struct vc4_dummy_plane, plane.base,
0,
&vc4_dummy_plane_funcs,
vc4_dummy_plane_formats,
ARRAY_SIZE(vc4_dummy_plane_formats),
NULL,
DRM_PLANE_TYPE_PRIMARY,
NULL);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_plane);
- plane = &dummy_plane->plane.base;
- drm_plane_helper_add(plane, &vc4_dummy_plane_helper_funcs);
- return dummy_plane;
+} diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 195d2b61839d..7d1a696477ce 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -675,8 +675,8 @@ void vc4_crtc_get_margins(struct drm_crtc_state *state, } } -static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_state *state)
+int vc4_crtc_atomic_check(struct drm_crtc *crtc,
{ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);struct drm_atomic_state *state)
@@ -1116,7 +1116,7 @@ static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = { .get_scanout_position = vc4_crtc_get_scanout_position, }; -static const struct vc4_pv_data bcm2835_pv0_data = { +const struct vc4_pv_data bcm2835_pv0_data = { .base = { .name = "pixelvalve-0", .debugfs_name = "crtc0_regs", @@ -1131,7 +1131,7 @@ static const struct vc4_pv_data bcm2835_pv0_data = { }, }; -static const struct vc4_pv_data bcm2835_pv1_data = { +const struct vc4_pv_data bcm2835_pv1_data = { .base = { .name = "pixelvalve-1", .debugfs_name = "crtc1_regs", @@ -1146,7 +1146,7 @@ static const struct vc4_pv_data bcm2835_pv1_data = { }, }; -static const struct vc4_pv_data bcm2835_pv2_data = { +const struct vc4_pv_data bcm2835_pv2_data = { .base = { .name = "pixelvalve-2", .debugfs_name = "crtc2_regs", @@ -1161,7 +1161,7 @@ static const struct vc4_pv_data bcm2835_pv2_data = { }, }; -static const struct vc4_pv_data bcm2711_pv0_data = { +const struct vc4_pv_data bcm2711_pv0_data = { .base = { .name = "pixelvalve-0", .debugfs_name = "crtc0_regs", @@ -1176,7 +1176,7 @@ static const struct vc4_pv_data bcm2711_pv0_data = { }, }; -static const struct vc4_pv_data bcm2711_pv1_data = { +const struct vc4_pv_data bcm2711_pv1_data = { .base = { .name = "pixelvalve-1", .debugfs_name = "crtc1_regs", @@ -1191,7 +1191,7 @@ static const struct vc4_pv_data bcm2711_pv1_data = { }, }; -static const struct vc4_pv_data bcm2711_pv2_data = { +const struct vc4_pv_data bcm2711_pv2_data = { .base = { .name = "pixelvalve-2", .debugfs_name = "crtc2_regs", @@ -1205,7 +1205,7 @@ static const struct vc4_pv_data bcm2711_pv2_data = { }, }; -static const struct vc4_pv_data bcm2711_pv3_data = { +const struct vc4_pv_data bcm2711_pv3_data = { .base = { .name = "pixelvalve-3", .debugfs_name = "crtc3_regs", @@ -1219,7 +1219,7 @@ static const struct vc4_pv_data bcm2711_pv3_data = { }, }; -static const struct vc4_pv_data bcm2711_pv4_data = { +const struct vc4_pv_data bcm2711_pv4_data = { .base = { .name = "pixelvalve-4", .debugfs_name = "crtc4_regs", diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 5990d8f8c363..3b0ae2c3e33c 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -196,7 +196,7 @@ static const struct drm_ioctl_desc vc4_drm_ioctls[] = { DRM_IOCTL_DEF_DRV(VC4_PERFMON_GET_VALUES, vc4_perfmon_get_values_ioctl, DRM_RENDER_ALLOW), }; -static const struct drm_driver vc4_drm_driver = { +const struct drm_driver vc4_drm_driver = { .driver_features = (DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM | @@ -225,7 +225,7 @@ static const struct drm_driver vc4_drm_driver = { .patchlevel = DRIVER_PATCHLEVEL, }; -static const struct drm_driver vc5_drm_driver = { +const struct drm_driver vc5_drm_driver = { .driver_features = (DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM), diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index e0be7a81a24a..418f4f308e36 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -24,6 +24,9 @@ struct drm_device; struct drm_gem_object; +extern const struct drm_driver vc4_drm_driver; +extern const struct drm_driver vc5_drm_driver;
- /* Don't forget to update vc4_bo.c: bo_type_names[] when adding to
*/
- this.
@@ -523,6 +526,8 @@ struct vc4_crtc_data { int hvs_output; }; +extern const struct vc4_crtc_data vc4_txp_crtc_data;
- struct vc4_pv_data { struct vc4_crtc_data base;
@@ -535,6 +540,15 @@ struct vc4_pv_data { enum vc4_encoder_type encoder_types[4]; }; +extern const struct vc4_pv_data bcm2835_pv0_data; +extern const struct vc4_pv_data bcm2835_pv1_data; +extern const struct vc4_pv_data bcm2835_pv2_data; +extern const struct vc4_pv_data bcm2711_pv0_data; +extern const struct vc4_pv_data bcm2711_pv1_data; +extern const struct vc4_pv_data bcm2711_pv2_data; +extern const struct vc4_pv_data bcm2711_pv3_data; +extern const struct vc4_pv_data bcm2711_pv4_data;
- struct vc4_crtc { struct drm_crtc base; struct platform_device *pdev;
@@ -920,6 +934,8 @@ int vc4_page_flip(struct drm_crtc *crtc, struct drm_pending_vblank_event *event, uint32_t flags, struct drm_modeset_acquire_ctx *ctx); +int vc4_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc); void vc4_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state);struct drm_atomic_state *state);
diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index 4f7ce5d3e8ad..2b69454b8534 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -479,7 +479,7 @@ static irqreturn_t vc4_txp_interrupt(int irq, void *data) return IRQ_HANDLED; } -static const struct vc4_crtc_data vc4_txp_crtc_data = { +const struct vc4_crtc_data vc4_txp_crtc_data = { .name = "txp", .debugfs_name = "txp_regs", .hvs_available_channels = BIT(2),
Accessing a register when running under kunit is a bad idea since our device is completely mocked.
Fail the current test if we ever access any of our hardware registers.
Reviewed-by: Javier Martinez Canillas javierm@redhat.com Signed-off-by: Maxime Ripard maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_crtc.c | 13 +++++++++++-- drivers/gpu/drm/vc4/vc4_dpi.c | 13 +++++++++++-- drivers/gpu/drm/vc4/vc4_drv.h | 29 +++++++++++++++++++++++++---- drivers/gpu/drm/vc4/vc4_dsi.c | 9 ++++++++- drivers/gpu/drm/vc4/vc4_hdmi_regs.h | 4 ++++ drivers/gpu/drm/vc4/vc4_txp.c | 13 +++++++++++-- drivers/gpu/drm/vc4/vc4_vec.c | 13 +++++++++++-- 7 files changed, 81 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 7d1a696477ce..a1a3465948c4 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -50,8 +50,17 @@
#define HVS_FIFO_LATENCY_PIX 6
-#define CRTC_WRITE(offset, val) writel(val, vc4_crtc->regs + (offset)) -#define CRTC_READ(offset) readl(vc4_crtc->regs + (offset)) +#define CRTC_WRITE(offset, val) \ + do { \ + kunit_fail_current_test("Accessing a register in a unit test!\n"); \ + writel(val, vc4_crtc->regs + (offset)); \ + } while (0) + +#define CRTC_READ(offset) \ + ({ \ + kunit_fail_current_test("Accessing a register in a unit test!\n"); \ + readl(vc4_crtc->regs + (offset)); \ + })
static const struct debugfs_reg32 crtc_regs[] = { VC4_REG32(PV_CONTROL), diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index 1f8f44b7b5a5..0edf3c4c98c8 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c @@ -103,8 +103,17 @@ to_vc4_dpi(struct drm_encoder *encoder) return container_of(encoder, struct vc4_dpi, encoder.base); }
-#define DPI_READ(offset) readl(dpi->regs + (offset)) -#define DPI_WRITE(offset, val) writel(val, dpi->regs + (offset)) +#define DPI_READ(offset) \ + ({ \ + kunit_fail_current_test("Accessing a register in a unit test!\n"); \ + readl(dpi->regs + (offset)); \ + }) + +#define DPI_WRITE(offset, val) \ + do { \ + kunit_fail_current_test("Accessing a register in a unit test!\n"); \ + writel(val, dpi->regs + (offset)); \ + } while (0)
static const struct debugfs_reg32 dpi_regs[] = { VC4_REG32(DPI_C), diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 418f4f308e36..78fda5332cb3 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -19,6 +19,8 @@ #include <drm/drm_mm.h> #include <drm/drm_modeset_lock.h>
+#include <kunit/test-bug.h> + #include "uapi/drm/vc4_drm.h"
struct drm_device; @@ -645,10 +647,29 @@ to_vc4_crtc_state(const struct drm_crtc_state *crtc_state) return container_of(crtc_state, struct vc4_crtc_state, base); }
-#define V3D_READ(offset) readl(vc4->v3d->regs + offset) -#define V3D_WRITE(offset, val) writel(val, vc4->v3d->regs + offset) -#define HVS_READ(offset) readl(hvs->regs + offset) -#define HVS_WRITE(offset, val) writel(val, hvs->regs + offset) +#define V3D_READ(offset) \ + ({ \ + kunit_fail_current_test("Accessing a register in a unit test!\n"); \ + readl(vc4->v3d->regs + (offset)); \ + }) + +#define V3D_WRITE(offset, val) \ + do { \ + kunit_fail_current_test("Accessing a register in a unit test!\n"); \ + writel(val, vc4->v3d->regs + (offset)); \ + } while (0) + +#define HVS_READ(offset) \ + ({ \ + kunit_fail_current_test("Accessing a register in a unit test!\n"); \ + readl(hvs->regs + (offset)); \ + }) + +#define HVS_WRITE(offset, val) \ + do { \ + kunit_fail_current_test("Accessing a register in a unit test!\n"); \ + writel(val, hvs->regs + (offset)); \ + } while (0)
#define VC4_REG32(reg) { .name = #reg, .offset = reg }
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index 878e05d79e81..2c9cb27903a0 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -617,6 +617,8 @@ dsi_dma_workaround_write(struct vc4_dsi *dsi, u32 offset, u32 val) dma_cookie_t cookie; int ret;
+ kunit_fail_current_test("Accessing a register in a unit test!\n"); + /* DSI0 should be able to write normally. */ if (!chan) { writel(val, dsi->regs + offset); @@ -645,7 +647,12 @@ dsi_dma_workaround_write(struct vc4_dsi *dsi, u32 offset, u32 val) DRM_ERROR("Failed to wait for DMA: %d\n", ret); }
-#define DSI_READ(offset) readl(dsi->regs + (offset)) +#define DSI_READ(offset) \ + ({ \ + kunit_fail_current_test("Accessing a register in a unit test!\n"); \ + readl(dsi->regs + (offset)); \ + }) + #define DSI_WRITE(offset, val) dsi_dma_workaround_write(dsi, offset, val) #define DSI_PORT_READ(offset) \ DSI_READ(dsi->variant->port ? DSI1_##offset : DSI0_##offset) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h index 48db438550b1..b04b2fc8d831 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h +++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h @@ -456,6 +456,8 @@ static inline u32 vc4_hdmi_read(struct vc4_hdmi *hdmi,
WARN_ON(pm_runtime_status_suspended(&hdmi->pdev->dev));
+ kunit_fail_current_test("Accessing an HDMI register in a unit test!\n"); + if (reg >= variant->num_registers) { dev_warn(&hdmi->pdev->dev, "Invalid register ID %u\n", reg); @@ -486,6 +488,8 @@ static inline void vc4_hdmi_write(struct vc4_hdmi *hdmi,
WARN_ON(pm_runtime_status_suspended(&hdmi->pdev->dev));
+ kunit_fail_current_test("Accessing an HDMI register in a unit test!\n"); + if (reg >= variant->num_registers) { dev_warn(&hdmi->pdev->dev, "Invalid register ID %u\n", reg); diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index 2b69454b8534..ef5cab2a3aa9 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -145,8 +145,17 @@ /* Number of lines received and committed to memory. */ #define TXP_PROGRESS 0x10
-#define TXP_READ(offset) readl(txp->regs + (offset)) -#define TXP_WRITE(offset, val) writel(val, txp->regs + (offset)) +#define TXP_READ(offset) \ + ({ \ + kunit_fail_current_test("Accessing a register in a unit test!\n"); \ + readl(txp->regs + (offset)); \ + }) + +#define TXP_WRITE(offset, val) \ + do { \ + kunit_fail_current_test("Accessing a register in a unit test!\n"); \ + writel(val, txp->regs + (offset)); \ + } while (0)
struct vc4_txp { struct vc4_crtc base; diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c index e270a4099be3..f589ab918f4d 100644 --- a/drivers/gpu/drm/vc4/vc4_vec.c +++ b/drivers/gpu/drm/vc4/vc4_vec.c @@ -207,8 +207,17 @@ struct vc4_vec { struct debugfs_regset32 regset; };
-#define VEC_READ(offset) readl(vec->regs + (offset)) -#define VEC_WRITE(offset, val) writel(val, vec->regs + (offset)) +#define VEC_READ(offset) \ + ({ \ + kunit_fail_current_test("Accessing a register in a unit test!\n"); \ + readl(vec->regs + (offset)); \ + }) + +#define VEC_WRITE(offset, val) \ + do { \ + kunit_fail_current_test("Accessing a register in a unit test!\n"); \ + writel(val, vec->regs + (offset)); \ + } while (0)
static inline struct vc4_vec * encoder_to_vc4_vec(struct drm_encoder *encoder)
On 12/1/22 12:11, Maxime Ripard wrote:
Accessing a register when running under kunit is a bad idea since our device is completely mocked.
Fail the current test if we ever access any of our hardware registers.
Reviewed-by: Javier Martinez Canillas javierm@redhat.com Signed-off-by: Maxime Ripard maxime@cerno.tech
Reviewed-by: Maíra Canal mcanal@igalia.com
Just a small nit: I believe that macros with multiple statements should be enclosed in a do-while block [1], even READ macros. I saw that you enclosed the WRITE macros on a do-while block, but not the READ macros.
[1] https://www.kernel.org/doc/html/latest/process/coding-style.html#macros-enum...
Best Regards, - Maíra Canal
drivers/gpu/drm/vc4/vc4_crtc.c | 13 +++++++++++-- drivers/gpu/drm/vc4/vc4_dpi.c | 13 +++++++++++-- drivers/gpu/drm/vc4/vc4_drv.h | 29 +++++++++++++++++++++++++---- drivers/gpu/drm/vc4/vc4_dsi.c | 9 ++++++++- drivers/gpu/drm/vc4/vc4_hdmi_regs.h | 4 ++++ drivers/gpu/drm/vc4/vc4_txp.c | 13 +++++++++++-- drivers/gpu/drm/vc4/vc4_vec.c | 13 +++++++++++-- 7 files changed, 81 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 7d1a696477ce..a1a3465948c4 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -50,8 +50,17 @@ #define HVS_FIFO_LATENCY_PIX 6 -#define CRTC_WRITE(offset, val) writel(val, vc4_crtc->regs + (offset)) -#define CRTC_READ(offset) readl(vc4_crtc->regs + (offset)) +#define CRTC_WRITE(offset, val) \
- do { \
kunit_fail_current_test("Accessing a register in a unit test!\n"); \
writel(val, vc4_crtc->regs + (offset)); \
- } while (0)
+#define CRTC_READ(offset) \
- ({ \
kunit_fail_current_test("Accessing a register in a unit test!\n"); \
readl(vc4_crtc->regs + (offset)); \
- })
static const struct debugfs_reg32 crtc_regs[] = { VC4_REG32(PV_CONTROL), diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index 1f8f44b7b5a5..0edf3c4c98c8 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c @@ -103,8 +103,17 @@ to_vc4_dpi(struct drm_encoder *encoder) return container_of(encoder, struct vc4_dpi, encoder.base); } -#define DPI_READ(offset) readl(dpi->regs + (offset)) -#define DPI_WRITE(offset, val) writel(val, dpi->regs + (offset)) +#define DPI_READ(offset) \
- ({ \
kunit_fail_current_test("Accessing a register in a unit test!\n"); \
readl(dpi->regs + (offset)); \
- })
+#define DPI_WRITE(offset, val) \
- do { \
kunit_fail_current_test("Accessing a register in a unit test!\n"); \
writel(val, dpi->regs + (offset)); \
- } while (0)
static const struct debugfs_reg32 dpi_regs[] = { VC4_REG32(DPI_C), diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 418f4f308e36..78fda5332cb3 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -19,6 +19,8 @@ #include <drm/drm_mm.h> #include <drm/drm_modeset_lock.h> +#include <kunit/test-bug.h>
- #include "uapi/drm/vc4_drm.h"
struct drm_device; @@ -645,10 +647,29 @@ to_vc4_crtc_state(const struct drm_crtc_state *crtc_state) return container_of(crtc_state, struct vc4_crtc_state, base); } -#define V3D_READ(offset) readl(vc4->v3d->regs + offset) -#define V3D_WRITE(offset, val) writel(val, vc4->v3d->regs + offset) -#define HVS_READ(offset) readl(hvs->regs + offset) -#define HVS_WRITE(offset, val) writel(val, hvs->regs + offset) +#define V3D_READ(offset) \
- ({ \
kunit_fail_current_test("Accessing a register in a unit test!\n"); \
readl(vc4->v3d->regs + (offset)); \
- })
+#define V3D_WRITE(offset, val) \
- do { \
kunit_fail_current_test("Accessing a register in a unit test!\n"); \
writel(val, vc4->v3d->regs + (offset)); \
- } while (0)
+#define HVS_READ(offset) \
- ({ \
kunit_fail_current_test("Accessing a register in a unit test!\n"); \
readl(hvs->regs + (offset)); \
- })
+#define HVS_WRITE(offset, val) \
- do { \
kunit_fail_current_test("Accessing a register in a unit test!\n"); \
writel(val, hvs->regs + (offset)); \
- } while (0)
#define VC4_REG32(reg) { .name = #reg, .offset = reg } diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index 878e05d79e81..2c9cb27903a0 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -617,6 +617,8 @@ dsi_dma_workaround_write(struct vc4_dsi *dsi, u32 offset, u32 val) dma_cookie_t cookie; int ret;
- kunit_fail_current_test("Accessing a register in a unit test!\n");
- /* DSI0 should be able to write normally. */ if (!chan) { writel(val, dsi->regs + offset);
@@ -645,7 +647,12 @@ dsi_dma_workaround_write(struct vc4_dsi *dsi, u32 offset, u32 val) DRM_ERROR("Failed to wait for DMA: %d\n", ret); } -#define DSI_READ(offset) readl(dsi->regs + (offset)) +#define DSI_READ(offset) \
- ({ \
kunit_fail_current_test("Accessing a register in a unit test!\n"); \
readl(dsi->regs + (offset)); \
- })
- #define DSI_WRITE(offset, val) dsi_dma_workaround_write(dsi, offset, val) #define DSI_PORT_READ(offset) \ DSI_READ(dsi->variant->port ? DSI1_##offset : DSI0_##offset)
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h index 48db438550b1..b04b2fc8d831 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h +++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h @@ -456,6 +456,8 @@ static inline u32 vc4_hdmi_read(struct vc4_hdmi *hdmi, WARN_ON(pm_runtime_status_suspended(&hdmi->pdev->dev));
- kunit_fail_current_test("Accessing an HDMI register in a unit test!\n");
- if (reg >= variant->num_registers) { dev_warn(&hdmi->pdev->dev, "Invalid register ID %u\n", reg);
@@ -486,6 +488,8 @@ static inline void vc4_hdmi_write(struct vc4_hdmi *hdmi, WARN_ON(pm_runtime_status_suspended(&hdmi->pdev->dev));
- kunit_fail_current_test("Accessing an HDMI register in a unit test!\n");
- if (reg >= variant->num_registers) { dev_warn(&hdmi->pdev->dev, "Invalid register ID %u\n", reg);
diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index 2b69454b8534..ef5cab2a3aa9 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -145,8 +145,17 @@ /* Number of lines received and committed to memory. */ #define TXP_PROGRESS 0x10 -#define TXP_READ(offset) readl(txp->regs + (offset)) -#define TXP_WRITE(offset, val) writel(val, txp->regs + (offset)) +#define TXP_READ(offset) \
- ({ \
kunit_fail_current_test("Accessing a register in a unit test!\n"); \
readl(txp->regs + (offset)); \
- })
+#define TXP_WRITE(offset, val) \
- do { \
kunit_fail_current_test("Accessing a register in a unit test!\n"); \
writel(val, txp->regs + (offset)); \
- } while (0)
struct vc4_txp { struct vc4_crtc base; diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c index e270a4099be3..f589ab918f4d 100644 --- a/drivers/gpu/drm/vc4/vc4_vec.c +++ b/drivers/gpu/drm/vc4/vc4_vec.c @@ -207,8 +207,17 @@ struct vc4_vec { struct debugfs_regset32 regset; }; -#define VEC_READ(offset) readl(vec->regs + (offset)) -#define VEC_WRITE(offset, val) writel(val, vec->regs + (offset)) +#define VEC_READ(offset) \
- ({ \
kunit_fail_current_test("Accessing a register in a unit test!\n"); \
readl(vec->regs + (offset)); \
- })
+#define VEC_WRITE(offset, val) \
- do { \
kunit_fail_current_test("Accessing a register in a unit test!\n"); \
writel(val, vec->regs + (offset)); \
- } while (0)
static inline struct vc4_vec * encoder_to_vc4_vec(struct drm_encoder *encoder)
Hi Maíra,
Thanks for your review!
On Wed, Dec 07, 2022 at 11:26:13AM -0300, Maíra Canal wrote:
On 12/1/22 12:11, Maxime Ripard wrote:
Accessing a register when running under kunit is a bad idea since our device is completely mocked.
Fail the current test if we ever access any of our hardware registers.
Reviewed-by: Javier Martinez Canillas javierm@redhat.com Signed-off-by: Maxime Ripard maxime@cerno.tech
Reviewed-by: Maíra Canal mcanal@igalia.com
Just a small nit: I believe that macros with multiple statements should be enclosed in a do-while block [1], even READ macros. I saw that you enclosed the WRITE macros on a do-while block, but not the READ macros.
This is on purpose: do-while blocks don't return a value, but ({ ... }) blocks do. So we can do a write macros with a do-while, but can't for read since we expect to get a value back.
Maxime
The HVS to PixelValve muxing code is fairly error prone and has a bunch of arbitrary constraints due to the hardware setup.
Let's create a test suite that makes sure that the possible combinations work and the invalid ones don't.
Reviewed-by: Javier Martinez Canillas javierm@redhat.com Signed-off-by: Maxime Ripard maxime@cerno.tech --- drivers/gpu/drm/vc4/Makefile | 3 +- drivers/gpu/drm/vc4/tests/vc4_mock.h | 9 +- drivers/gpu/drm/vc4/tests/vc4_mock_output.c | 49 +- drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c | 1039 ++++++++++++++++++++++++ 4 files changed, 1091 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile index f974a8c80e2f..c41f89a15a55 100644 --- a/drivers/gpu/drm/vc4/Makefile +++ b/drivers/gpu/drm/vc4/Makefile @@ -29,7 +29,8 @@ 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 + tests/vc4_mock_plane.o \ + tests/vc4_test_pv_muxing.o
vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o
diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock.h b/drivers/gpu/drm/vc4/tests/vc4_mock.h index ace5b2e00f4a..db8e9a141ef8 100644 --- a/drivers/gpu/drm/vc4/tests/vc4_mock.h +++ b/drivers/gpu/drm/vc4/tests/vc4_mock.h @@ -53,8 +53,11 @@ struct vc4_dummy_output *vc4_dummy_output(struct kunit *test, 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); +int vc4_mock_atomic_add_output(struct kunit *test, + struct drm_atomic_state *state, + enum vc4_encoder_type type); +int vc4_mock_atomic_del_output(struct kunit *test, + struct drm_atomic_state *state, + enum vc4_encoder_type type);
#endif // VC4_MOCK_H_ diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock_output.c b/drivers/gpu/drm/vc4/tests/vc4_mock_output.c index cb16ab4451f7..8d33be828d9a 100644 --- a/drivers/gpu/drm/vc4/tests/vc4_mock_output.c +++ b/drivers/gpu/drm/vc4/tests/vc4_mock_output.c @@ -61,16 +61,17 @@ static const struct drm_display_mode default_mode = { DRM_SIMPLE_MODE(640, 480, 64, 48) };
-int vc4_mock_atomic_add_output(struct kunit *test, struct drm_device *drm, - enum vc4_encoder_type type, - struct drm_atomic_state *state) +int vc4_mock_atomic_add_output(struct kunit *test, + struct drm_atomic_state *state, + enum vc4_encoder_type type) { + struct drm_device *drm = state->dev; + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; struct vc4_dummy_output *output; struct drm_connector *conn; - struct drm_connector_state *conn_state; struct drm_encoder *encoder; struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; int ret;
encoder = vc4_find_encoder_by_type(drm, type); @@ -97,3 +98,41 @@ int vc4_mock_atomic_add_output(struct kunit *test, struct drm_device *drm,
return 0; } + +int vc4_mock_atomic_del_output(struct kunit *test, + struct drm_atomic_state *state, + enum vc4_encoder_type type) +{ + struct drm_device *drm = state->dev; + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + struct vc4_dummy_output *output; + struct drm_connector *conn; + struct drm_encoder *encoder; + struct drm_crtc *crtc; + int ret; + + encoder = vc4_find_encoder_by_type(drm, type); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, encoder); + + crtc = vc4_find_crtc_for_encoder(test, drm, encoder); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc); + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + + crtc_state->active = false; + + ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL); + KUNIT_ASSERT_EQ(test, ret, 0); + + output = container_of(encoder, struct vc4_dummy_output, encoder.base); + conn = &output->connector; + conn_state = drm_atomic_get_connector_state(state, conn); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + ret = drm_atomic_set_crtc_for_connector(conn_state, NULL); + KUNIT_ASSERT_EQ(test, ret, 0); + + return 0; +} diff --git a/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c new file mode 100644 index 000000000000..ae0bd0f81698 --- /dev/null +++ b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c @@ -0,0 +1,1039 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_atomic_uapi.h> +#include <drm/drm_crtc.h> +#include <drm/drm_drv.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_kunit_helpers.h> +#include <drm/drm_mode.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_plane.h> + +#include <kunit/test.h> + +#include "../vc4_drv.h" + +#include "vc4_mock.h" + +struct pv_muxing_priv { + struct vc4_dev *vc4; + struct drm_modeset_acquire_ctx ctx; + struct drm_atomic_state *state; +}; + +static bool check_fifo_conflict(struct kunit *test, + const struct drm_atomic_state *state) +{ + struct vc4_hvs_state *hvs_state; + unsigned int used_fifos = 0; + unsigned int i; + + hvs_state = vc4_hvs_get_new_global_state(state); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hvs_state); + + for (i = 0; i < HVS_NUM_CHANNELS; i++) { + if (!hvs_state->fifo_state[i].in_use) + continue; + + KUNIT_EXPECT_FALSE(test, used_fifos & BIT(i)); + used_fifos |= BIT(i); + } + + return true; +} + +struct encoder_constraint { + enum vc4_encoder_type type; + unsigned int *channels; + size_t nchannels; +}; + +#define ENCODER_CONSTRAINT(_type, ...) \ + { \ + .type = _type, \ + .channels = (unsigned int[]) { __VA_ARGS__ }, \ + .nchannels = sizeof((unsigned int[]) { __VA_ARGS__ }) / \ + sizeof(unsigned int), \ + } + +static bool __check_encoder_constraints(const struct encoder_constraint *constraints, + size_t nconstraints, + enum vc4_encoder_type type, + unsigned int channel) +{ + unsigned int i; + + for (i = 0; i < nconstraints; i++) { + const struct encoder_constraint *constraint = &constraints[i]; + unsigned int j; + + if (constraint->type != type) + continue; + + for (j = 0; j < constraint->nchannels; j++) { + unsigned int _channel = constraint->channels[j]; + + if (channel != _channel) + continue; + + return true; + } + } + + return false; +} + +static const struct encoder_constraint vc4_encoder_constraints[] = { + ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DPI, 0), + ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DSI0, 0), + ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI0, 1), + ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_VEC, 1), + ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_TXP, 2), + ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DSI1, 2), +}; + +static const struct encoder_constraint vc5_encoder_constraints[] = { + ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DPI, 0), + ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DSI0, 0), + ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_VEC, 1), + ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_TXP, 0, 2), + ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DSI1, 0, 1, 2), + ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI0, 0, 1, 2), + ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI1, 0, 1, 2), +}; + +static bool check_vc4_encoder_constraints(enum vc4_encoder_type type, unsigned int channel) +{ + return __check_encoder_constraints(vc4_encoder_constraints, + ARRAY_SIZE(vc4_encoder_constraints), + type, channel); +} + +static bool check_vc5_encoder_constraints(enum vc4_encoder_type type, unsigned int channel) +{ + return __check_encoder_constraints(vc5_encoder_constraints, + ARRAY_SIZE(vc5_encoder_constraints), + type, channel); +} + +static struct vc4_crtc_state * +get_vc4_crtc_state_for_encoder(struct kunit *test, + const struct drm_atomic_state *state, + enum vc4_encoder_type type) +{ + struct drm_device *drm = state->dev; + struct drm_crtc_state *new_crtc_state; + struct drm_encoder *encoder; + struct drm_crtc *crtc; + + encoder = vc4_find_encoder_by_type(drm, type); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, encoder); + + crtc = vc4_find_crtc_for_encoder(test, drm, encoder); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc); + + new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + if (!new_crtc_state) + return NULL; + + return to_vc4_crtc_state(new_crtc_state); +} + +static bool check_channel_for_encoder(struct kunit *test, + const struct drm_atomic_state *state, + enum vc4_encoder_type type, + bool (*check_fn)(enum vc4_encoder_type type, unsigned int channel)) +{ + struct vc4_crtc_state *new_vc4_crtc_state; + struct vc4_hvs_state *new_hvs_state; + unsigned int channel; + + new_hvs_state = vc4_hvs_get_new_global_state(state); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_hvs_state); + + new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state, type); + KUNIT_ASSERT_NOT_NULL(test, new_vc4_crtc_state); + + channel = new_vc4_crtc_state->assigned_channel; + KUNIT_EXPECT_NE(test, channel, VC4_HVS_CHANNEL_DISABLED); + + KUNIT_EXPECT_TRUE(test, new_hvs_state->fifo_state[channel].in_use); + + KUNIT_EXPECT_TRUE(test, check_fn(type, channel)); + + return true; +} + +struct pv_muxing_param { + const char *name; + struct vc4_dev *(*mock_fn)(struct kunit *test); + bool (*check_fn)(enum vc4_encoder_type type, unsigned int channel); + enum vc4_encoder_type *encoders; + size_t nencoders; +}; + +static void vc4_test_pv_muxing_desc(const struct pv_muxing_param *t, char *desc) +{ + strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); +} + +#define PV_MUXING_TEST(_name, _mock_fn, _check_fn, ...) \ + { \ + .name = _name, \ + .mock_fn = &_mock_fn, \ + .check_fn = &_check_fn, \ + .encoders = (enum vc4_encoder_type[]) { __VA_ARGS__ }, \ + .nencoders = sizeof((enum vc4_encoder_type[]) { __VA_ARGS__ }) / \ + sizeof(enum vc4_encoder_type), \ + } + +#define VC4_PV_MUXING_TEST(_name, ...) \ + PV_MUXING_TEST(_name, vc4_mock_device, check_vc4_encoder_constraints, __VA_ARGS__) + +#define VC5_PV_MUXING_TEST(_name, ...) \ + PV_MUXING_TEST(_name, vc5_mock_device, check_vc5_encoder_constraints, __VA_ARGS__) + +static const struct pv_muxing_param vc4_test_pv_muxing_params[] = { + VC4_PV_MUXING_TEST("1 output: DSI0", + VC4_ENCODER_TYPE_DSI0), + VC4_PV_MUXING_TEST("1 output: DPI", + VC4_ENCODER_TYPE_DPI), + VC4_PV_MUXING_TEST("1 output: HDMI0", + VC4_ENCODER_TYPE_HDMI0), + VC4_PV_MUXING_TEST("1 output: VEC", + VC4_ENCODER_TYPE_VEC), + VC4_PV_MUXING_TEST("1 output: DSI1", + VC4_ENCODER_TYPE_DSI1), + VC4_PV_MUXING_TEST("1 output: TXP", + VC4_ENCODER_TYPE_TXP), + VC4_PV_MUXING_TEST("2 outputs: DSI0, HDMI0", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_HDMI0), + VC4_PV_MUXING_TEST("2 outputs: DSI0, VEC", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_VEC), + VC4_PV_MUXING_TEST("2 outputs: DSI0, DSI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_DSI1), + VC4_PV_MUXING_TEST("2 outputs: DSI0, TXP", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_TXP), + VC4_PV_MUXING_TEST("2 outputs: DPI, HDMI0", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_HDMI0), + VC4_PV_MUXING_TEST("2 outputs: DPI, VEC", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_VEC), + VC4_PV_MUXING_TEST("2 outputs: DPI, DSI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_DSI1), + VC4_PV_MUXING_TEST("2 outputs: DPI, TXP", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_TXP), + VC4_PV_MUXING_TEST("2 outputs: HDMI0, DSI1", + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_DSI1), + VC4_PV_MUXING_TEST("2 outputs: HDMI0, TXP", + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_TXP), + VC4_PV_MUXING_TEST("2 outputs: VEC, DSI1", + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_DSI1), + VC4_PV_MUXING_TEST("2 outputs: VEC, TXP", + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_TXP), + VC4_PV_MUXING_TEST("3 outputs: DSI0, HDMI0, DSI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_DSI1), + VC4_PV_MUXING_TEST("3 outputs: DSI0, HDMI0, TXP", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_TXP), + VC4_PV_MUXING_TEST("3 outputs: DSI0, VEC, DSI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_DSI1), + VC4_PV_MUXING_TEST("3 outputs: DSI0, VEC, TXP", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_TXP), + VC4_PV_MUXING_TEST("3 outputs: DPI, HDMI0, DSI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_DSI1), + VC4_PV_MUXING_TEST("3 outputs: DPI, HDMI0, TXP", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_TXP), + VC4_PV_MUXING_TEST("3 outputs: DPI, VEC, DSI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_DSI1), + VC4_PV_MUXING_TEST("3 outputs: DPI, VEC, TXP", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_TXP), +}; + +KUNIT_ARRAY_PARAM(vc4_test_pv_muxing, + vc4_test_pv_muxing_params, + vc4_test_pv_muxing_desc); + +static const struct pv_muxing_param vc4_test_pv_muxing_invalid_params[] = { + VC4_PV_MUXING_TEST("DPI/DSI0 Conflict", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_DSI0), + VC4_PV_MUXING_TEST("TXP/DSI1 Conflict", + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_DSI1), + VC4_PV_MUXING_TEST("HDMI0/VEC Conflict", + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_VEC), + VC4_PV_MUXING_TEST("More than 3 outputs: DSI0, HDMI0, DSI1, TXP", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_TXP), + VC4_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, DSI1, TXP", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_TXP), + VC4_PV_MUXING_TEST("More than 3 outputs: DPI, HDMI0, DSI1, TXP", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_TXP), + VC4_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, DSI1, TXP", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_TXP), +}; + +KUNIT_ARRAY_PARAM(vc4_test_pv_muxing_invalid, + vc4_test_pv_muxing_invalid_params, + vc4_test_pv_muxing_desc); + +static const struct pv_muxing_param vc5_test_pv_muxing_params[] = { + VC5_PV_MUXING_TEST("1 output: DPI", + VC4_ENCODER_TYPE_DPI), + VC5_PV_MUXING_TEST("1 output: DSI0", + VC4_ENCODER_TYPE_DSI0), + VC5_PV_MUXING_TEST("1 output: DSI1", + VC4_ENCODER_TYPE_DSI1), + VC5_PV_MUXING_TEST("1 output: HDMI0", + VC4_ENCODER_TYPE_HDMI0), + VC5_PV_MUXING_TEST("1 output: HDMI1", + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("1 output: VEC", + VC4_ENCODER_TYPE_VEC), + VC5_PV_MUXING_TEST("2 outputs: DPI, DSI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_DSI1), + VC5_PV_MUXING_TEST("2 outputs: DPI, HDMI0", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_HDMI0), + VC5_PV_MUXING_TEST("2 outputs: DPI, HDMI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("2 outputs: DPI, TXP", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_TXP), + VC5_PV_MUXING_TEST("2 outputs: DPI, VEC", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_VEC), + VC5_PV_MUXING_TEST("2 outputs: DPI, DSI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_DSI1), + VC5_PV_MUXING_TEST("2 outputs: DSI0, DSI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_DSI1), + VC5_PV_MUXING_TEST("2 outputs: DSI0, HDMI0", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_HDMI0), + VC5_PV_MUXING_TEST("2 outputs: DSI0, HDMI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("2 outputs: DSI0, TXP", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_TXP), + VC5_PV_MUXING_TEST("2 outputs: DSI0, VEC", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_VEC), + VC5_PV_MUXING_TEST("2 outputs: DSI0, DSI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_DSI1), + VC5_PV_MUXING_TEST("2 outputs: DSI1, VEC", + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_VEC), + VC5_PV_MUXING_TEST("2 outputs: DSI1, TXP", + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_TXP), + VC5_PV_MUXING_TEST("2 outputs: DSI1, HDMI0", + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI0), + VC5_PV_MUXING_TEST("2 outputs: DSI1, HDMI1", + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("2 outputs: HDMI0, VEC", + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_VEC), + VC5_PV_MUXING_TEST("2 outputs: HDMI0, TXP", + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_TXP), + VC5_PV_MUXING_TEST("2 outputs: HDMI0, HDMI1", + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("2 outputs: HDMI1, VEC", + VC4_ENCODER_TYPE_HDMI1, + VC4_ENCODER_TYPE_VEC), + VC5_PV_MUXING_TEST("2 outputs: HDMI1, TXP", + VC4_ENCODER_TYPE_HDMI1, + VC4_ENCODER_TYPE_TXP), + VC5_PV_MUXING_TEST("2 outputs: TXP, VEC", + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_VEC), + VC5_PV_MUXING_TEST("3 outputs: DPI, VEC, TXP", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_TXP), + VC5_PV_MUXING_TEST("3 outputs: DPI, VEC, DSI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_DSI1), + VC5_PV_MUXING_TEST("3 outputs: DPI, VEC, HDMI0", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_HDMI0), + VC5_PV_MUXING_TEST("3 outputs: DPI, VEC, HDMI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("3 outputs: DPI, TXP, DSI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_DSI1), + VC5_PV_MUXING_TEST("3 outputs: DPI, TXP, HDMI0", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_HDMI0), + VC5_PV_MUXING_TEST("3 outputs: DPI, TXP, HDMI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("3 outputs: DPI, DSI1, HDMI0", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI0), + VC5_PV_MUXING_TEST("3 outputs: DPI, DSI1, HDMI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("3 outputs: DPI, HDMI0, HDMI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("3 outputs: DSI0, VEC, TXP", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_TXP), + VC5_PV_MUXING_TEST("3 outputs: DSI0, VEC, DSI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_DSI1), + VC5_PV_MUXING_TEST("3 outputs: DSI0, VEC, HDMI0", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_HDMI0), + VC5_PV_MUXING_TEST("3 outputs: DSI0, VEC, HDMI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("3 outputs: DSI0, TXP, DSI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_DSI1), + VC5_PV_MUXING_TEST("3 outputs: DSI0, TXP, HDMI0", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_HDMI0), + VC5_PV_MUXING_TEST("3 outputs: DSI0, TXP, HDMI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("3 outputs: DSI0, DSI1, HDMI0", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI0), + VC5_PV_MUXING_TEST("3 outputs: DSI0, DSI1, HDMI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("3 outputs: DSI0, HDMI0, HDMI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_HDMI1), +}; + +KUNIT_ARRAY_PARAM(vc5_test_pv_muxing, + vc5_test_pv_muxing_params, + vc4_test_pv_muxing_desc); + +static const struct pv_muxing_param vc5_test_pv_muxing_invalid_params[] = { + VC5_PV_MUXING_TEST("DPI/DSI0 Conflict", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_DSI0), + VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, DSI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_DSI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, HDMI0", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_HDMI0), + VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, HDMI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, DSI1, HDMI0", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI0), + VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, DSI1, HDMI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, HDMI0, HDMI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DPI, TXP, DSI1, HDMI0", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI0), + VC5_PV_MUXING_TEST("More than 3 outputs: DPI, TXP, DSI1, HDMI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DPI, TXP, HDMI0, HDMI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DPI, DSI1, HDMI0, HDMI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, DSI1, HDMI0", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI0), + VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, DSI1, HDMI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, HDMI0, HDMI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, DSI1, HDMI0, HDMI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DPI, TXP, DSI1, HDMI0, HDMI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, DSI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_DSI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, HDMI0", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_HDMI0), + VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, HDMI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, DSI1, HDMI0", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI0), + VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, DSI1, HDMI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, HDMI0, HDMI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, TXP, DSI1, HDMI0", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI0), + VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, TXP, DSI1, HDMI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, TXP, HDMI0, HDMI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, DSI1, HDMI0, HDMI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, DSI1, HDMI0", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI0), + VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, DSI1, HDMI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, HDMI0, HDMI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, DSI1, HDMI0, HDMI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, TXP, DSI1, HDMI0, HDMI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: VEC, TXP, DSI1, HDMI0, HDMI1", + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, DSI1, HDMI0, HDMI1", + VC4_ENCODER_TYPE_DPI, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_HDMI1), + VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, DSI1, HDMI0, HDMI1", + VC4_ENCODER_TYPE_DSI0, + VC4_ENCODER_TYPE_VEC, + VC4_ENCODER_TYPE_TXP, + VC4_ENCODER_TYPE_DSI1, + VC4_ENCODER_TYPE_HDMI0, + VC4_ENCODER_TYPE_HDMI1), +}; + +KUNIT_ARRAY_PARAM(vc5_test_pv_muxing_invalid, + vc5_test_pv_muxing_invalid_params, + vc4_test_pv_muxing_desc); + +static void drm_vc4_test_pv_muxing(struct kunit *test) +{ + const struct pv_muxing_param *params = test->param_value; + const struct pv_muxing_priv *priv = test->priv; + struct drm_atomic_state *state = priv->state; + unsigned int i; + int ret; + + for (i = 0; i < params->nencoders; i++) { + enum vc4_encoder_type enc_type = params->encoders[i]; + + ret = vc4_mock_atomic_add_output(test, state, enc_type); + KUNIT_ASSERT_EQ(test, ret, 0); + } + + ret = drm_atomic_check_only(state); + KUNIT_EXPECT_EQ(test, ret, 0); + + KUNIT_EXPECT_TRUE(test, + check_fifo_conflict(test, state)); + + for (i = 0; i < params->nencoders; i++) { + enum vc4_encoder_type enc_type = params->encoders[i]; + + KUNIT_EXPECT_TRUE(test, check_channel_for_encoder(test, state, enc_type, + params->check_fn)); + } +} + +static void drm_vc4_test_pv_muxing_invalid(struct kunit *test) +{ + const struct pv_muxing_param *params = test->param_value; + const struct pv_muxing_priv *priv = test->priv; + struct drm_atomic_state *state = priv->state; + unsigned int i; + int ret; + + for (i = 0; i < params->nencoders; i++) { + enum vc4_encoder_type enc_type = params->encoders[i]; + + ret = vc4_mock_atomic_add_output(test, state, enc_type); + KUNIT_ASSERT_EQ(test, ret, 0); + } + + ret = drm_atomic_check_only(state); + KUNIT_EXPECT_LT(test, ret, 0); +} + +static int vc4_pv_muxing_test_init(struct kunit *test) +{ + const struct pv_muxing_param *params = test->param_value; + struct drm_atomic_state *state; + struct pv_muxing_priv *priv; + struct drm_device *drm; + struct vc4_dev *vc4; + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, priv); + test->priv = priv; + + vc4 = params->mock_fn(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4); + priv->vc4 = vc4; + + drm_modeset_acquire_init(&priv->ctx, 0); + + drm = &vc4->base; + state = drm_atomic_state_alloc(drm); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + state->acquire_ctx = &priv->ctx; + + priv->state = state; + + return 0; +} + +static void vc4_pv_muxing_test_exit(struct kunit *test) +{ + struct pv_muxing_priv *priv = test->priv; + struct vc4_dev *vc4 = priv->vc4; + struct drm_device *drm = &vc4->base; + struct drm_atomic_state *state = priv->state; + + drm_atomic_state_put(state); + drm_modeset_drop_locks(&priv->ctx); + drm_modeset_acquire_fini(&priv->ctx); + drm_dev_unregister(drm); + drm_kunit_helper_free_device(test, vc4->dev); +} + +static struct kunit_case vc4_pv_muxing_tests[] = { + KUNIT_CASE_PARAM(drm_vc4_test_pv_muxing, + vc4_test_pv_muxing_gen_params), + KUNIT_CASE_PARAM(drm_vc4_test_pv_muxing_invalid, + vc4_test_pv_muxing_invalid_gen_params), + {} +}; + +static struct kunit_suite vc4_pv_muxing_test_suite = { + .name = "vc4-pv-muxing-combinations", + .init = vc4_pv_muxing_test_init, + .exit = vc4_pv_muxing_test_exit, + .test_cases = vc4_pv_muxing_tests, +}; + +static struct kunit_case vc5_pv_muxing_tests[] = { + KUNIT_CASE_PARAM(drm_vc4_test_pv_muxing, + vc5_test_pv_muxing_gen_params), + KUNIT_CASE_PARAM(drm_vc4_test_pv_muxing_invalid, + vc5_test_pv_muxing_invalid_gen_params), + {} +}; + +static struct kunit_suite vc5_pv_muxing_test_suite = { + .name = "vc5-pv-muxing-combinations", + .init = vc4_pv_muxing_test_init, + .exit = vc4_pv_muxing_test_exit, + .test_cases = vc5_pv_muxing_tests, +}; + +/* See + * https://lore.kernel.org/all/3e113525-aa89-b1e2-56b7-ca55bd41d057@samsung.com... + * and + * https://lore.kernel.org/dri-devel/20200917121623.42023-1-maxime@cerno.tech/ + */ +static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *test) +{ + struct drm_modeset_acquire_ctx ctx; + struct drm_atomic_state *state; + struct vc4_crtc_state *new_vc4_crtc_state; + struct vc4_hvs_state *new_hvs_state; + unsigned int hdmi0_channel; + unsigned int hdmi1_channel; + struct drm_device *drm; + struct vc4_dev *vc4; + int ret; + + vc4 = vc5_mock_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4); + + drm_modeset_acquire_init(&ctx, 0); + + drm = &vc4->base; + state = drm_atomic_state_alloc(drm); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + state->acquire_ctx = &ctx; + + ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = drm_atomic_check_only(state); + KUNIT_ASSERT_EQ(test, ret, 0); + + new_hvs_state = vc4_hvs_get_new_global_state(state); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_hvs_state); + + new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state, + VC4_ENCODER_TYPE_HDMI0); + KUNIT_ASSERT_NOT_NULL(test, new_vc4_crtc_state); + + hdmi0_channel = new_vc4_crtc_state->assigned_channel; + KUNIT_ASSERT_NE(test, hdmi0_channel, VC4_HVS_CHANNEL_DISABLED); + KUNIT_ASSERT_TRUE(test, new_hvs_state->fifo_state[hdmi0_channel].in_use); + + ret = drm_atomic_helper_swap_state(state, false); + KUNIT_ASSERT_EQ(test, ret, 0); + + drm_atomic_state_put(state); + + state = drm_atomic_state_alloc(drm); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + state->acquire_ctx = &ctx; + + ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = drm_atomic_check_only(state); + KUNIT_ASSERT_EQ(test, ret, 0); + + new_hvs_state = vc4_hvs_get_new_global_state(state); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_hvs_state); + + new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state, + VC4_ENCODER_TYPE_HDMI1); + KUNIT_ASSERT_NOT_NULL(test, new_vc4_crtc_state); + + hdmi1_channel = new_vc4_crtc_state->assigned_channel; + KUNIT_ASSERT_NE(test, hdmi1_channel, VC4_HVS_CHANNEL_DISABLED); + KUNIT_ASSERT_TRUE(test, new_hvs_state->fifo_state[hdmi1_channel].in_use); + + KUNIT_EXPECT_NE(test, hdmi0_channel, hdmi1_channel); + + drm_atomic_state_put(state); + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + drm_dev_unregister(drm); + drm_kunit_helper_free_device(test, vc4->dev); +} + +static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test) +{ + struct drm_modeset_acquire_ctx ctx; + struct drm_atomic_state *state; + struct vc4_crtc_state *new_vc4_crtc_state; + struct vc4_hvs_state *new_hvs_state; + unsigned int old_hdmi0_channel; + unsigned int old_hdmi1_channel; + struct drm_device *drm; + struct vc4_dev *vc4; + int ret; + + vc4 = vc5_mock_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4); + + drm_modeset_acquire_init(&ctx, 0); + + drm = &vc4->base; + state = drm_atomic_state_alloc(drm); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + state->acquire_ctx = &ctx; + + ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = drm_atomic_check_only(state); + KUNIT_ASSERT_EQ(test, ret, 0); + + new_hvs_state = vc4_hvs_get_new_global_state(state); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_hvs_state); + + new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state, + VC4_ENCODER_TYPE_HDMI0); + KUNIT_ASSERT_NOT_NULL(test, new_vc4_crtc_state); + + old_hdmi0_channel = new_vc4_crtc_state->assigned_channel; + KUNIT_ASSERT_NE(test, old_hdmi0_channel, VC4_HVS_CHANNEL_DISABLED); + KUNIT_ASSERT_TRUE(test, new_hvs_state->fifo_state[old_hdmi0_channel].in_use); + + new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state, + VC4_ENCODER_TYPE_HDMI1); + KUNIT_ASSERT_NOT_NULL(test, new_vc4_crtc_state); + + old_hdmi1_channel = new_vc4_crtc_state->assigned_channel; + KUNIT_ASSERT_NE(test, old_hdmi1_channel, VC4_HVS_CHANNEL_DISABLED); + KUNIT_ASSERT_TRUE(test, new_hvs_state->fifo_state[old_hdmi1_channel].in_use); + + ret = drm_atomic_helper_swap_state(state, false); + KUNIT_ASSERT_EQ(test, ret, 0); + + drm_atomic_state_put(state); + + state = drm_atomic_state_alloc(drm); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + state->acquire_ctx = &ctx; + + ret = vc4_mock_atomic_del_output(test, state, VC4_ENCODER_TYPE_HDMI0); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = drm_atomic_check_only(state); + KUNIT_ASSERT_EQ(test, ret, 0); + + new_hvs_state = vc4_hvs_get_new_global_state(state); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_hvs_state); + + new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state, + VC4_ENCODER_TYPE_HDMI1); + + if (new_vc4_crtc_state) { + unsigned int hdmi1_channel; + + hdmi1_channel = new_vc4_crtc_state->assigned_channel; + KUNIT_ASSERT_NE(test, hdmi1_channel, VC4_HVS_CHANNEL_DISABLED); + KUNIT_ASSERT_TRUE(test, new_hvs_state->fifo_state[hdmi1_channel].in_use); + + KUNIT_EXPECT_EQ(test, old_hdmi1_channel, hdmi1_channel); + } + + drm_atomic_state_put(state); + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + drm_dev_unregister(drm); + drm_kunit_helper_free_device(test, vc4->dev); +} + +static void +drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state(struct kunit *test) +{ + struct drm_modeset_acquire_ctx ctx; + struct drm_atomic_state *state; + struct vc4_crtc_state *new_vc4_crtc_state; + struct drm_device *drm; + struct vc4_dev *vc4; + int ret; + + vc4 = vc5_mock_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4); + + drm_modeset_acquire_init(&ctx, 0); + + drm = &vc4->base; + state = drm_atomic_state_alloc(drm); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + state->acquire_ctx = &ctx; + + ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = drm_atomic_check_only(state); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = drm_atomic_helper_swap_state(state, false); + KUNIT_ASSERT_EQ(test, ret, 0); + + drm_atomic_state_put(state); + + state = drm_atomic_state_alloc(drm); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + state->acquire_ctx = &ctx; + + ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = drm_atomic_check_only(state); + KUNIT_ASSERT_EQ(test, ret, 0); + + new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state, + VC4_ENCODER_TYPE_HDMI0); + KUNIT_EXPECT_NULL(test, new_vc4_crtc_state); + + drm_atomic_state_put(state); + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + drm_dev_unregister(drm); + drm_kunit_helper_free_device(test, vc4->dev); +} + +static struct kunit_case vc5_pv_muxing_bugs_tests[] = { + KUNIT_CASE(drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable), + KUNIT_CASE(drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state), + KUNIT_CASE(drm_test_vc5_pv_muxing_bugs_stable_fifo), + {} +}; + +static struct kunit_suite vc5_pv_muxing_bugs_test_suite = { + .name = "vc5-pv-muxing-bugs", + .test_cases = vc5_pv_muxing_bugs_tests, +}; + +kunit_test_suites( + &vc4_pv_muxing_test_suite, + &vc5_pv_muxing_test_suite, + &vc5_pv_muxing_bugs_test_suite +);
On 12/1/22 12:11, Maxime Ripard wrote:
The HVS to PixelValve muxing code is fairly error prone and has a bunch of arbitrary constraints due to the hardware setup.
Let's create a test suite that makes sure that the possible combinations work and the invalid ones don't.
Reviewed-by: Javier Martinez Canillas javierm@redhat.com Signed-off-by: Maxime Ripard maxime@cerno.tech
Reviewed-by: Maíra Canal mcanal@igalia.com
Best Regards, - Maíra Canal
drivers/gpu/drm/vc4/Makefile | 3 +- drivers/gpu/drm/vc4/tests/vc4_mock.h | 9 +- drivers/gpu/drm/vc4/tests/vc4_mock_output.c | 49 +- drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c | 1039 ++++++++++++++++++++++++ 4 files changed, 1091 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile index f974a8c80e2f..c41f89a15a55 100644 --- a/drivers/gpu/drm/vc4/Makefile +++ b/drivers/gpu/drm/vc4/Makefile @@ -29,7 +29,8 @@ 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
- tests/vc4_mock_plane.o \
- tests/vc4_test_pv_muxing.o
vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock.h b/drivers/gpu/drm/vc4/tests/vc4_mock.h index ace5b2e00f4a..db8e9a141ef8 100644 --- a/drivers/gpu/drm/vc4/tests/vc4_mock.h +++ b/drivers/gpu/drm/vc4/tests/vc4_mock.h @@ -53,8 +53,11 @@ struct vc4_dummy_output *vc4_dummy_output(struct kunit *test, 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);
+int vc4_mock_atomic_add_output(struct kunit *test,
struct drm_atomic_state *state,
enum vc4_encoder_type type);
+int vc4_mock_atomic_del_output(struct kunit *test,
struct drm_atomic_state *state,
enum vc4_encoder_type type);
#endif // VC4_MOCK_H_ diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock_output.c b/drivers/gpu/drm/vc4/tests/vc4_mock_output.c index cb16ab4451f7..8d33be828d9a 100644 --- a/drivers/gpu/drm/vc4/tests/vc4_mock_output.c +++ b/drivers/gpu/drm/vc4/tests/vc4_mock_output.c @@ -61,16 +61,17 @@ static const struct drm_display_mode default_mode = { DRM_SIMPLE_MODE(640, 480, 64, 48) }; -int vc4_mock_atomic_add_output(struct kunit *test, struct drm_device *drm,
enum vc4_encoder_type type,
struct drm_atomic_state *state)
+int vc4_mock_atomic_add_output(struct kunit *test,
struct drm_atomic_state *state,
{enum vc4_encoder_type type)
- struct drm_device *drm = state->dev;
- struct drm_connector_state *conn_state;
- struct drm_crtc_state *crtc_state; struct vc4_dummy_output *output; struct drm_connector *conn;
- struct drm_connector_state *conn_state; struct drm_encoder *encoder; struct drm_crtc *crtc;
- struct drm_crtc_state *crtc_state; int ret;
encoder = vc4_find_encoder_by_type(drm, type); @@ -97,3 +98,41 @@ int vc4_mock_atomic_add_output(struct kunit *test, struct drm_device *drm, return 0; }
+int vc4_mock_atomic_del_output(struct kunit *test,
struct drm_atomic_state *state,
enum vc4_encoder_type type)
+{
- struct drm_device *drm = state->dev;
- struct drm_connector_state *conn_state;
- struct drm_crtc_state *crtc_state;
- struct vc4_dummy_output *output;
- struct drm_connector *conn;
- struct drm_encoder *encoder;
- struct drm_crtc *crtc;
- int ret;
- encoder = vc4_find_encoder_by_type(drm, type);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, encoder);
- crtc = vc4_find_crtc_for_encoder(test, drm, encoder);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc);
- crtc_state = drm_atomic_get_crtc_state(state, crtc);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
- crtc_state->active = false;
- ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL);
- KUNIT_ASSERT_EQ(test, ret, 0);
- output = container_of(encoder, struct vc4_dummy_output, encoder.base);
- conn = &output->connector;
- conn_state = drm_atomic_get_connector_state(state, conn);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
- ret = drm_atomic_set_crtc_for_connector(conn_state, NULL);
- KUNIT_ASSERT_EQ(test, ret, 0);
- return 0;
+} diff --git a/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c new file mode 100644 index 000000000000..ae0bd0f81698 --- /dev/null +++ b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c @@ -0,0 +1,1039 @@ +// SPDX-License-Identifier: GPL-2.0
+#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_atomic_uapi.h> +#include <drm/drm_crtc.h> +#include <drm/drm_drv.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_kunit_helpers.h> +#include <drm/drm_mode.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_plane.h>
+#include <kunit/test.h>
+#include "../vc4_drv.h"
+#include "vc4_mock.h"
+struct pv_muxing_priv {
- struct vc4_dev *vc4;
- struct drm_modeset_acquire_ctx ctx;
- struct drm_atomic_state *state;
+};
+static bool check_fifo_conflict(struct kunit *test,
const struct drm_atomic_state *state)
+{
- struct vc4_hvs_state *hvs_state;
- unsigned int used_fifos = 0;
- unsigned int i;
- hvs_state = vc4_hvs_get_new_global_state(state);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hvs_state);
- for (i = 0; i < HVS_NUM_CHANNELS; i++) {
if (!hvs_state->fifo_state[i].in_use)
continue;
KUNIT_EXPECT_FALSE(test, used_fifos & BIT(i));
used_fifos |= BIT(i);
- }
- return true;
+}
+struct encoder_constraint {
- enum vc4_encoder_type type;
- unsigned int *channels;
- size_t nchannels;
+};
+#define ENCODER_CONSTRAINT(_type, ...) \
- { \
.type = _type, \
.channels = (unsigned int[]) { __VA_ARGS__ }, \
.nchannels = sizeof((unsigned int[]) { __VA_ARGS__ }) / \
sizeof(unsigned int), \
- }
+static bool __check_encoder_constraints(const struct encoder_constraint *constraints,
size_t nconstraints,
enum vc4_encoder_type type,
unsigned int channel)
+{
- unsigned int i;
- for (i = 0; i < nconstraints; i++) {
const struct encoder_constraint *constraint = &constraints[i];
unsigned int j;
if (constraint->type != type)
continue;
for (j = 0; j < constraint->nchannels; j++) {
unsigned int _channel = constraint->channels[j];
if (channel != _channel)
continue;
return true;
}
- }
- return false;
+}
+static const struct encoder_constraint vc4_encoder_constraints[] = {
- ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DPI, 0),
- ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DSI0, 0),
- ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI0, 1),
- ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_VEC, 1),
- ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_TXP, 2),
- ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DSI1, 2),
+};
+static const struct encoder_constraint vc5_encoder_constraints[] = {
- ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DPI, 0),
- ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DSI0, 0),
- ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_VEC, 1),
- ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_TXP, 0, 2),
- ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DSI1, 0, 1, 2),
- ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI0, 0, 1, 2),
- ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI1, 0, 1, 2),
+};
+static bool check_vc4_encoder_constraints(enum vc4_encoder_type type, unsigned int channel) +{
- return __check_encoder_constraints(vc4_encoder_constraints,
ARRAY_SIZE(vc4_encoder_constraints),
type, channel);
+}
+static bool check_vc5_encoder_constraints(enum vc4_encoder_type type, unsigned int channel) +{
- return __check_encoder_constraints(vc5_encoder_constraints,
ARRAY_SIZE(vc5_encoder_constraints),
type, channel);
+}
+static struct vc4_crtc_state * +get_vc4_crtc_state_for_encoder(struct kunit *test,
const struct drm_atomic_state *state,
enum vc4_encoder_type type)
+{
- struct drm_device *drm = state->dev;
- struct drm_crtc_state *new_crtc_state;
- struct drm_encoder *encoder;
- struct drm_crtc *crtc;
- encoder = vc4_find_encoder_by_type(drm, type);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, encoder);
- crtc = vc4_find_crtc_for_encoder(test, drm, encoder);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc);
- new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
- if (!new_crtc_state)
return NULL;
- return to_vc4_crtc_state(new_crtc_state);
+}
+static bool check_channel_for_encoder(struct kunit *test,
const struct drm_atomic_state *state,
enum vc4_encoder_type type,
bool (*check_fn)(enum vc4_encoder_type type, unsigned int channel))
+{
- struct vc4_crtc_state *new_vc4_crtc_state;
- struct vc4_hvs_state *new_hvs_state;
- unsigned int channel;
- new_hvs_state = vc4_hvs_get_new_global_state(state);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_hvs_state);
- new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state, type);
- KUNIT_ASSERT_NOT_NULL(test, new_vc4_crtc_state);
- channel = new_vc4_crtc_state->assigned_channel;
- KUNIT_EXPECT_NE(test, channel, VC4_HVS_CHANNEL_DISABLED);
- KUNIT_EXPECT_TRUE(test, new_hvs_state->fifo_state[channel].in_use);
- KUNIT_EXPECT_TRUE(test, check_fn(type, channel));
- return true;
+}
+struct pv_muxing_param {
- const char *name;
- struct vc4_dev *(*mock_fn)(struct kunit *test);
- bool (*check_fn)(enum vc4_encoder_type type, unsigned int channel);
- enum vc4_encoder_type *encoders;
- size_t nencoders;
+};
+static void vc4_test_pv_muxing_desc(const struct pv_muxing_param *t, char *desc) +{
- strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
+}
+#define PV_MUXING_TEST(_name, _mock_fn, _check_fn, ...) \
- { \
.name = _name, \
.mock_fn = &_mock_fn, \
.check_fn = &_check_fn, \
.encoders = (enum vc4_encoder_type[]) { __VA_ARGS__ }, \
.nencoders = sizeof((enum vc4_encoder_type[]) { __VA_ARGS__ }) / \
sizeof(enum vc4_encoder_type), \
- }
+#define VC4_PV_MUXING_TEST(_name, ...) \
- PV_MUXING_TEST(_name, vc4_mock_device, check_vc4_encoder_constraints, __VA_ARGS__)
+#define VC5_PV_MUXING_TEST(_name, ...) \
- PV_MUXING_TEST(_name, vc5_mock_device, check_vc5_encoder_constraints, __VA_ARGS__)
+static const struct pv_muxing_param vc4_test_pv_muxing_params[] = {
- VC4_PV_MUXING_TEST("1 output: DSI0",
VC4_ENCODER_TYPE_DSI0),
- VC4_PV_MUXING_TEST("1 output: DPI",
VC4_ENCODER_TYPE_DPI),
- VC4_PV_MUXING_TEST("1 output: HDMI0",
VC4_ENCODER_TYPE_HDMI0),
- VC4_PV_MUXING_TEST("1 output: VEC",
VC4_ENCODER_TYPE_VEC),
- VC4_PV_MUXING_TEST("1 output: DSI1",
VC4_ENCODER_TYPE_DSI1),
- VC4_PV_MUXING_TEST("1 output: TXP",
VC4_ENCODER_TYPE_TXP),
- VC4_PV_MUXING_TEST("2 outputs: DSI0, HDMI0",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_HDMI0),
- VC4_PV_MUXING_TEST("2 outputs: DSI0, VEC",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_VEC),
- VC4_PV_MUXING_TEST("2 outputs: DSI0, DSI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_DSI1),
- VC4_PV_MUXING_TEST("2 outputs: DSI0, TXP",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_TXP),
- VC4_PV_MUXING_TEST("2 outputs: DPI, HDMI0",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_HDMI0),
- VC4_PV_MUXING_TEST("2 outputs: DPI, VEC",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_VEC),
- VC4_PV_MUXING_TEST("2 outputs: DPI, DSI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_DSI1),
- VC4_PV_MUXING_TEST("2 outputs: DPI, TXP",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_TXP),
- VC4_PV_MUXING_TEST("2 outputs: HDMI0, DSI1",
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_DSI1),
- VC4_PV_MUXING_TEST("2 outputs: HDMI0, TXP",
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_TXP),
- VC4_PV_MUXING_TEST("2 outputs: VEC, DSI1",
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_DSI1),
- VC4_PV_MUXING_TEST("2 outputs: VEC, TXP",
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_TXP),
- VC4_PV_MUXING_TEST("3 outputs: DSI0, HDMI0, DSI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_DSI1),
- VC4_PV_MUXING_TEST("3 outputs: DSI0, HDMI0, TXP",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_TXP),
- VC4_PV_MUXING_TEST("3 outputs: DSI0, VEC, DSI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_DSI1),
- VC4_PV_MUXING_TEST("3 outputs: DSI0, VEC, TXP",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_TXP),
- VC4_PV_MUXING_TEST("3 outputs: DPI, HDMI0, DSI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_DSI1),
- VC4_PV_MUXING_TEST("3 outputs: DPI, HDMI0, TXP",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_TXP),
- VC4_PV_MUXING_TEST("3 outputs: DPI, VEC, DSI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_DSI1),
- VC4_PV_MUXING_TEST("3 outputs: DPI, VEC, TXP",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_TXP),
+};
+KUNIT_ARRAY_PARAM(vc4_test_pv_muxing,
vc4_test_pv_muxing_params,
vc4_test_pv_muxing_desc);
+static const struct pv_muxing_param vc4_test_pv_muxing_invalid_params[] = {
- VC4_PV_MUXING_TEST("DPI/DSI0 Conflict",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_DSI0),
- VC4_PV_MUXING_TEST("TXP/DSI1 Conflict",
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_DSI1),
- VC4_PV_MUXING_TEST("HDMI0/VEC Conflict",
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_VEC),
- VC4_PV_MUXING_TEST("More than 3 outputs: DSI0, HDMI0, DSI1, TXP",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_TXP),
- VC4_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, DSI1, TXP",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_TXP),
- VC4_PV_MUXING_TEST("More than 3 outputs: DPI, HDMI0, DSI1, TXP",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_TXP),
- VC4_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, DSI1, TXP",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_TXP),
+};
+KUNIT_ARRAY_PARAM(vc4_test_pv_muxing_invalid,
vc4_test_pv_muxing_invalid_params,
vc4_test_pv_muxing_desc);
+static const struct pv_muxing_param vc5_test_pv_muxing_params[] = {
- VC5_PV_MUXING_TEST("1 output: DPI",
VC4_ENCODER_TYPE_DPI),
- VC5_PV_MUXING_TEST("1 output: DSI0",
VC4_ENCODER_TYPE_DSI0),
- VC5_PV_MUXING_TEST("1 output: DSI1",
VC4_ENCODER_TYPE_DSI1),
- VC5_PV_MUXING_TEST("1 output: HDMI0",
VC4_ENCODER_TYPE_HDMI0),
- VC5_PV_MUXING_TEST("1 output: HDMI1",
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("1 output: VEC",
VC4_ENCODER_TYPE_VEC),
- VC5_PV_MUXING_TEST("2 outputs: DPI, DSI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_DSI1),
- VC5_PV_MUXING_TEST("2 outputs: DPI, HDMI0",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_HDMI0),
- VC5_PV_MUXING_TEST("2 outputs: DPI, HDMI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("2 outputs: DPI, TXP",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_TXP),
- VC5_PV_MUXING_TEST("2 outputs: DPI, VEC",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_VEC),
- VC5_PV_MUXING_TEST("2 outputs: DPI, DSI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_DSI1),
- VC5_PV_MUXING_TEST("2 outputs: DSI0, DSI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_DSI1),
- VC5_PV_MUXING_TEST("2 outputs: DSI0, HDMI0",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_HDMI0),
- VC5_PV_MUXING_TEST("2 outputs: DSI0, HDMI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("2 outputs: DSI0, TXP",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_TXP),
- VC5_PV_MUXING_TEST("2 outputs: DSI0, VEC",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_VEC),
- VC5_PV_MUXING_TEST("2 outputs: DSI0, DSI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_DSI1),
- VC5_PV_MUXING_TEST("2 outputs: DSI1, VEC",
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_VEC),
- VC5_PV_MUXING_TEST("2 outputs: DSI1, TXP",
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_TXP),
- VC5_PV_MUXING_TEST("2 outputs: DSI1, HDMI0",
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI0),
- VC5_PV_MUXING_TEST("2 outputs: DSI1, HDMI1",
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("2 outputs: HDMI0, VEC",
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_VEC),
- VC5_PV_MUXING_TEST("2 outputs: HDMI0, TXP",
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_TXP),
- VC5_PV_MUXING_TEST("2 outputs: HDMI0, HDMI1",
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("2 outputs: HDMI1, VEC",
VC4_ENCODER_TYPE_HDMI1,
VC4_ENCODER_TYPE_VEC),
- VC5_PV_MUXING_TEST("2 outputs: HDMI1, TXP",
VC4_ENCODER_TYPE_HDMI1,
VC4_ENCODER_TYPE_TXP),
- VC5_PV_MUXING_TEST("2 outputs: TXP, VEC",
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_VEC),
- VC5_PV_MUXING_TEST("3 outputs: DPI, VEC, TXP",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_TXP),
- VC5_PV_MUXING_TEST("3 outputs: DPI, VEC, DSI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_DSI1),
- VC5_PV_MUXING_TEST("3 outputs: DPI, VEC, HDMI0",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_HDMI0),
- VC5_PV_MUXING_TEST("3 outputs: DPI, VEC, HDMI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("3 outputs: DPI, TXP, DSI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_DSI1),
- VC5_PV_MUXING_TEST("3 outputs: DPI, TXP, HDMI0",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_HDMI0),
- VC5_PV_MUXING_TEST("3 outputs: DPI, TXP, HDMI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("3 outputs: DPI, DSI1, HDMI0",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI0),
- VC5_PV_MUXING_TEST("3 outputs: DPI, DSI1, HDMI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("3 outputs: DPI, HDMI0, HDMI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("3 outputs: DSI0, VEC, TXP",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_TXP),
- VC5_PV_MUXING_TEST("3 outputs: DSI0, VEC, DSI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_DSI1),
- VC5_PV_MUXING_TEST("3 outputs: DSI0, VEC, HDMI0",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_HDMI0),
- VC5_PV_MUXING_TEST("3 outputs: DSI0, VEC, HDMI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("3 outputs: DSI0, TXP, DSI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_DSI1),
- VC5_PV_MUXING_TEST("3 outputs: DSI0, TXP, HDMI0",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_HDMI0),
- VC5_PV_MUXING_TEST("3 outputs: DSI0, TXP, HDMI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("3 outputs: DSI0, DSI1, HDMI0",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI0),
- VC5_PV_MUXING_TEST("3 outputs: DSI0, DSI1, HDMI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("3 outputs: DSI0, HDMI0, HDMI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_HDMI1),
+};
+KUNIT_ARRAY_PARAM(vc5_test_pv_muxing,
vc5_test_pv_muxing_params,
vc4_test_pv_muxing_desc);
+static const struct pv_muxing_param vc5_test_pv_muxing_invalid_params[] = {
- VC5_PV_MUXING_TEST("DPI/DSI0 Conflict",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_DSI0),
- VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, DSI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_DSI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, HDMI0",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_HDMI0),
- VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, HDMI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, DSI1, HDMI0",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI0),
- VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, DSI1, HDMI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, HDMI0, HDMI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DPI, TXP, DSI1, HDMI0",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI0),
- VC5_PV_MUXING_TEST("More than 3 outputs: DPI, TXP, DSI1, HDMI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DPI, TXP, HDMI0, HDMI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DPI, DSI1, HDMI0, HDMI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, DSI1, HDMI0",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI0),
- VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, DSI1, HDMI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, HDMI0, HDMI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, DSI1, HDMI0, HDMI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DPI, TXP, DSI1, HDMI0, HDMI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, DSI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_DSI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, HDMI0",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_HDMI0),
- VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, HDMI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, DSI1, HDMI0",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI0),
- VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, DSI1, HDMI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, HDMI0, HDMI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, TXP, DSI1, HDMI0",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI0),
- VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, TXP, DSI1, HDMI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, TXP, HDMI0, HDMI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, DSI1, HDMI0, HDMI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, DSI1, HDMI0",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI0),
- VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, DSI1, HDMI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, HDMI0, HDMI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, DSI1, HDMI0, HDMI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, TXP, DSI1, HDMI0, HDMI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: VEC, TXP, DSI1, HDMI0, HDMI1",
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, DSI1, HDMI0, HDMI1",
VC4_ENCODER_TYPE_DPI,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_HDMI1),
- VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, DSI1, HDMI0, HDMI1",
VC4_ENCODER_TYPE_DSI0,
VC4_ENCODER_TYPE_VEC,
VC4_ENCODER_TYPE_TXP,
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_HDMI0,
VC4_ENCODER_TYPE_HDMI1),
+};
+KUNIT_ARRAY_PARAM(vc5_test_pv_muxing_invalid,
vc5_test_pv_muxing_invalid_params,
vc4_test_pv_muxing_desc);
+static void drm_vc4_test_pv_muxing(struct kunit *test) +{
- const struct pv_muxing_param *params = test->param_value;
- const struct pv_muxing_priv *priv = test->priv;
- struct drm_atomic_state *state = priv->state;
- unsigned int i;
- int ret;
- for (i = 0; i < params->nencoders; i++) {
enum vc4_encoder_type enc_type = params->encoders[i];
ret = vc4_mock_atomic_add_output(test, state, enc_type);
KUNIT_ASSERT_EQ(test, ret, 0);
- }
- ret = drm_atomic_check_only(state);
- KUNIT_EXPECT_EQ(test, ret, 0);
- KUNIT_EXPECT_TRUE(test,
check_fifo_conflict(test, state));
- for (i = 0; i < params->nencoders; i++) {
enum vc4_encoder_type enc_type = params->encoders[i];
KUNIT_EXPECT_TRUE(test, check_channel_for_encoder(test, state, enc_type,
params->check_fn));
- }
+}
+static void drm_vc4_test_pv_muxing_invalid(struct kunit *test) +{
- const struct pv_muxing_param *params = test->param_value;
- const struct pv_muxing_priv *priv = test->priv;
- struct drm_atomic_state *state = priv->state;
- unsigned int i;
- int ret;
- for (i = 0; i < params->nencoders; i++) {
enum vc4_encoder_type enc_type = params->encoders[i];
ret = vc4_mock_atomic_add_output(test, state, enc_type);
KUNIT_ASSERT_EQ(test, ret, 0);
- }
- ret = drm_atomic_check_only(state);
- KUNIT_EXPECT_LT(test, ret, 0);
+}
+static int vc4_pv_muxing_test_init(struct kunit *test) +{
- const struct pv_muxing_param *params = test->param_value;
- struct drm_atomic_state *state;
- struct pv_muxing_priv *priv;
- struct drm_device *drm;
- struct vc4_dev *vc4;
- priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
- KUNIT_ASSERT_NOT_NULL(test, priv);
- test->priv = priv;
- vc4 = params->mock_fn(test);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
- priv->vc4 = vc4;
- drm_modeset_acquire_init(&priv->ctx, 0);
- drm = &vc4->base;
- state = drm_atomic_state_alloc(drm);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
- state->acquire_ctx = &priv->ctx;
- priv->state = state;
- return 0;
+}
+static void vc4_pv_muxing_test_exit(struct kunit *test) +{
- struct pv_muxing_priv *priv = test->priv;
- struct vc4_dev *vc4 = priv->vc4;
- struct drm_device *drm = &vc4->base;
- struct drm_atomic_state *state = priv->state;
- drm_atomic_state_put(state);
- drm_modeset_drop_locks(&priv->ctx);
- drm_modeset_acquire_fini(&priv->ctx);
- drm_dev_unregister(drm);
- drm_kunit_helper_free_device(test, vc4->dev);
+}
+static struct kunit_case vc4_pv_muxing_tests[] = {
- KUNIT_CASE_PARAM(drm_vc4_test_pv_muxing,
vc4_test_pv_muxing_gen_params),
- KUNIT_CASE_PARAM(drm_vc4_test_pv_muxing_invalid,
vc4_test_pv_muxing_invalid_gen_params),
- {}
+};
+static struct kunit_suite vc4_pv_muxing_test_suite = {
- .name = "vc4-pv-muxing-combinations",
- .init = vc4_pv_muxing_test_init,
- .exit = vc4_pv_muxing_test_exit,
- .test_cases = vc4_pv_muxing_tests,
+};
+static struct kunit_case vc5_pv_muxing_tests[] = {
- KUNIT_CASE_PARAM(drm_vc4_test_pv_muxing,
vc5_test_pv_muxing_gen_params),
- KUNIT_CASE_PARAM(drm_vc4_test_pv_muxing_invalid,
vc5_test_pv_muxing_invalid_gen_params),
- {}
+};
+static struct kunit_suite vc5_pv_muxing_test_suite = {
- .name = "vc5-pv-muxing-combinations",
- .init = vc4_pv_muxing_test_init,
- .exit = vc4_pv_muxing_test_exit,
- .test_cases = vc5_pv_muxing_tests,
+};
+/* See
- and
- */
+static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *test) +{
- struct drm_modeset_acquire_ctx ctx;
- struct drm_atomic_state *state;
- struct vc4_crtc_state *new_vc4_crtc_state;
- struct vc4_hvs_state *new_hvs_state;
- unsigned int hdmi0_channel;
- unsigned int hdmi1_channel;
- struct drm_device *drm;
- struct vc4_dev *vc4;
- int ret;
- vc4 = vc5_mock_device(test);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
- drm_modeset_acquire_init(&ctx, 0);
- drm = &vc4->base;
- state = drm_atomic_state_alloc(drm);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
- state->acquire_ctx = &ctx;
- ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
- KUNIT_ASSERT_EQ(test, ret, 0);
- ret = drm_atomic_check_only(state);
- KUNIT_ASSERT_EQ(test, ret, 0);
- new_hvs_state = vc4_hvs_get_new_global_state(state);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_hvs_state);
- new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state,
VC4_ENCODER_TYPE_HDMI0);
- KUNIT_ASSERT_NOT_NULL(test, new_vc4_crtc_state);
- hdmi0_channel = new_vc4_crtc_state->assigned_channel;
- KUNIT_ASSERT_NE(test, hdmi0_channel, VC4_HVS_CHANNEL_DISABLED);
- KUNIT_ASSERT_TRUE(test, new_hvs_state->fifo_state[hdmi0_channel].in_use);
- ret = drm_atomic_helper_swap_state(state, false);
- KUNIT_ASSERT_EQ(test, ret, 0);
- drm_atomic_state_put(state);
- state = drm_atomic_state_alloc(drm);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
- state->acquire_ctx = &ctx;
- ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
- KUNIT_ASSERT_EQ(test, ret, 0);
- ret = drm_atomic_check_only(state);
- KUNIT_ASSERT_EQ(test, ret, 0);
- new_hvs_state = vc4_hvs_get_new_global_state(state);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_hvs_state);
- new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state,
VC4_ENCODER_TYPE_HDMI1);
- KUNIT_ASSERT_NOT_NULL(test, new_vc4_crtc_state);
- hdmi1_channel = new_vc4_crtc_state->assigned_channel;
- KUNIT_ASSERT_NE(test, hdmi1_channel, VC4_HVS_CHANNEL_DISABLED);
- KUNIT_ASSERT_TRUE(test, new_hvs_state->fifo_state[hdmi1_channel].in_use);
- KUNIT_EXPECT_NE(test, hdmi0_channel, hdmi1_channel);
- drm_atomic_state_put(state);
- drm_modeset_drop_locks(&ctx);
- drm_modeset_acquire_fini(&ctx);
- drm_dev_unregister(drm);
- drm_kunit_helper_free_device(test, vc4->dev);
+}
+static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test) +{
- struct drm_modeset_acquire_ctx ctx;
- struct drm_atomic_state *state;
- struct vc4_crtc_state *new_vc4_crtc_state;
- struct vc4_hvs_state *new_hvs_state;
- unsigned int old_hdmi0_channel;
- unsigned int old_hdmi1_channel;
- struct drm_device *drm;
- struct vc4_dev *vc4;
- int ret;
- vc4 = vc5_mock_device(test);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
- drm_modeset_acquire_init(&ctx, 0);
- drm = &vc4->base;
- state = drm_atomic_state_alloc(drm);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
- state->acquire_ctx = &ctx;
- ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
- KUNIT_ASSERT_EQ(test, ret, 0);
- ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
- KUNIT_ASSERT_EQ(test, ret, 0);
- ret = drm_atomic_check_only(state);
- KUNIT_ASSERT_EQ(test, ret, 0);
- new_hvs_state = vc4_hvs_get_new_global_state(state);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_hvs_state);
- new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state,
VC4_ENCODER_TYPE_HDMI0);
- KUNIT_ASSERT_NOT_NULL(test, new_vc4_crtc_state);
- old_hdmi0_channel = new_vc4_crtc_state->assigned_channel;
- KUNIT_ASSERT_NE(test, old_hdmi0_channel, VC4_HVS_CHANNEL_DISABLED);
- KUNIT_ASSERT_TRUE(test, new_hvs_state->fifo_state[old_hdmi0_channel].in_use);
- new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state,
VC4_ENCODER_TYPE_HDMI1);
- KUNIT_ASSERT_NOT_NULL(test, new_vc4_crtc_state);
- old_hdmi1_channel = new_vc4_crtc_state->assigned_channel;
- KUNIT_ASSERT_NE(test, old_hdmi1_channel, VC4_HVS_CHANNEL_DISABLED);
- KUNIT_ASSERT_TRUE(test, new_hvs_state->fifo_state[old_hdmi1_channel].in_use);
- ret = drm_atomic_helper_swap_state(state, false);
- KUNIT_ASSERT_EQ(test, ret, 0);
- drm_atomic_state_put(state);
- state = drm_atomic_state_alloc(drm);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
- state->acquire_ctx = &ctx;
- ret = vc4_mock_atomic_del_output(test, state, VC4_ENCODER_TYPE_HDMI0);
- KUNIT_ASSERT_EQ(test, ret, 0);
- ret = drm_atomic_check_only(state);
- KUNIT_ASSERT_EQ(test, ret, 0);
- new_hvs_state = vc4_hvs_get_new_global_state(state);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_hvs_state);
- new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state,
VC4_ENCODER_TYPE_HDMI1);
- if (new_vc4_crtc_state) {
unsigned int hdmi1_channel;
hdmi1_channel = new_vc4_crtc_state->assigned_channel;
KUNIT_ASSERT_NE(test, hdmi1_channel, VC4_HVS_CHANNEL_DISABLED);
KUNIT_ASSERT_TRUE(test, new_hvs_state->fifo_state[hdmi1_channel].in_use);
KUNIT_EXPECT_EQ(test, old_hdmi1_channel, hdmi1_channel);
- }
- drm_atomic_state_put(state);
- drm_modeset_drop_locks(&ctx);
- drm_modeset_acquire_fini(&ctx);
- drm_dev_unregister(drm);
- drm_kunit_helper_free_device(test, vc4->dev);
+}
+static void +drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state(struct kunit *test) +{
- struct drm_modeset_acquire_ctx ctx;
- struct drm_atomic_state *state;
- struct vc4_crtc_state *new_vc4_crtc_state;
- struct drm_device *drm;
- struct vc4_dev *vc4;
- int ret;
- vc4 = vc5_mock_device(test);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
- drm_modeset_acquire_init(&ctx, 0);
- drm = &vc4->base;
- state = drm_atomic_state_alloc(drm);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
- state->acquire_ctx = &ctx;
- ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
- KUNIT_ASSERT_EQ(test, ret, 0);
- ret = drm_atomic_check_only(state);
- KUNIT_ASSERT_EQ(test, ret, 0);
- ret = drm_atomic_helper_swap_state(state, false);
- KUNIT_ASSERT_EQ(test, ret, 0);
- drm_atomic_state_put(state);
- state = drm_atomic_state_alloc(drm);
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
- state->acquire_ctx = &ctx;
- ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
- KUNIT_ASSERT_EQ(test, ret, 0);
- ret = drm_atomic_check_only(state);
- KUNIT_ASSERT_EQ(test, ret, 0);
- new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state,
VC4_ENCODER_TYPE_HDMI0);
- KUNIT_EXPECT_NULL(test, new_vc4_crtc_state);
- drm_atomic_state_put(state);
- drm_modeset_drop_locks(&ctx);
- drm_modeset_acquire_fini(&ctx);
- drm_dev_unregister(drm);
- drm_kunit_helper_free_device(test, vc4->dev);
+}
+static struct kunit_case vc5_pv_muxing_bugs_tests[] = {
- KUNIT_CASE(drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable),
- KUNIT_CASE(drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state),
- KUNIT_CASE(drm_test_vc5_pv_muxing_bugs_stable_fifo),
- {}
+};
+static struct kunit_suite vc5_pv_muxing_bugs_test_suite = {
- .name = "vc5-pv-muxing-bugs",
- .test_cases = vc5_pv_muxing_bugs_tests,
+};
+kunit_test_suites(
- &vc4_pv_muxing_test_suite,
- &vc5_pv_muxing_test_suite,
- &vc5_pv_muxing_bugs_test_suite
+);
Now that we have VC4-specific tests in place, let's document them properly.
Signed-off-by: Maxime Ripard maxime@cerno.tech --- Documentation/gpu/vc4.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
diff --git a/Documentation/gpu/vc4.rst b/Documentation/gpu/vc4.rst index 5df1d98b9544..a2375f1584e6 100644 --- a/Documentation/gpu/vc4.rst +++ b/Documentation/gpu/vc4.rst @@ -54,6 +54,22 @@ VEC (Composite TV out) encoder .. kernel-doc:: drivers/gpu/drm/vc4/vc4_vec.c :doc: VC4 SDTV module
+KUnit Tests +=========== + +The VC4 Driver uses KUnit to perform driver-specific unit and +integration tests. + +These tests are using a mock driver and can be ran using the +command:: + ./tools/testing/kunit/kunit.py run \ + --kunitconfig=drivers/gpu/drm/vc4/tests/.kunitconfig \ + --cross_compile aarch64-linux-gnu- --arch arm64 + +Parts of the driver that are currently covered by tests are: + * The HVS to PixelValve dynamic FIFO assignment, for the BCM2835-7 + and BCM2711. + Memory Management and 3D Command Submission ===========================================
On 12/1/22 12:11, Maxime Ripard wrote:
Now that we have VC4-specific tests in place, let's document them properly.
Signed-off-by: Maxime Ripard maxime@cerno.tech
Reviewed-by: Maíra Canal mcanal@igalia.com
Just a small nit below.
Documentation/gpu/vc4.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
diff --git a/Documentation/gpu/vc4.rst b/Documentation/gpu/vc4.rst index 5df1d98b9544..a2375f1584e6 100644 --- a/Documentation/gpu/vc4.rst +++ b/Documentation/gpu/vc4.rst @@ -54,6 +54,22 @@ VEC (Composite TV out) encoder .. kernel-doc:: drivers/gpu/drm/vc4/vc4_vec.c :doc: VC4 SDTV module +KUnit Tests +===========
+The VC4 Driver uses KUnit to perform driver-specific unit and +integration tests.
+These tests are using a mock driver and can be ran using the +command::
- ./tools/testing/kunit/kunit.py run \
--kunitconfig=drivers/gpu/drm/vc4/tests/.kunitconfig \
--cross_compile aarch64-linux-gnu- --arch arm64
I believe you could use a code block here, like:
.. code-block:: bash $ ./tools/testing/kunit/kunit.py run \ --kunitconfig=drivers/gpu/drm/vc4/tests/.kunitconfig \ --cross_compile aarch64-linux-gnu- --arch arm64
You could also mention that you can run the tests with --arch arm.
Best Regards, - Maíra Canal
+Parts of the driver that are currently covered by tests are:
- The HVS to PixelValve dynamic FIFO assignment, for the BCM2835-7
- and BCM2711.
Memory Management and 3D Command Submission
On Thu, 01 Dec 2022 16:11:31 +0100, Maxime Ripard wrote:
This series introduce Kunit tests to the vc4 KMS driver, but unlike what we have been doing so far in KMS, it actually tests the atomic modesetting code.
In order to do so, I've had to improve a fair bit on the Kunit helpers already found in the tree in order to register a full blown and somewhat functional KMS driver.
[...]
Applied to drm/drm-misc (drm-misc-next).
Thanks! Maxime
linaro-mm-sig@lists.linaro.org