[PATCH 00/24] drm: Introduce Kunit Tests to VC4

9 views
Skip to first unread message

Maxime Ripard

unread,
Nov 23, 2022, 10:28:40 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
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.

Let me know what you think,
Maxime

To: David Airlie <air...@gmail.com>
To: Daniel Vetter <dan...@ffwll.ch>
To: Maarten Lankhorst <maarten....@linux.intel.com>
To: Maxime Ripard <mri...@kernel.org>
To: Thomas Zimmermann <tzimm...@suse.de>
Cc: Dave Stevenson <dave.st...@raspberrypi.com>
Cc: Javier Martinez Canillas <jav...@redhat.com>
Cc: Greg Kroah-Hartman <gre...@linuxfoundation.org>
Cc: Maíra Canal <maira...@riseup.net>
Cc: Brendan Higgins <brendan...@linux.dev>
Cc: David Gow <davi...@google.com>
Cc: linux-k...@vger.kernel.org
Cc: kuni...@googlegroups.com
Cc: dri-...@lists.freedesktop.org
Cc: linux-...@vger.kernel.org
Cc: linux...@vger.kernel.org
Cc: linaro...@lists.linaro.org
Signed-off-by: Maxime Ripard <max...@cerno.tech>

---
Maxime Ripard (24):
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: kunit: 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/atomic: Constify the old/new state accessors
drm/vc4: kms: Sort the CRTCs by output before assigning them
drm/vc4: Constify container_of wrappers
drm/vc4: Move HVS state to main header
drm/vc4: kms: Constify the HVS old/new state helpers
drm/vc4: txp: Reorder the variable assignments
drm/vc4: Add TXP encoder type
drm/vc4: txp: Initialise the CRTC before the encoder and connector
drm/vc4: crtc: Pass the device and data in vc4_crtc_init
drm/vc4: crtc: Introduce a lower-level crtc init helper
drm/vc4: crtc: Make encoder lookup helper public
drm/vc4: crtc: Provide a CRTC name
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

drivers/gpu/drm/drm_atomic.c | 12 +-
drivers/gpu/drm/tests/Makefile | 1 +
drivers/gpu/drm/tests/drm_client_modeset_test.c | 16 +-
drivers/gpu/drm/tests/drm_kunit_helpers.c | 116 +++--
drivers/gpu/drm/tests/drm_kunit_helpers.h | 39 +-
drivers/gpu/drm/tests/drm_managed_test.c | 68 +++
drivers/gpu/drm/vc4/Kconfig | 15 +
drivers/gpu/drm/vc4/Makefile | 1 +
drivers/gpu/drm/vc4/tests/.kunitconfig | 14 +
drivers/gpu/drm/vc4/tests/Makefile | 8 +
drivers/gpu/drm/vc4/tests/vc4_mock.c | 174 +++++++
drivers/gpu/drm/vc4/tests/vc4_mock.h | 58 +++
drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c | 39 ++
drivers/gpu/drm/vc4/tests/vc4_mock_output.c | 97 ++++
drivers/gpu/drm/vc4/tests/vc4_mock_plane.c | 45 ++
drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c | 624 ++++++++++++++++++++++++
drivers/gpu/drm/vc4/vc4_crtc.c | 119 +++--
drivers/gpu/drm/vc4/vc4_dpi.c | 13 +-
drivers/gpu/drm/vc4/vc4_drv.c | 4 +-
drivers/gpu/drm/vc4/vc4_drv.h | 113 ++++-
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 | 138 +++---
drivers/gpu/drm/vc4/vc4_txp.c | 66 ++-
drivers/gpu/drm/vc4/vc4_vec.c | 13 +-
include/drm/drm_atomic.h | 32 +-
27 files changed, 1678 insertions(+), 241 deletions(-)
---
base-commit: 35c3a2d02f0dc153a5f2f304ba33e1436b6a8d8f
change-id: 20221123-rpi-kunit-tests-87a388492a73

Best regards,
--
Maxime Ripard <max...@cerno.tech>

Maxime Ripard

unread,
Nov 23, 2022, 10:28:44 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
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.

Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/tests/drm_client_modeset_test.c | 2 +-
drivers/gpu/drm/tests/drm_kunit_helpers.c | 4 +++-
drivers/gpu/drm/tests/drm_kunit_helpers.h | 5 ++++-
3 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c b/drivers/gpu/drm/tests/drm_client_modeset_test.c
index 362a5fbd82f5..e469d1634e2d 100644
--- a/drivers/gpu/drm/tests/drm_client_modeset_test.c
+++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c
@@ -41,7 +41,7 @@ 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 f1662091f250..a4ad030ed101 100644
--- a/drivers/gpu/drm/tests/drm_kunit_helpers.c
+++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c
@@ -36,7 +36,9 @@ static void dev_free(struct kunit_resource *res)
root_device_unregister(dev);
}

-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;
diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.h b/drivers/gpu/drm/tests/drm_kunit_helpers.h
index 20ab6eec4c89..e9870c7911fe 100644
--- a/drivers/gpu/drm/tests/drm_kunit_helpers.h
+++ b/drivers/gpu/drm/tests/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_

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 23, 2022, 10:28:49 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
The device name isn't really useful, we can just define it instead of
exposing it in the API.

Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/tests/drm_client_modeset_test.c | 2 +-
drivers/gpu/drm/tests/drm_kunit_helpers.c | 6 ++++--
drivers/gpu/drm/tests/drm_kunit_helpers.h | 3 +--
3 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c b/drivers/gpu/drm/tests/drm_client_modeset_test.c
index e469d1634e2d..6920c3ffdfdf 100644
--- a/drivers/gpu/drm/tests/drm_client_modeset_test.c
+++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c
@@ -41,7 +41,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 a4ad030ed101..7f69f56f5892 100644
--- a/drivers/gpu/drm/tests/drm_kunit_helpers.c
+++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c
@@ -9,6 +9,8 @@

#include "drm_kunit_helpers.h"

+#define FAKE_DEVICE_NAME "drm-kunit-fake-device"
+
struct kunit_dev {
struct drm_device base;
};
@@ -38,7 +40,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;
@@ -46,7 +48,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, FAKE_DEVICE_NAME);
if (!dev)
return ERR_PTR(-ENOMEM);

diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.h b/drivers/gpu/drm/tests/drm_kunit_helpers.h
index e9870c7911fe..6c12b1426ba0 100644
--- a/drivers/gpu/drm/tests/drm_kunit_helpers.h
+++ b/drivers/gpu/drm/tests/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);

Maxime Ripard

unread,
Nov 23, 2022, 10:28:55 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
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.

Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/tests/drm_client_modeset_test.c | 14 +++++++++++++-
drivers/gpu/drm/tests/drm_kunit_helpers.c | 23 ++++-------------------
drivers/gpu/drm/tests/drm_kunit_helpers.h | 5 ++++-
3 files changed, 21 insertions(+), 21 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c b/drivers/gpu/drm/tests/drm_client_modeset_test.c
index 6920c3ffdfdf..fe95fda350ae 100644
--- a/drivers/gpu/drm/tests/drm_client_modeset_test.c
+++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c
@@ -16,6 +16,7 @@

struct drm_client_modeset_test_priv {
struct drm_device *drm;
+ struct device *dev;
struct drm_connector connector;
};

@@ -41,7 +42,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,
@@ -55,6 +59,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;
@@ -92,6 +103,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 7f69f56f5892..2f974f45c5b4 100644
--- a/drivers/gpu/drm/tests/drm_kunit_helpers.c
+++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c
@@ -18,40 +18,25 @@ struct kunit_dev {
static const struct drm_mode_config_funcs drm_mode_config_funcs = {
};

-static int dev_init(struct kunit_resource *res, void *ptr)
+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(FAKE_DEVICE_NAME);
}

-static void dev_free(struct kunit_resource *res)
+void drm_kunit_helper_free_device(struct kunit *test, struct device *dev)
{
- struct device *dev = res->data;
-
root_device_unregister(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)
{
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, FAKE_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_kunit_helpers.h b/drivers/gpu/drm/tests/drm_kunit_helpers.h
index 6c12b1426ba0..b4277fe92c38 100644
--- a/drivers/gpu/drm/tests/drm_kunit_helpers.h
+++ b/drivers/gpu/drm/tests/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,

Maxime Ripard

unread,
Nov 23, 2022, 10:29:00 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
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.

Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/tests/drm_kunit_helpers.c | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c
index 2f974f45c5b4..651aa93a98a8 100644
--- a/drivers/gpu/drm/tests/drm_kunit_helpers.c
+++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c
@@ -6,6 +6,7 @@
#include <kunit/resource.h>

#include <linux/device.h>
+#include <linux/platform_device.h>

#include "drm_kunit_helpers.h"

@@ -20,12 +21,26 @@ 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(FAKE_DEVICE_NAME);
+ struct platform_device *pdev;
+ int ret;
+
+ ret = platform_driver_register(&fake_platform_driver);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ pdev = platform_device_alloc(FAKE_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;
}

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);
}

struct drm_device *

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 23, 2022, 10:29:05 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
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.

Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/tests/drm_kunit_helpers.c | 60 +++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+)

diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c
index 651aa93a98a8..8bd7c40d0e1a 100644
--- a/drivers/gpu/drm/tests/drm_kunit_helpers.c
+++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c
@@ -10,6 +10,7 @@

#include "drm_kunit_helpers.h"

+#define WQ_TIMEOUT_MS 100
#define FAKE_DEVICE_NAME "drm-kunit-fake-device"

struct kunit_dev {
@@ -19,28 +20,87 @@ struct kunit_dev {
static const struct drm_mode_config_funcs drm_mode_config_funcs = {
};

+struct fake_pdev_sync_point {
+ bool done;
+ wait_queue_head_t wq;
+};
+
+struct fake_pdev_priv {
+ struct fake_pdev_sync_point probe;
+ struct fake_pdev_sync_point remove;
+};
+
+static int fake_probe(struct platform_device *pdev)
+{
+ struct fake_pdev_priv *priv = *(struct fake_pdev_priv **)dev_get_platdata(&pdev->dev);
+
+ priv->probe.done = true;
+ wake_up_interruptible(&priv->probe.wq);
+
+ return 0;
+}
+
+static int fake_remove(struct platform_device *pdev)
+{
+ struct fake_pdev_priv *priv = *(struct fake_pdev_priv **)dev_get_platdata(&pdev->dev);
+
+ priv->remove.done = true;
+ wake_up_interruptible(&priv->remove.wq);
+
+ return 0;
+}
+
+static struct platform_driver fake_platform_driver = {
+ .probe = fake_probe,
+ .remove = fake_remove,
+ .driver = {
+ .name = FAKE_DEVICE_NAME,
+ },
+};
+
struct device *drm_kunit_helper_alloc_device(struct kunit *test)
{
+ struct fake_pdev_priv *priv;
struct platform_device *pdev;
int ret;

+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+ init_waitqueue_head(&priv->probe.wq);
+ init_waitqueue_head(&priv->remove.wq);
+
ret = platform_driver_register(&fake_platform_driver);
KUNIT_ASSERT_EQ(test, ret, 0);

pdev = platform_device_alloc(FAKE_DEVICE_NAME, PLATFORM_DEVID_NONE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);

+ ret = platform_device_add_data(pdev, &priv, sizeof(priv));
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
ret = platform_device_add(pdev);
KUNIT_ASSERT_EQ(test, ret, 0);

+ ret = wait_event_interruptible_timeout(priv->probe.wq, priv->probe.done,
+ msecs_to_jiffies(WQ_TIMEOUT_MS));
+ KUNIT_ASSERT_GT(test, ret, 0);
+
return &pdev->dev;
}

void drm_kunit_helper_free_device(struct kunit *test, struct device *dev)
{
+ struct fake_pdev_priv *priv = *(struct fake_pdev_priv **)dev_get_platdata(dev);
struct platform_device *pdev = to_platform_device(dev);
+ int ret;

platform_device_unregister(pdev);
+
+ ret = wait_event_interruptible_timeout(priv->probe.wq, priv->remove.done,
+ msecs_to_jiffies(WQ_TIMEOUT_MS));
+ KUNIT_ASSERT_GT(test, ret, 0);
+
+ platform_driver_unregister(&fake_platform_driver);

Maxime Ripard

unread,
Nov 23, 2022, 10:29:10 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
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.

Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/tests/drm_client_modeset_test.c | 4 +++-
drivers/gpu/drm/tests/drm_kunit_helpers.c | 19 ++++++++-----------
drivers/gpu/drm/tests/drm_kunit_helpers.h | 11 +++++++++--
3 files changed, 20 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c b/drivers/gpu/drm/tests/drm_client_modeset_test.c
index fe95fda350ae..086469273580 100644
--- a/drivers/gpu/drm/tests/drm_client_modeset_test.c
+++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c
@@ -45,7 +45,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 8bd7c40d0e1a..6d614f9e62d8 100644
--- a/drivers/gpu/drm/tests/drm_kunit_helpers.c
+++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c
@@ -13,10 +13,6 @@
#define WQ_TIMEOUT_MS 100
#define FAKE_DEVICE_NAME "drm-kunit-fake-device"

-struct kunit_dev {
- struct drm_device base;
-};
-
static const struct drm_mode_config_funcs drm_mode_config_funcs = {
};

@@ -104,12 +100,13 @@ 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)
{
- struct kunit_dev *kdev;
struct drm_device *drm;
struct drm_driver *driver;
+ void *container;
int ret;

driver = kunit_kzalloc(test, sizeof(*driver), GFP_KERNEL);
@@ -117,11 +114,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);
diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.h b/drivers/gpu/drm/tests/drm_kunit_helpers.h
index b4277fe92c38..70abc8c806c9 100644
--- a/drivers/gpu/drm/tests/drm_kunit_helpers.h
+++ b/drivers/gpu/drm/tests/drm_kunit_helpers.h
@@ -10,7 +10,14 @@ 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);
+
+#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))

Maxime Ripard

unread,
Nov 23, 2022, 10:29:16 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
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.

Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/tests/drm_kunit_helpers.c | 13 ++++---------
drivers/gpu/drm/tests/drm_kunit_helpers.h | 29 +++++++++++++++++++++++++++--
2 files changed, 31 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c
index 6d614f9e62d8..5c758a22f526 100644
--- a/drivers/gpu/drm/tests/drm_kunit_helpers.c
+++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c
@@ -100,20 +100,15 @@ 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,
- 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);
diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.h b/drivers/gpu/drm/tests/drm_kunit_helpers.h
index 70abc8c806c9..45bc12a53cd4 100644
--- a/drivers/gpu/drm/tests/drm_kunit_helpers.h
+++ b/drivers/gpu/drm/tests/drm_kunit_helpers.h
@@ -10,9 +10,34 @@ 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);
+
+#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);
+}

#define drm_kunit_helper_alloc_drm_device(_test, _dev, _type, _member, _feat) \
((_type *) __drm_kunit_helper_alloc_drm_device(_test, _dev, \

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 23, 2022, 10:29:22 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
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.

Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/tests/Makefile | 1 +
drivers/gpu/drm/tests/drm_managed_test.c | 68 ++++++++++++++++++++++++++++++++
2 files changed, 69 insertions(+)

diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile
index b29ef1085cad..35962c6ef0c4 100644
--- a/drivers/gpu/drm/tests/Makefile
+++ b/drivers/gpu/drm/tests/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \
drm_format_test.o \
drm_framebuffer_test.o \
drm_kunit_helpers.o \
+ drm_managed_test.o \
drm_mm_test.o \
drm_plane_helper_test.o \
drm_rect_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..4fc11b289d9e
--- /dev/null
+++ b/drivers/gpu/drm/tests/drm_managed_test.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <drm/drm_drv.h>
+#include <drm/drm_managed.h>
+
+#include <kunit/resource.h>
+
+#include <linux/device.h>
+
+#include "drm_kunit_helpers.h"
+
+#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);

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 23, 2022, 10:29:27 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
The drm_atomic_get_(old|new)_*_state don't modify the passed
drm_atomic_state, so we can make it const.

Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/drm_atomic.c | 12 ++++++------
include/drm/drm_atomic.h | 32 ++++++++++++++++----------------
2 files changed, 22 insertions(+), 22 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index f197f59f6d99..e666799a46d5 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -880,7 +880,7 @@ EXPORT_SYMBOL(drm_atomic_get_private_obj_state);
* or NULL if the private_obj is not part of the global atomic state.
*/
struct drm_private_state *
-drm_atomic_get_old_private_obj_state(struct drm_atomic_state *state,
+drm_atomic_get_old_private_obj_state(const struct drm_atomic_state *state,
struct drm_private_obj *obj)
{
int i;
@@ -902,7 +902,7 @@ EXPORT_SYMBOL(drm_atomic_get_old_private_obj_state);
* or NULL if the private_obj is not part of the global atomic state.
*/
struct drm_private_state *
-drm_atomic_get_new_private_obj_state(struct drm_atomic_state *state,
+drm_atomic_get_new_private_obj_state(const struct drm_atomic_state *state,
struct drm_private_obj *obj)
{
int i;
@@ -934,7 +934,7 @@ EXPORT_SYMBOL(drm_atomic_get_new_private_obj_state);
* not connected.
*/
struct drm_connector *
-drm_atomic_get_old_connector_for_encoder(struct drm_atomic_state *state,
+drm_atomic_get_old_connector_for_encoder(const struct drm_atomic_state *state,
struct drm_encoder *encoder)
{
struct drm_connector_state *conn_state;
@@ -968,7 +968,7 @@ EXPORT_SYMBOL(drm_atomic_get_old_connector_for_encoder);
* not connected.
*/
struct drm_connector *
-drm_atomic_get_new_connector_for_encoder(struct drm_atomic_state *state,
+drm_atomic_get_new_connector_for_encoder(const struct drm_atomic_state *state,
struct drm_encoder *encoder)
{
struct drm_connector_state *conn_state;
@@ -1117,7 +1117,7 @@ EXPORT_SYMBOL(drm_atomic_get_bridge_state);
* the bridge is not part of the global atomic state.
*/
struct drm_bridge_state *
-drm_atomic_get_old_bridge_state(struct drm_atomic_state *state,
+drm_atomic_get_old_bridge_state(const struct drm_atomic_state *state,
struct drm_bridge *bridge)
{
struct drm_private_state *obj_state;
@@ -1139,7 +1139,7 @@ EXPORT_SYMBOL(drm_atomic_get_old_bridge_state);
* the bridge is not part of the global atomic state.
*/
struct drm_bridge_state *
-drm_atomic_get_new_bridge_state(struct drm_atomic_state *state,
+drm_atomic_get_new_bridge_state(const struct drm_atomic_state *state,
struct drm_bridge *bridge)
{
struct drm_private_state *obj_state;
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index 10b1990bc1f6..92586ab55ef5 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -515,17 +515,17 @@ struct drm_private_state * __must_check
drm_atomic_get_private_obj_state(struct drm_atomic_state *state,
struct drm_private_obj *obj);
struct drm_private_state *
-drm_atomic_get_old_private_obj_state(struct drm_atomic_state *state,
+drm_atomic_get_old_private_obj_state(const struct drm_atomic_state *state,
struct drm_private_obj *obj);
struct drm_private_state *
-drm_atomic_get_new_private_obj_state(struct drm_atomic_state *state,
+drm_atomic_get_new_private_obj_state(const struct drm_atomic_state *state,
struct drm_private_obj *obj);

struct drm_connector *
-drm_atomic_get_old_connector_for_encoder(struct drm_atomic_state *state,
+drm_atomic_get_old_connector_for_encoder(const struct drm_atomic_state *state,
struct drm_encoder *encoder);
struct drm_connector *
-drm_atomic_get_new_connector_for_encoder(struct drm_atomic_state *state,
+drm_atomic_get_new_connector_for_encoder(const struct drm_atomic_state *state,
struct drm_encoder *encoder);

/**
@@ -540,7 +540,7 @@ drm_atomic_get_new_connector_for_encoder(struct drm_atomic_state *state,
* @drm_atomic_get_new_crtc_state should be used instead.
*/
static inline struct drm_crtc_state *
-drm_atomic_get_existing_crtc_state(struct drm_atomic_state *state,
+drm_atomic_get_existing_crtc_state(const struct drm_atomic_state *state,
struct drm_crtc *crtc)
{
return state->crtcs[drm_crtc_index(crtc)].state;
@@ -555,7 +555,7 @@ drm_atomic_get_existing_crtc_state(struct drm_atomic_state *state,
* NULL if the CRTC is not part of the global atomic state.
*/
static inline struct drm_crtc_state *
-drm_atomic_get_old_crtc_state(struct drm_atomic_state *state,
+drm_atomic_get_old_crtc_state(const struct drm_atomic_state *state,
struct drm_crtc *crtc)
{
return state->crtcs[drm_crtc_index(crtc)].old_state;
@@ -569,7 +569,7 @@ drm_atomic_get_old_crtc_state(struct drm_atomic_state *state,
* NULL if the CRTC is not part of the global atomic state.
*/
static inline struct drm_crtc_state *
-drm_atomic_get_new_crtc_state(struct drm_atomic_state *state,
+drm_atomic_get_new_crtc_state(const struct drm_atomic_state *state,
struct drm_crtc *crtc)
{
return state->crtcs[drm_crtc_index(crtc)].new_state;
@@ -587,7 +587,7 @@ drm_atomic_get_new_crtc_state(struct drm_atomic_state *state,
* @drm_atomic_get_new_plane_state should be used instead.
*/
static inline struct drm_plane_state *
-drm_atomic_get_existing_plane_state(struct drm_atomic_state *state,
+drm_atomic_get_existing_plane_state(const struct drm_atomic_state *state,
struct drm_plane *plane)
{
return state->planes[drm_plane_index(plane)].state;
@@ -602,7 +602,7 @@ drm_atomic_get_existing_plane_state(struct drm_atomic_state *state,
* NULL if the plane is not part of the global atomic state.
*/
static inline struct drm_plane_state *
-drm_atomic_get_old_plane_state(struct drm_atomic_state *state,
+drm_atomic_get_old_plane_state(const struct drm_atomic_state *state,
struct drm_plane *plane)
{
return state->planes[drm_plane_index(plane)].old_state;
@@ -617,7 +617,7 @@ drm_atomic_get_old_plane_state(struct drm_atomic_state *state,
* NULL if the plane is not part of the global atomic state.
*/
static inline struct drm_plane_state *
-drm_atomic_get_new_plane_state(struct drm_atomic_state *state,
+drm_atomic_get_new_plane_state(const struct drm_atomic_state *state,
struct drm_plane *plane)
{
return state->planes[drm_plane_index(plane)].new_state;
@@ -635,7 +635,7 @@ drm_atomic_get_new_plane_state(struct drm_atomic_state *state,
* @drm_atomic_get_new_connector_state should be used instead.
*/
static inline struct drm_connector_state *
-drm_atomic_get_existing_connector_state(struct drm_atomic_state *state,
+drm_atomic_get_existing_connector_state(const struct drm_atomic_state *state,
struct drm_connector *connector)
{
int index = drm_connector_index(connector);
@@ -655,7 +655,7 @@ drm_atomic_get_existing_connector_state(struct drm_atomic_state *state,
* or NULL if the connector is not part of the global atomic state.
*/
static inline struct drm_connector_state *
-drm_atomic_get_old_connector_state(struct drm_atomic_state *state,
+drm_atomic_get_old_connector_state(const struct drm_atomic_state *state,
struct drm_connector *connector)
{
int index = drm_connector_index(connector);
@@ -675,7 +675,7 @@ drm_atomic_get_old_connector_state(struct drm_atomic_state *state,
* or NULL if the connector is not part of the global atomic state.
*/
static inline struct drm_connector_state *
-drm_atomic_get_new_connector_state(struct drm_atomic_state *state,
+drm_atomic_get_new_connector_state(const struct drm_atomic_state *state,
struct drm_connector *connector)
{
int index = drm_connector_index(connector);
@@ -713,7 +713,7 @@ drm_atomic_get_new_connector_state(struct drm_atomic_state *state,
* Read-only pointer to the current plane state.
*/
static inline const struct drm_plane_state *
-__drm_atomic_get_current_plane_state(struct drm_atomic_state *state,
+__drm_atomic_get_current_plane_state(const struct drm_atomic_state *state,
struct drm_plane *plane)
{
if (state->planes[drm_plane_index(plane)].state)
@@ -1134,10 +1134,10 @@ struct drm_bridge_state *
drm_atomic_get_bridge_state(struct drm_atomic_state *state,
struct drm_bridge *bridge);
struct drm_bridge_state *
-drm_atomic_get_old_bridge_state(struct drm_atomic_state *state,
+drm_atomic_get_old_bridge_state(const struct drm_atomic_state *state,
struct drm_bridge *bridge);
struct drm_bridge_state *
-drm_atomic_get_new_bridge_state(struct drm_atomic_state *state,
+drm_atomic_get_new_bridge_state(const struct drm_atomic_state *state,
struct drm_bridge *bridge);

#endif /* DRM_ATOMIC_H_ */

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 23, 2022, 10:29:31 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On the vc4 devices (and later), the blending is done by a single device
called the HVS. The HVS has three FIFO that can operate in parallel, and
route their output to 6 CRTCs and 7 encoders on the BCM2711.

Each of these CRTCs and encoders have some contraints on which FIFO they
can feed from, so we need some code to take all those constraints into
account and assign FIFOs to CRTCs.

The problem can be simplified by assigning those FIFOs to CRTCs by
ascending output index number. We had a comment mentioning it already,
but we were never actually enforcing it.

It was working still in most situations because the probe order is
roughly equivalent, except for the (optional, and fairly rarely used on
the Pi4) VEC which was last in the probe order sequence, but one of the
earliest device to assign.

This resulted in configurations that were rejected by our code but were
still valid with a different assignment.

We can fix this by making sure we assign CRTCs to FIFOs by ordering
them by ascending HVS output index.

Fixes: 87ebcd42fb7b ("drm/vc4: crtc: Assign output to channel automatically")
Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/vc4/vc4_kms.c | 109 +++++++++++++++++++++++++++++-------------
1 file changed, 76 insertions(+), 33 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c
index 5c97642ed66a..17a28f9a2f31 100644
--- a/drivers/gpu/drm/vc4/vc4_kms.c
+++ b/drivers/gpu/drm/vc4/vc4_kms.c
@@ -12,6 +12,7 @@
*/

#include <linux/clk.h>
+#include <linux/sort.h>

#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
@@ -776,6 +777,20 @@ static int vc4_hvs_channels_obj_init(struct vc4_dev *vc4)
return drmm_add_action_or_reset(&vc4->base, vc4_hvs_channels_obj_fini, NULL);
}

+static int cmp_vc4_crtc_hvs_output(const void *a, const void *b)
+{
+ const struct vc4_crtc *crtc_a =
+ to_vc4_crtc(*(const struct drm_crtc **)a);
+ const struct vc4_crtc_data *data_a =
+ vc4_crtc_to_vc4_crtc_data(crtc_a);
+ const struct vc4_crtc *crtc_b =
+ to_vc4_crtc(*(const struct drm_crtc **)b);
+ const struct vc4_crtc_data *data_b =
+ vc4_crtc_to_vc4_crtc_data(crtc_b);
+
+ return data_a->hvs_output - data_b->hvs_output;
+}
+
/*
* The BCM2711 HVS has up to 7 outputs connected to the pixelvalves and
* the TXP (and therefore all the CRTCs found on that platform).
@@ -810,10 +825,11 @@ static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
struct drm_atomic_state *state)
{
struct vc4_hvs_state *hvs_new_state;
- struct drm_crtc_state *old_crtc_state, *new_crtc_state;
+ struct drm_crtc **sorted_crtcs;
struct drm_crtc *crtc;
unsigned int unassigned_channels = 0;
unsigned int i;
+ int ret;

hvs_new_state = vc4_hvs_get_global_state(state);
if (IS_ERR(hvs_new_state))
@@ -823,15 +839,59 @@ static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
if (!hvs_new_state->fifo_state[i].in_use)
unassigned_channels |= BIT(i);

- for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
- struct vc4_crtc_state *old_vc4_crtc_state =
- to_vc4_crtc_state(old_crtc_state);
- struct vc4_crtc_state *new_vc4_crtc_state =
- to_vc4_crtc_state(new_crtc_state);
- struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ /*
+ * The problem we have to solve here is that we have up to 7
+ * encoders, connected to up to 6 CRTCs.
+ *
+ * Those CRTCs, depending on the instance, can be routed to 1, 2
+ * or 3 HVS FIFOs, and we need to set the change the muxing
+ * between FIFOs and outputs in the HVS accordingly.
+ *
+ * It would be pretty hard to come up with an algorithm that
+ * would generically solve this. However, the current routing
+ * trees we support allow us to simplify a bit the problem.
+ *
+ * Indeed, with the current supported layouts, if we try to
+ * assign in the ascending crtc index order the FIFOs, we can't
+ * fall into the situation where an earlier CRTC that had
+ * multiple routes is assigned one that was the only option for
+ * a later CRTC.
+ *
+ * If the layout changes and doesn't give us that in the future,
+ * we will need to have something smarter, but it works so far.
+ */
+ sorted_crtcs = kmalloc_array(dev->num_crtcs, sizeof(*sorted_crtcs), GFP_KERNEL);
+ if (!sorted_crtcs)
+ return -ENOMEM;
+
+ i = 0;
+ drm_for_each_crtc(crtc, dev)
+ sorted_crtcs[i++] = crtc;
+
+ sort(sorted_crtcs, i, sizeof(*sorted_crtcs), cmp_vc4_crtc_hvs_output, NULL);
+
+ for (i = 0; i < dev->num_crtcs; i++) {
+ struct vc4_crtc_state *old_vc4_crtc_state, *new_vc4_crtc_state;
+ struct drm_crtc_state *old_crtc_state, *new_crtc_state;
+ struct vc4_crtc *vc4_crtc;
unsigned int matching_channels;
unsigned int channel;

+ crtc = sorted_crtcs[i];
+ if (!crtc)
+ continue;
+ vc4_crtc = to_vc4_crtc(crtc);
+
+ old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
+ if (!old_crtc_state)
+ continue;
+ old_vc4_crtc_state = to_vc4_crtc_state(old_crtc_state);
+
+ new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ if (!new_crtc_state)
+ continue;
+ new_vc4_crtc_state = to_vc4_crtc_state(new_crtc_state);
+
drm_dbg(dev, "%s: Trying to find a channel.\n", crtc->name);

/* Nothing to do here, let's skip it */
@@ -860,33 +920,11 @@ static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
continue;
}

- /*
- * The problem we have to solve here is that we have
- * up to 7 encoders, connected to up to 6 CRTCs.
- *
- * Those CRTCs, depending on the instance, can be
- * routed to 1, 2 or 3 HVS FIFOs, and we need to set
- * the change the muxing between FIFOs and outputs in
- * the HVS accordingly.
- *
- * It would be pretty hard to come up with an
- * algorithm that would generically solve
- * this. However, the current routing trees we support
- * allow us to simplify a bit the problem.
- *
- * Indeed, with the current supported layouts, if we
- * try to assign in the ascending crtc index order the
- * FIFOs, we can't fall into the situation where an
- * earlier CRTC that had multiple routes is assigned
- * one that was the only option for a later CRTC.
- *
- * If the layout changes and doesn't give us that in
- * the future, we will need to have something smarter,
- * but it works so far.
- */
matching_channels = unassigned_channels & vc4_crtc->data->hvs_available_channels;
- if (!matching_channels)
- return -EINVAL;
+ if (!matching_channels) {
+ ret = -EINVAL;
+ goto err_free_crtc_array;
+ }

channel = ffs(matching_channels) - 1;

@@ -896,7 +934,12 @@ static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
hvs_new_state->fifo_state[channel].in_use = true;
}

+ kfree(sorted_crtcs);
return 0;
+
+err_free_crtc_array:
+ kfree(sorted_crtcs);
+ return ret;
}

static int

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 23, 2022, 10:29:36 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
None of our wrappers around container_of to access our objects from the
DRM object pointer actually modify the latter.

Let's make them const.

Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/vc4/vc4_drv.h | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 515228682e8e..3ff56c1821ef 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -233,7 +233,7 @@ struct vc4_dev {
};

static inline struct vc4_dev *
-to_vc4_dev(struct drm_device *dev)
+to_vc4_dev(const struct drm_device *dev)
{
return container_of(dev, struct vc4_dev, base);
}
@@ -286,7 +286,7 @@ struct vc4_bo {
};

static inline struct vc4_bo *
-to_vc4_bo(struct drm_gem_object *bo)
+to_vc4_bo(const struct drm_gem_object *bo)
{
return container_of(to_drm_gem_dma_obj(bo), struct vc4_bo, base);
}
@@ -299,7 +299,7 @@ struct vc4_fence {
};

static inline struct vc4_fence *
-to_vc4_fence(struct dma_fence *fence)
+to_vc4_fence(const struct dma_fence *fence)
{
return container_of(fence, struct vc4_fence, base);
}
@@ -360,7 +360,7 @@ struct vc4_plane {
};

static inline struct vc4_plane *
-to_vc4_plane(struct drm_plane *plane)
+to_vc4_plane(const struct drm_plane *plane)
{
return container_of(plane, struct vc4_plane, base);
}
@@ -436,7 +436,7 @@ struct vc4_plane_state {
};

static inline struct vc4_plane_state *
-to_vc4_plane_state(struct drm_plane_state *state)
+to_vc4_plane_state(const struct drm_plane_state *state)
{
return container_of(state, struct vc4_plane_state, base);
}
@@ -466,7 +466,7 @@ struct vc4_encoder {
};

static inline struct vc4_encoder *
-to_vc4_encoder(struct drm_encoder *encoder)
+to_vc4_encoder(const struct drm_encoder *encoder)
{
return container_of(encoder, struct vc4_encoder, base);
}
@@ -539,7 +539,7 @@ struct vc4_crtc {
};

static inline struct vc4_crtc *
-to_vc4_crtc(struct drm_crtc *crtc)
+to_vc4_crtc(const struct drm_crtc *crtc)
{
return container_of(crtc, struct vc4_crtc, base);
}
@@ -584,7 +584,7 @@ struct vc4_crtc_state {
#define VC4_HVS_CHANNEL_DISABLED ((unsigned int)-1)

static inline struct vc4_crtc_state *
-to_vc4_crtc_state(struct drm_crtc_state *crtc_state)
+to_vc4_crtc_state(const struct drm_crtc_state *crtc_state)
{
return container_of(crtc_state, struct vc4_crtc_state, base);
}

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 23, 2022, 10:29:41 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
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.

Signed-off-by: Maxime Ripard <max...@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 3ff56c1821ef..6ff63036915c 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(struct drm_atomic_state *state);
+struct vc4_hvs_state *vc4_hvs_get_new_global_state(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 17a28f9a2f31..cb2197622b2b 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(struct drm_atomic_state *state)
{
struct vc4_dev *vc4 = to_vc4_dev(state->dev);
@@ -204,7 +185,7 @@ vc4_hvs_get_new_global_state(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(struct drm_atomic_state *state)
{
struct vc4_dev *vc4 = to_vc4_dev(state->dev);
@@ -217,7 +198,7 @@ vc4_hvs_get_old_global_state(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);

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 23, 2022, 10:29:47 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
The vc4_hvs_get_(old|new)_global_state functions don't modify the
drm_atomic_state passed as an argument, so let's make it const.

Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/vc4/vc4_drv.h | 4 ++--
drivers/gpu/drm/vc4/vc4_kms.c | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 6ff63036915c..855207c07e2e 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -375,8 +375,8 @@ to_vc4_hvs_state(const struct drm_private_state *priv)
}

struct vc4_hvs_state *vc4_hvs_get_global_state(struct drm_atomic_state *state);
-struct vc4_hvs_state *vc4_hvs_get_old_global_state(struct drm_atomic_state *state);
-struct vc4_hvs_state *vc4_hvs_get_new_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 cb2197622b2b..6c9949399310 100644
--- a/drivers/gpu/drm/vc4/vc4_kms.c
+++ b/drivers/gpu/drm/vc4/vc4_kms.c
@@ -173,7 +173,7 @@ vc4_ctm_commit(struct vc4_dev *vc4, struct drm_atomic_state *state)
}

struct vc4_hvs_state *
-vc4_hvs_get_new_global_state(struct drm_atomic_state *state)
+vc4_hvs_get_new_global_state(const struct drm_atomic_state *state)
{
struct vc4_dev *vc4 = to_vc4_dev(state->dev);
struct drm_private_state *priv_state;
@@ -186,7 +186,7 @@ vc4_hvs_get_new_global_state(struct drm_atomic_state *state)
}

struct vc4_hvs_state *
-vc4_hvs_get_old_global_state(struct drm_atomic_state *state)
+vc4_hvs_get_old_global_state(const struct drm_atomic_state *state)
{
struct vc4_dev *vc4 = to_vc4_dev(state->dev);
struct drm_private_state *priv_state;

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 23, 2022, 10:29:51 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
The current order of variable assignments is unneccessarily complex,
let's make it simpler.

Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/vc4/vc4_txp.c | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index bd181b5a7b52..b00c6fae972f 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -490,7 +490,6 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
struct drm_device *drm = dev_get_drvdata(master);
struct vc4_crtc *vc4_crtc;
struct vc4_txp *txp;
- struct drm_crtc *crtc;
struct drm_encoder *encoder;
int ret, irq;

@@ -501,18 +500,16 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
txp = drmm_kzalloc(drm, sizeof(*txp), GFP_KERNEL);
if (!txp)
return -ENOMEM;
- vc4_crtc = &txp->base;
- crtc = &vc4_crtc->base;
-
- vc4_crtc->pdev = pdev;
- vc4_crtc->data = &vc4_txp_crtc_data;
- vc4_crtc->feeds_txp = true;

txp->pdev = pdev;
-
txp->regs = vc4_ioremap_regs(pdev, 0);
if (IS_ERR(txp->regs))
return PTR_ERR(txp->regs);
+
+ vc4_crtc = &txp->base;
+ vc4_crtc->pdev = pdev;
+ vc4_crtc->data = &vc4_txp_crtc_data;
+ vc4_crtc->feeds_txp = true;
vc4_crtc->regset.base = txp->regs;
vc4_crtc->regset.regs = txp_regs;
vc4_crtc->regset.nregs = ARRAY_SIZE(txp_regs);
@@ -533,7 +530,7 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
return ret;

encoder = &txp->connector.encoder;
- encoder->possible_crtcs = drm_crtc_mask(crtc);
+ encoder->possible_crtcs = drm_crtc_mask(&vc4_crtc->base);

ret = devm_request_irq(dev, irq, vc4_txp_interrupt, 0,
dev_name(dev), txp);

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 23, 2022, 10:29:56 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
The TXP is integrated as a separate CRTC/Encoder/Connector combo, but
for some reason doesn't rely on the vc4_encoder type and it's associated
type.

Let's create a type to make it consistent with the other encoders.

Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/vc4/vc4_drv.h | 1 +
drivers/gpu/drm/vc4/vc4_txp.c | 30 ++++++++++++++++++++----------
2 files changed, 21 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 855207c07e2e..31228d4cf080 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -473,6 +473,7 @@ enum vc4_encoder_type {
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_SMI,
VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_TXP,
};

struct vc4_encoder {
diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index b00c6fae972f..0bb8e97d7891 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -153,6 +153,7 @@ struct vc4_txp {

struct platform_device *pdev;

+ struct vc4_encoder encoder;
struct drm_writeback_connector connector;

void __iomem *regs;
@@ -160,7 +161,7 @@ struct vc4_txp {

static inline struct vc4_txp *encoder_to_vc4_txp(struct drm_encoder *encoder)
{
- return container_of(encoder, struct vc4_txp, connector.encoder);
+ return container_of(encoder, struct vc4_txp, encoder.base);
}

static inline struct vc4_txp *connector_to_vc4_txp(struct drm_connector *conn)
@@ -488,9 +489,10 @@ static int vc4_txp_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_encoder *vc4_encoder;
+ struct drm_encoder *encoder;
struct vc4_crtc *vc4_crtc;
struct vc4_txp *txp;
- struct drm_encoder *encoder;
int ret, irq;

irq = platform_get_irq(pdev, 0);
@@ -514,13 +516,24 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
vc4_crtc->regset.regs = txp_regs;
vc4_crtc->regset.nregs = ARRAY_SIZE(txp_regs);

+ vc4_encoder = &txp->encoder;
+ txp->encoder.type = VC4_ENCODER_TYPE_TXP;
+
+ encoder = &vc4_encoder->base;
+ encoder->possible_crtcs = drm_crtc_mask(&vc4_crtc->base);
+
+ drm_encoder_helper_add(encoder, &vc4_txp_encoder_helper_funcs);
+
+ ret = drmm_encoder_init(drm, encoder, NULL, DRM_MODE_ENCODER_VIRTUAL, NULL);
+ if (ret)
+ return ret;
+
drm_connector_helper_add(&txp->connector.base,
&vc4_txp_connector_helper_funcs);
- ret = drm_writeback_connector_init(drm, &txp->connector,
- &vc4_txp_connector_funcs,
- &vc4_txp_encoder_helper_funcs,
- drm_fmts, ARRAY_SIZE(drm_fmts),
- 0);
+ ret = drm_writeback_connector_init_with_encoder(drm, &txp->connector,
+ encoder,
+ &vc4_txp_connector_funcs,
+ drm_fmts, ARRAY_SIZE(drm_fmts));
if (ret)
return ret;

@@ -529,9 +542,6 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
if (ret)
return ret;

- encoder = &txp->connector.encoder;
- encoder->possible_crtcs = drm_crtc_mask(&vc4_crtc->base);
-
ret = devm_request_irq(dev, irq, vc4_txp_interrupt, 0,
dev_name(dev), txp);
if (ret)

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 23, 2022, 10:30:01 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
It makes more sense to register the CRTC before the encoder and
connectors, so let's move our call around.

Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/vc4/vc4_txp.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index 0bb8e97d7891..c2a6bea6fd96 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -516,6 +516,11 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
vc4_crtc->regset.regs = txp_regs;
vc4_crtc->regset.nregs = ARRAY_SIZE(txp_regs);

+ ret = vc4_crtc_init(drm, vc4_crtc,
+ &vc4_txp_crtc_funcs, &vc4_txp_crtc_helper_funcs);
+ if (ret)
+ return ret;
+
vc4_encoder = &txp->encoder;
txp->encoder.type = VC4_ENCODER_TYPE_TXP;

@@ -537,11 +542,6 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
if (ret)
return ret;

- ret = vc4_crtc_init(drm, vc4_crtc,
- &vc4_txp_crtc_funcs, &vc4_txp_crtc_helper_funcs);
- if (ret)
- return ret;

Maxime Ripard

unread,
Nov 23, 2022, 10:30:06 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
Both users of vc4_crtc_init need the same extra initialization to set
the pointer to the platform_device and the CRTC data. Since it's
mandatory, let's make them both arguments of vc4_crtc_init().

Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/vc4/vc4_crtc.c | 17 +++++++++++------
drivers/gpu/drm/vc4/vc4_drv.h | 6 ++++--
drivers/gpu/drm/vc4/vc4_txp.c | 7 ++-----
3 files changed, 17 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index 0108613e79d5..1ea190dffe87 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -1278,9 +1278,12 @@ static void vc4_set_crtc_possible_masks(struct drm_device *drm,
}
}

-int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc,
+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)
+ 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;
@@ -1300,6 +1303,9 @@ int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc,
return PTR_ERR(primary_plane);
}

+ vc4_crtc->data = data;
+ vc4_crtc->pdev = pdev;
+ vc4_crtc->feeds_txp = feeds_txp;
spin_lock_init(&vc4_crtc->irq_lock);
ret = drmm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
crtc_funcs, NULL);
@@ -1345,8 +1351,6 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
pv_data = of_device_get_match_data(dev);
if (!pv_data)
return -ENODEV;
- vc4_crtc->data = &pv_data->base;
- vc4_crtc->pdev = pdev;

vc4_crtc->regs = vc4_ioremap_regs(pdev, 0);
if (IS_ERR(vc4_crtc->regs))
@@ -1356,8 +1360,9 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
vc4_crtc->regset.regs = crtc_regs;
vc4_crtc->regset.nregs = ARRAY_SIZE(crtc_regs);

- ret = vc4_crtc_init(drm, vc4_crtc,
- &vc4_crtc_funcs, &vc4_crtc_helper_funcs);
+ ret = vc4_crtc_init(drm, pdev, vc4_crtc, &pv_data->base,
+ &vc4_crtc_funcs, &vc4_crtc_helper_funcs,
+ false);
if (ret)
return ret;
vc4_set_crtc_possible_masks(drm, crtc);
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 31228d4cf080..599d24f1f55a 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -886,9 +886,11 @@ 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 vc4_crtc *vc4_crtc,
+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);
+ const struct drm_crtc_helper_funcs *crtc_helper_funcs,
+ bool feeds_txp);
int vc4_page_flip(struct drm_crtc *crtc,
struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event,
diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index c2a6bea6fd96..841da240d93a 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -509,15 +509,12 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
return PTR_ERR(txp->regs);

vc4_crtc = &txp->base;
- vc4_crtc->pdev = pdev;
- vc4_crtc->data = &vc4_txp_crtc_data;
- vc4_crtc->feeds_txp = true;
vc4_crtc->regset.base = txp->regs;
vc4_crtc->regset.regs = txp_regs;
vc4_crtc->regset.nregs = ARRAY_SIZE(txp_regs);

- ret = vc4_crtc_init(drm, vc4_crtc,
- &vc4_txp_crtc_funcs, &vc4_txp_crtc_helper_funcs);
+ ret = vc4_crtc_init(drm, pdev, vc4_crtc, &vc4_txp_crtc_data,
+ &vc4_txp_crtc_funcs, &vc4_txp_crtc_helper_funcs, true);
if (ret)
return ret;


--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 23, 2022, 10:30:12 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
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.

Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/vc4/vc4_crtc.c | 52 +++++++++++++++++++++++++++---------------
drivers/gpu/drm/vc4/vc4_drv.h | 6 +++++
2 files changed, 39 insertions(+), 19 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index 1ea190dffe87..340c39921bce 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -1278,31 +1278,20 @@ 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)
+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;
@@ -1334,6 +1323,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 599d24f1f55a..01ca2b25d2e3 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -886,6 +886,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,

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 23, 2022, 10:30:17 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
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.

Signed-off-by: Maxime Ripard <max...@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 340c39921bce..8bc30ad0904b 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 01ca2b25d2e3..dca7be3fccb5 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 *debugfs_name;


--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 23, 2022, 10:30:22 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
It's fairly hard to figure out the instance of the CRTC affected by an
atomic change using the default name.

Since we can provide our own to the CRTC initialization functions, let's
do so to make the debugging sessions easier.

Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/vc4/vc4_crtc.c | 10 +++++++++-
drivers/gpu/drm/vc4/vc4_drv.h | 2 ++
drivers/gpu/drm/vc4/vc4_txp.c | 1 +
3 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index 8bc30ad0904b..59e473059fa2 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -1118,6 +1118,7 @@ static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {

static const struct vc4_pv_data bcm2835_pv0_data = {
.base = {
+ .name = "pixelvalve-0",
.debugfs_name = "crtc0_regs",
.hvs_available_channels = BIT(0),
.hvs_output = 0,
@@ -1132,6 +1133,7 @@ static const struct vc4_pv_data bcm2835_pv0_data = {

static const struct vc4_pv_data bcm2835_pv1_data = {
.base = {
+ .name = "pixelvalve-1",
.debugfs_name = "crtc1_regs",
.hvs_available_channels = BIT(2),
.hvs_output = 2,
@@ -1146,6 +1148,7 @@ static const struct vc4_pv_data bcm2835_pv1_data = {

static const struct vc4_pv_data bcm2835_pv2_data = {
.base = {
+ .name = "pixelvalve-2",
.debugfs_name = "crtc2_regs",
.hvs_available_channels = BIT(1),
.hvs_output = 1,
@@ -1160,6 +1163,7 @@ static const struct vc4_pv_data bcm2835_pv2_data = {

static const struct vc4_pv_data bcm2711_pv0_data = {
.base = {
+ .name = "pixelvalve-0",
.debugfs_name = "crtc0_regs",
.hvs_available_channels = BIT(0),
.hvs_output = 0,
@@ -1174,6 +1178,7 @@ static const struct vc4_pv_data bcm2711_pv0_data = {

static const struct vc4_pv_data bcm2711_pv1_data = {
.base = {
+ .name = "pixelvalve-1",
.debugfs_name = "crtc1_regs",
.hvs_available_channels = BIT(0) | BIT(1) | BIT(2),
.hvs_output = 3,
@@ -1188,6 +1193,7 @@ static const struct vc4_pv_data bcm2711_pv1_data = {

static const struct vc4_pv_data bcm2711_pv2_data = {
.base = {
+ .name = "pixelvalve-2",
.debugfs_name = "crtc2_regs",
.hvs_available_channels = BIT(0) | BIT(1) | BIT(2),
.hvs_output = 4,
@@ -1201,6 +1207,7 @@ static const struct vc4_pv_data bcm2711_pv2_data = {

static const struct vc4_pv_data bcm2711_pv3_data = {
.base = {
+ .name = "pixelvalve-3",
.debugfs_name = "crtc3_regs",
.hvs_available_channels = BIT(1),
.hvs_output = 1,
@@ -1214,6 +1221,7 @@ static const struct vc4_pv_data bcm2711_pv3_data = {

static const struct vc4_pv_data bcm2711_pv4_data = {
.base = {
+ .name = "pixelvalve-4",
.debugfs_name = "crtc4_regs",
.hvs_available_channels = BIT(0) | BIT(1) | BIT(2),
.hvs_output = 5,
@@ -1282,7 +1290,7 @@ int __vc4_crtc_init(struct drm_device *drm,
vc4_crtc->feeds_txp = feeds_txp;
spin_lock_init(&vc4_crtc->irq_lock);
ret = drmm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
- crtc_funcs, NULL);
+ crtc_funcs, data->name);
if (ret)
return ret;

diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index dca7be3fccb5..54352db48476 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -512,6 +512,8 @@ struct drm_encoder *vc4_find_encoder_by_type(struct drm_device *drm,
}

struct vc4_crtc_data {
+ const char *name;
+
const char *debugfs_name;

/* Bitmask of channels (FIFOs) of the HVS that the output can source from */
diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index 841da240d93a..4f7ce5d3e8ad 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -480,6 +480,7 @@ static irqreturn_t vc4_txp_interrupt(int irq, void *data)
}

static const struct vc4_crtc_data vc4_txp_crtc_data = {
+ .name = "txp",
.debugfs_name = "txp_regs",
.hvs_available_channels = BIT(2),
.hvs_output = 2,

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 23, 2022, 10:30:27 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
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.

Signed-off-by: Maxime Ripard <max...@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,

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 23, 2022, 10:30:33 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
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.

Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/vc4/Kconfig | 15 +++
drivers/gpu/drm/vc4/Makefile | 1 +
drivers/gpu/drm/vc4/tests/.kunitconfig | 14 +++
drivers/gpu/drm/vc4/tests/Makefile | 7 ++
drivers/gpu/drm/vc4/tests/vc4_mock.c | 174 ++++++++++++++++++++++++++++
drivers/gpu/drm/vc4/tests/vc4_mock.h | 58 ++++++++++
drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c | 39 +++++++
drivers/gpu/drm/vc4/tests/vc4_mock_output.c | 97 ++++++++++++++++
drivers/gpu/drm/vc4/tests/vc4_mock_plane.c | 45 +++++++
drivers/gpu/drm/vc4/vc4_crtc.c | 20 ++--
drivers/gpu/drm/vc4/vc4_drv.c | 4 +-
drivers/gpu/drm/vc4/vc4_drv.h | 13 +++
drivers/gpu/drm/vc4/vc4_txp.c | 2 +-
13 files changed, 476 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig
index 246305d17a52..eb5e8367da67 100644
--- a/drivers/gpu/drm/vc4/Kconfig
+++ b/drivers/gpu/drm/vc4/Kconfig
@@ -34,3 +34,18 @@ 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
+ tristate "KUnit tests for VC4" if !KUNIT_ALL_TESTS
+ depends on DRM_VC4 && KUNIT
+ 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..8984b2b5da77 100644
--- a/drivers/gpu/drm/vc4/Makefile
+++ b/drivers/gpu/drm/vc4/Makefile
@@ -28,3 +28,4 @@ vc4-y := \
vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o

obj-$(CONFIG_DRM_VC4) += vc4.o
+obj-$(CONFIG_DRM_VC4_KUNIT_TEST) += tests/
diff --git a/drivers/gpu/drm/vc4/tests/.kunitconfig b/drivers/gpu/drm/vc4/tests/.kunitconfig
new file mode 100644
index 000000000000..bfd9899ed6e0
--- /dev/null
+++ b/drivers/gpu/drm/vc4/tests/.kunitconfig
@@ -0,0 +1,14 @@
+CONFIG_ARCH_BCM=y
+CONFIG_ARCH_BCM2835=y
+CONFIG_BCM2835_MBOX=y
+CONFIG_KUNIT=y
+CONFIG_DRM=y
+CONFIG_DRM_KUNIT_TEST=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/Makefile b/drivers/gpu/drm/vc4/tests/Makefile
new file mode 100644
index 000000000000..3e8a7b4f9017
--- /dev/null
+++ b/drivers/gpu/drm/vc4/tests/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_DRM_VC4_KUNIT_TEST) += \
+ vc4_mock.o \
+ vc4_mock_crtc.o \
+ vc4_mock_output.o \
+ vc4_mock_plane.o
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..f4c2213ea0e2
--- /dev/null
+++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c
@@ -0,0 +1,174 @@
+#include <drm/drm_drv.h>
+
+#include <kunit/test.h>
+
+#include "../../tests/drm_kunit_helpers.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 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 : NULL;
+ 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..144af2ed6c40
--- /dev/null
+++ b/drivers/gpu/drm/vc4/tests/vc4_mock.h
@@ -0,0 +1,58 @@
+#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..213622f9fba2
--- /dev/null
+++ b/drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c
@@ -0,0 +1,39 @@
+#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..18bd088530fe
--- /dev/null
+++ b/drivers/gpu/drm/vc4/tests/vc4_mock_output.c
@@ -0,0 +1,97 @@
+#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..106d3bd53aa5
--- /dev/null
+++ b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
@@ -0,0 +1,45 @@
+#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 59e473059fa2..21b40d644ace 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 b66bf7aea632..af6a83ac8526 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..e2a56c32a124 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,12 @@ struct vc4_pv_data {
enum vc4_encoder_type encoder_types[4];
};

+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 +931,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),

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 23, 2022, 10:30:38 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
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.

Signed-off-by: Maxime Ripard <max...@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 21b40d644ace..ad868c24672a 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 e2a56c32a124..d13033ed8848 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;
@@ -642,10 +644,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 92c07e31d632..79bdef35db76 100644
--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -175,8 +175,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)

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 23, 2022, 10:30:43 AM11/23/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, Maxime Ripard, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
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.

Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/vc4/tests/Makefile | 3 +-
drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c | 624 +++++++++++++++++++++++++
2 files changed, 626 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vc4/tests/Makefile b/drivers/gpu/drm/vc4/tests/Makefile
index 3e8a7b4f9017..1ae143c415a4 100644
--- a/drivers/gpu/drm/vc4/tests/Makefile
+++ b/drivers/gpu/drm/vc4/tests/Makefile
@@ -4,4 +4,5 @@ obj-$(CONFIG_DRM_VC4_KUNIT_TEST) += \
vc4_mock.o \
vc4_mock_crtc.o \
vc4_mock_output.o \
- vc4_mock_plane.o
+ vc4_mock_plane.o \
+ vc4_test_pv_muxing.o
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..3047fc768c09
--- /dev/null
+++ b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c
@@ -0,0 +1,624 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <drm/drm_atomic.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_mode.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_plane.h>
+
+#include <kunit/test.h>
+
+#include "../../tests/drm_kunit_helpers.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 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_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 bool check_channel_for_encoder(struct kunit *test,
+ const struct drm_atomic_state *state,
+ enum vc4_encoder_type type)
+{
+ struct drm_device *drm = state->dev;
+ struct drm_encoder *encoder;
+ struct drm_crtc *crtc;
+ struct drm_crtc_state *crtc_state;
+ 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);
+
+ 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_new_crtc_state(state, crtc);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
+
+ new_vc4_crtc_state = to_vc4_crtc_state(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_vc5_encoder_constraints(type,
+ channel));
+
+ return true;
+}
+
+struct pv_muxing_param {
+ const char *name;
+ enum vc4_encoder_type *encoders;
+ size_t nencoders;
+};
+
+#define PV_MUXING_TEST(_name, ...) \
+ { \
+ .name = _name, \
+ .encoders = (enum vc4_encoder_type[]) { __VA_ARGS__ }, \
+ .nencoders = sizeof((enum vc4_encoder_type[]) { __VA_ARGS__ }) / \
+ sizeof(enum vc4_encoder_type), \
+ }
+
+static const struct pv_muxing_param vc5_test_pv_muxing_params[] = {
+ PV_MUXING_TEST("1 output: DPI",
+ VC4_ENCODER_TYPE_DPI),
+ PV_MUXING_TEST("1 output: DSI0",
+ VC4_ENCODER_TYPE_DSI0),
+ PV_MUXING_TEST("1 output: DSI1",
+ VC4_ENCODER_TYPE_DSI1),
+ PV_MUXING_TEST("1 output: HDMI0",
+ VC4_ENCODER_TYPE_HDMI0),
+ PV_MUXING_TEST("1 output: HDMI1",
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("1 output: VEC",
+ VC4_ENCODER_TYPE_VEC),
+ PV_MUXING_TEST("2 output: DPI, DSI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_DSI1),
+ PV_MUXING_TEST("2 output: DPI, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_HDMI0),
+ PV_MUXING_TEST("2 output: DPI, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("2 output: DPI, TXP",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_TXP),
+ PV_MUXING_TEST("2 output: DPI, VEC",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC),
+ PV_MUXING_TEST("2 output: DPI, DSI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_DSI1),
+ PV_MUXING_TEST("2 output: DSI0, DSI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_DSI1),
+ PV_MUXING_TEST("2 output: DSI0, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_HDMI0),
+ PV_MUXING_TEST("2 output: DSI0, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("2 output: DSI0, TXP",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_TXP),
+ PV_MUXING_TEST("2 output: DSI0, VEC",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC),
+ PV_MUXING_TEST("2 output: DSI0, DSI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_DSI1),
+ PV_MUXING_TEST("2 output: DSI1, VEC",
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_VEC),
+ PV_MUXING_TEST("2 output: DSI1, TXP",
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_TXP),
+ PV_MUXING_TEST("2 output: DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+ PV_MUXING_TEST("2 output: DSI1, HDMI1",
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("2 output: HDMI0, VEC",
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_VEC),
+ PV_MUXING_TEST("2 output: HDMI0, TXP",
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_TXP),
+ PV_MUXING_TEST("2 output: HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("2 output: HDMI1, VEC",
+ VC4_ENCODER_TYPE_HDMI1,
+ VC4_ENCODER_TYPE_VEC),
+ PV_MUXING_TEST("2 output: HDMI1, TXP",
+ VC4_ENCODER_TYPE_HDMI1,
+ VC4_ENCODER_TYPE_TXP),
+ PV_MUXING_TEST("2 output: TXP, VEC",
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_VEC),
+ PV_MUXING_TEST("3 output: DPI, VEC, TXP",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP),
+ PV_MUXING_TEST("3 output: DPI, VEC, DSI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1),
+ PV_MUXING_TEST("3 output: DPI, VEC, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_HDMI0),
+ PV_MUXING_TEST("3 output: DPI, VEC, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("3 output: DPI, TXP, DSI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1),
+ PV_MUXING_TEST("3 output: DPI, TXP, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI0),
+ PV_MUXING_TEST("3 output: DPI, TXP, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("3 output: DPI, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+ PV_MUXING_TEST("3 output: DPI, DSI1, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("3 output: DPI, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("3 output: DSI0, VEC, TXP",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP),
+ PV_MUXING_TEST("3 output: DSI0, VEC, DSI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1),
+ PV_MUXING_TEST("3 output: DSI0, VEC, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_HDMI0),
+ PV_MUXING_TEST("3 output: DSI0, VEC, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("3 output: DSI0, TXP, DSI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1),
+ PV_MUXING_TEST("3 output: DSI0, TXP, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI0),
+ PV_MUXING_TEST("3 output: DSI0, TXP, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("3 output: DSI0, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+ PV_MUXING_TEST("3 output: DSI0, DSI1, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("3 output: DSI0, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+};
+
+static void vc4_test_pv_muxing_desc(const struct pv_muxing_param *t, char *desc)
+{
+ strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
+}
+
+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[] = {
+ PV_MUXING_TEST("DPI/DSI0 Conflict",
+ VC4_ENCODER_TYPE_DPI, VC4_ENCODER_TYPE_DSI0),
+ PV_MUXING_TEST("More than 3 output: DPI, VEC, TXP, DSI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1),
+ PV_MUXING_TEST("More than 3 output: DPI, VEC, TXP, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI0),
+ PV_MUXING_TEST("More than 3 output: DPI, VEC, TXP, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("More than 3 output: DPI, VEC, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+ PV_MUXING_TEST("More than 3 output: DPI, VEC, DSI1, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("More than 3 output: DPI, VEC, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("More than 3 output: DPI, TXP, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+ PV_MUXING_TEST("More than 3 output: DPI, TXP, DSI1, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("More than 3 output: DPI, TXP, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("More than 3 output: DPI, DSI1, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("More than 3 output: 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),
+ PV_MUXING_TEST("More than 3 output: 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),
+ PV_MUXING_TEST("More than 3 output: 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),
+ PV_MUXING_TEST("More than 3 output: 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),
+ PV_MUXING_TEST("More than 3 output: 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),
+ PV_MUXING_TEST("More than 3 output: DSI0, VEC, TXP, DSI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1),
+ PV_MUXING_TEST("More than 3 output: DSI0, VEC, TXP, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI0),
+ PV_MUXING_TEST("More than 3 output: DSI0, VEC, TXP, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("More than 3 output: DSI0, VEC, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+ PV_MUXING_TEST("More than 3 output: DSI0, VEC, DSI1, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("More than 3 output: DSI0, VEC, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("More than 3 output: DSI0, TXP, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+ PV_MUXING_TEST("More than 3 output: DSI0, TXP, DSI1, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("More than 3 output: DSI0, TXP, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("More than 3 output: DSI0, DSI1, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ PV_MUXING_TEST("More than 3 output: 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),
+ PV_MUXING_TEST("More than 3 output: 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),
+ PV_MUXING_TEST("More than 3 output: 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),
+ PV_MUXING_TEST("More than 3 output: 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),
+ PV_MUXING_TEST("More than 3 output: 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),
+ PV_MUXING_TEST("More than 3 output: 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),
+ PV_MUXING_TEST("More than 3 output: 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),
+ PV_MUXING_TEST("More than 3 output: 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_vc5_test_pv_muxing(struct kunit *test)
+{
+ const struct pv_muxing_priv *priv = test->priv;
+ const struct pv_muxing_param *params = test->param_value;
+ struct vc4_dev *vc4 = priv->vc4;
+ struct drm_device *drm = &vc4->base;
+ 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, drm, enc_type, state);
+ 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));
+ }
+}
+
+static void drm_vc4_test_pv_muxing_invalid(struct kunit *test)
+{
+ const struct pv_muxing_priv *priv = test->priv;
+ const struct pv_muxing_param *params = test->param_value;
+ struct vc4_dev *vc4 = priv->vc4;
+ struct drm_device *drm = &vc4->base;
+ 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, drm, enc_type, state);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ }
+
+ ret = drm_atomic_check_only(state);
+ KUNIT_EXPECT_LT(test, ret, 0);
+}
+
+static int vc5_pv_muxing_test_init(struct kunit *test)
+{
+ 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 = vc5_mock_device(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 vc5_pv_muxing_tests[] = {
+ KUNIT_CASE_PARAM(drm_vc5_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",
+ .init = vc5_pv_muxing_test_init,
+ .exit = vc4_pv_muxing_test_exit,
+ .test_cases = vc5_pv_muxing_tests,
+};
+
+kunit_test_suite(vc5_pv_muxing_test_suite);

--
2.38.1-b4-0.11.0-dev-d416f

David Gow

unread,
Nov 24, 2022, 3:31:27 AM11/24/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
Hi Maxime,

Thanks very much for this! I'm really excited to see these sorts of
tests being written.

I was able to successfully run these under qemu with:
./tools/testing/kunit/kunit.py run --kunitconfig
drivers/gpu/drm/vc4/tests --arch arm64
--cross_compile=aarch64-linux-gnu-
(and also with clang, using --make_options LLVM=1 instead of the
--cross_compile flag)

On the other hand, they don't compile as a module:
ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/drm/vc4/tests/vc4_mock.o
ERROR: modpost: missing MODULE_LICENSE() in
drivers/gpu/drm/vc4/tests/vc4_mock_crtc.o
ERROR: modpost: missing MODULE_LICENSE() in
drivers/gpu/drm/vc4/tests/vc4_mock_output.o
ERROR: modpost: missing MODULE_LICENSE() in
drivers/gpu/drm/vc4/tests/vc4_mock_plane.o
ERROR: modpost: missing MODULE_LICENSE() in
drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.o
ERROR: modpost: missing MODULE_LICENSE() in
drivers/gpu/drm/tests/drm_managed_test.o
ERROR: modpost: "vc4_drm_driver"
[drivers/gpu/drm/vc4/tests/vc4_mock.ko] undefined!
ERROR: modpost: "vc5_drm_driver"
[drivers/gpu/drm/vc4/tests/vc4_mock.ko] undefined!
ERROR: modpost: "drm_kunit_helper_alloc_device"
[drivers/gpu/drm/vc4/tests/vc4_mock.ko] undefined!
ERROR: modpost: "__drm_kunit_helper_alloc_drm_device_with_driver"
[drivers/gpu/drm/vc4/tests/vc4_mock.ko] undefined!
ERROR: modpost: "__vc4_hvs_alloc"
[drivers/gpu/drm/vc4/tests/vc4_mock.ko] undefined!
ERROR: modpost: "vc4_dummy_plane"
[drivers/gpu/drm/vc4/tests/vc4_mock.ko] undefined!
ERROR: modpost: "vc4_mock_pv" [drivers/gpu/drm/vc4/tests/vc4_mock.ko] undefined!
ERROR: modpost: "vc4_dummy_output"
[drivers/gpu/drm/vc4/tests/vc4_mock.ko] undefined!
ERROR: modpost: "vc4_kms_load" [drivers/gpu/drm/vc4/tests/vc4_mock.ko]
undefined!
ERROR: modpost: "vc4_txp_crtc_data"
[drivers/gpu/drm/vc4/tests/vc4_mock.ko] undefined!
WARNING: modpost: suppressed 17 unresolved symbol warnings because
there were too many)

Most of those are just the need to export some symbols. There's some
work underway to support conditionally exporting symbols only if KUnit
is enabled, which may help:
https://lore.kernel.org/linux-kselftest/20221102175959....@google.com/

Otherwise, I suspect the better short-term solution would just be to
require that the tests are built-in (or at least compiled into
whatever of the drm/vc4 modules makes most sense).

The only other thing which has me a little confused is the naming of
some of the functions, specifically with the __ prefix. Is it just for
internal functions (many of them aren't static, but maybe they could
use the VISIBLE_IF_KUNIT macro if that makes sense), or for versions
of functions which accept extra arguments? Not a big deal (and maybe
it's a DRM naming convention I'm ignorant of), but I couldn't quite
find a pattern on my first read through.

But on the whole, these look good from a KUnit point-of-view. It's
really to see some solid mocking and driver testing, too. There would
be ways to avoid passing the 'struct kunit' around in more places (or
to store extra data as a kunit_resource), but I think it's better
overall to pass it around like you have in this case -- it's certainly
more compatible with things which might span threads (e.g. the
workqueues).

Thanks a bunch,
-- David

Maxime Ripard

unread,
Nov 24, 2022, 9:01:17 AM11/24/22
to David Gow, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, Javier Martinez Canillas, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
Hi David,

On Thu, Nov 24, 2022 at 04:31:14PM +0800, David Gow wrote:
> On Wed, Nov 23, 2022 at 11:28 PM Maxime Ripard <max...@cerno.tech> wrote:
> >
> > 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.
>
Thanks I'll fix it

> Most of those are just the need to export some symbols. There's some
> work underway to support conditionally exporting symbols only if KUnit
> is enabled, which may help:
> https://lore.kernel.org/linux-kselftest/20221102175959....@google.com/

That's awesome :)

The current solution to include the test implementation is not ideal, so
it's great to see a nicer solution being worked on.

> Otherwise, I suspect the better short-term solution would just be to
> require that the tests are built-in (or at least compiled into
> whatever of the drm/vc4 modules makes most sense).
>
> The only other thing which has me a little confused is the naming of
> some of the functions, specifically with the __ prefix. Is it just for
> internal functions (many of them aren't static, but maybe they could
> use the VISIBLE_IF_KUNIT macro if that makes sense), or for versions
> of functions which accept extra arguments?

It was for internal functions that would definitely benefit from
VISIBLE_IF_KUNIT indeed

> Not a big deal (and maybe it's a DRM naming convention I'm ignorant
> of), but I couldn't quite find a pattern on my first read through.
>
> But on the whole, these look good from a KUnit point-of-view. It's
> really to see some solid mocking and driver testing, too. There would
> be ways to avoid passing the 'struct kunit' around in more places (or
> to store extra data as a kunit_resource), but I think it's better
> overall to pass it around like you have in this case -- it's certainly
> more compatible with things which might span threads (e.g. the
> workqueues).

One thing I'm really unsure about and would like your input on is
basically the entire device instantiation code in drm_kunit_helpers.c

It's a little fishy since it will allocate a platform_device while the
driver might expect some other bus device. And the code to bind the
driver based around probe and workqueues seems like a hack.

This is something that would benefit from having proper functions in
kunit to allocate a proper device for a given test. This is already
something that other unit test suites seems to get wrong, and I'm sure
there's some bugs somewhere in the helpers I did for DRM. What do you
think?

Maxime

Javier Martinez Canillas

unread,
Nov 25, 2022, 3:50:37 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:25, Maxime Ripard wrote:
> 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.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>
> ---

Reviewed-by: Javier Martinez Canillas <jav...@redhat.com>

--
Best regards,

Javier Martinez Canillas
Core Platforms
Red Hat

Javier Martinez Canillas

unread,
Nov 25, 2022, 3:57:29 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:25, Maxime Ripard wrote:
> The device name isn't really useful, we can just define it instead of
> exposing it in the API.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>
> ---
> drivers/gpu/drm/tests/drm_client_modeset_test.c | 2 +-
> drivers/gpu/drm/tests/drm_kunit_helpers.c | 6 ++++--
> drivers/gpu/drm/tests/drm_kunit_helpers.h | 3 +--
> 3 files changed, 6 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c b/drivers/gpu/drm/tests/drm_client_modeset_test.c
> index e469d1634e2d..6920c3ffdfdf 100644
> --- a/drivers/gpu/drm/tests/drm_client_modeset_test.c
> +++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c
> @@ -41,7 +41,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 a4ad030ed101..7f69f56f5892 100644
> --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c
> +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c
> @@ -9,6 +9,8 @@
>
> #include "drm_kunit_helpers.h"
>
> +#define FAKE_DEVICE_NAME "drm-kunit-fake-device"
> +

I think that KUNIT_DEVICE_NAME and "drm-kunit-mock-device" are more descriptive
names here. But I'm also OK with the patch as is.

Javier Martinez Canillas

unread,
Nov 25, 2022, 4:06:44 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:25, Maxime Ripard wrote:
> 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.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>
> ---

[...]

> -static int dev_init(struct kunit_resource *res, void *ptr)
> +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(FAKE_DEVICE_NAME);

I was going to ask if a platform device could be used here, to prevent the
issue you mentioned the other day of root_device not having a bus set. But
I see now that you are doing that change in the next patch.

Javier Martinez Canillas

unread,
Nov 25, 2022, 4:25:31 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:25, Maxime Ripard wrote:
> 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.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>
> ---
> drivers/gpu/drm/tests/drm_kunit_helpers.c | 19 +++++++++++++++++--
> 1 file changed, 17 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c
> index 2f974f45c5b4..651aa93a98a8 100644
> --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c
> +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c
> @@ -6,6 +6,7 @@
> #include <kunit/resource.h>
>
> #include <linux/device.h>
> +#include <linux/platform_device.h>
>
> #include "drm_kunit_helpers.h"
>
> @@ -20,12 +21,26 @@ 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(FAKE_DEVICE_NAME);
> + struct platform_device *pdev;
> + int ret;
> +
> + ret = platform_driver_register(&fake_platform_driver);

Maybe the platform_driver_register() is something that should be done
in the drm_client_modeset_test_init() callback ?

I would also as a part of this series rename the kunit_suite from
"drm_test_pick_cmdline_test_suite" to "drm_test_modeset_test_suite"
or something like that. Since you are already setting the .init and
.exit callbacks to allocate and release the devices. So it is more
than just a test suite for cmdline params testing.

> + KUNIT_ASSERT_EQ(test, ret, 0);
> +
> + pdev = platform_device_alloc(FAKE_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;
> }
>

Probably won't do it as a part of this series to avoid making it cross
subsystem, but I think there's room to further consolidation since this
helper seems to be quite generic and something that would be useful for
other subsystems. So it would make sense for kunit to have helpers to
manage platform drivers and devices.

In fact, I see that drivers/base/test/test_async_driver_probe.c already
has similar helpers that could be exposed to the rest of the kernel.

The patch looks good to me though and any of these changes could be done
as a follow-up:

Javier Martinez Canillas

unread,
Nov 25, 2022, 5:19:48 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:25, Maxime Ripard wrote:
> 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.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>
> ---
> drivers/gpu/drm/tests/drm_kunit_helpers.c | 60 +++++++++++++++++++++++++++++++
> 1 file changed, 60 insertions(+)
>

If I understood the platform core code correctly, the probe is always sync in
the case of platform drivers. Unless .probe_type = PROBE_PREFER_ASYNCHRONOUS is
set or a module is loaded using with the "async_probe=1" parameter. So I believe
the wait queue won't be needed. The only DRM driver that forces an async probe is
drivers/gpu/drm/hyperv/hyperv_drm_drv.c AFAICT.

So I would drop this patch from the set for now.

Javier Martinez Canillas

unread,
Nov 25, 2022, 5:46:31 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:25, Maxime Ripard wrote:
> 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.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>
> ---

Reviewed-by: Javier Martinez Canillas <jav...@redhat.com>

Javier Martinez Canillas

unread,
Nov 25, 2022, 5:48:39 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:25, Maxime Ripard wrote:
> 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.

Javier Martinez Canillas

unread,
Nov 25, 2022, 5:52:41 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:25, Maxime Ripard wrote:
> 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.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>
> ---
> drivers/gpu/drm/tests/Makefile | 1 +
> drivers/gpu/drm/tests/drm_managed_test.c | 68 ++++++++++++++++++++++++++++++++
> 2 files changed, 69 insertions(+)
>
> diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile
> index b29ef1085cad..35962c6ef0c4 100644
> --- a/drivers/gpu/drm/tests/Makefile
> +++ b/drivers/gpu/drm/tests/Makefile
> @@ -9,6 +9,7 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \
> drm_format_test.o \
> drm_framebuffer_test.o \
> drm_kunit_helpers.o \
> + drm_managed_test.o \
> drm_mm_test.o \
> drm_plane_helper_test.o \
> drm_rect_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..4fc11b289d9e
> --- /dev/null
> +++ b/drivers/gpu/drm/tests/drm_managed_test.c
> @@ -0,0 +1,68 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <drm/drm_drv.h>
> +#include <drm/drm_managed.h>
> +
> +#include <kunit/resource.h>
> +
> +#include <linux/device.h>
> +
> +#include "drm_kunit_helpers.h"
> +
> +#define TEST_TIMEOUT_MS 100
> +

Why did you choose this timeout value? Maybe we can add some comments
explaining the rationale.

Javier Martinez Canillas

unread,
Nov 25, 2022, 5:54:09 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:25, Maxime Ripard wrote:
> The drm_atomic_get_(old|new)_*_state don't modify the passed
> drm_atomic_state, so we can make it const.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>
> ---

Javier Martinez Canillas

unread,
Nov 25, 2022, 6:00:51 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:25, Maxime Ripard wrote:
> On the vc4 devices (and later), the blending is done by a single device
> called the HVS. The HVS has three FIFO that can operate in parallel, and
> route their output to 6 CRTCs and 7 encoders on the BCM2711.
>
> Each of these CRTCs and encoders have some contraints on which FIFO they

constraints.

> can feed from, so we need some code to take all those constraints into
> account and assign FIFOs to CRTCs.
>
> The problem can be simplified by assigning those FIFOs to CRTCs by
> ascending output index number. We had a comment mentioning it already,
> but we were never actually enforcing it.
>
> It was working still in most situations because the probe order is
> roughly equivalent, except for the (optional, and fairly rarely used on
> the Pi4) VEC which was last in the probe order sequence, but one of the
> earliest device to assign.
>
> This resulted in configurations that were rejected by our code but were
> still valid with a different assignment.
>
> We can fix this by making sure we assign CRTCs to FIFOs by ordering
> them by ascending HVS output index.
>
> Fixes: 87ebcd42fb7b ("drm/vc4: crtc: Assign output to channel automatically")
> Signed-off-by: Maxime Ripard <max...@cerno.tech>
> ---

[...]

>
> - for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
> - struct vc4_crtc_state *old_vc4_crtc_state =
> - to_vc4_crtc_state(old_crtc_state);
> - struct vc4_crtc_state *new_vc4_crtc_state =
> - to_vc4_crtc_state(new_crtc_state);
> - struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
> + /*
> + * The problem we have to solve here is that we have up to 7
> + * encoders, connected to up to 6 CRTCs.
> + *
> + * Those CRTCs, depending on the instance, can be routed to 1, 2
> + * or 3 HVS FIFOs, and we need to set the change the muxing

This sentence sounds a little bit off to me. Did you mean:

"we need to set the muxing between" or "we need to change the muxing" ?

I'm not familiar with VC4 but the patch seems to do what the commit message
says, so the changes look good to me.

Javier Martinez Canillas

unread,
Nov 25, 2022, 6:01:55 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:25, Maxime Ripard wrote:
> None of our wrappers around container_of to access our objects from the
> DRM object pointer actually modify the latter.
>
> Let's make them const.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>
> ---

Javier Martinez Canillas

unread,
Nov 25, 2022, 6:04:52 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:25, Maxime Ripard wrote:
> 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.

Javier Martinez Canillas

unread,
Nov 25, 2022, 6:05:35 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:25, Maxime Ripard wrote:
> The vc4_hvs_get_(old|new)_global_state functions don't modify the
> drm_atomic_state passed as an argument, so let's make it const.

Javier Martinez Canillas

unread,
Nov 25, 2022, 6:07:14 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:25, Maxime Ripard wrote:
> The current order of variable assignments is unneccessarily complex,
> let's make it simpler.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>
> ---
Much easier to follow indeed.

Javier Martinez Canillas

unread,
Nov 25, 2022, 6:11:26 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:25, Maxime Ripard wrote:
> The TXP is integrated as a separate CRTC/Encoder/Connector combo, but
> for some reason doesn't rely on the vc4_encoder type and it's associated
> type.
>
> Let's create a type to make it consistent with the other encoders.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>
> ---

Javier Martinez Canillas

unread,
Nov 25, 2022, 6:12:15 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:25, Maxime Ripard wrote:
> It makes more sense to register the CRTC before the encoder and
> connectors, so let's move our call around.

Javier Martinez Canillas

unread,
Nov 25, 2022, 6:13:42 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:25, Maxime Ripard wrote:
> Both users of vc4_crtc_init need the same extra initialization to set
> the pointer to the platform_device and the CRTC data. Since it's
> mandatory, let's make them both arguments of vc4_crtc_init().

Javier Martinez Canillas

unread,
Nov 25, 2022, 6:29:35 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:26, Maxime Ripard wrote:
> 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.

Javier Martinez Canillas

unread,
Nov 25, 2022, 6:30:36 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:26, Maxime Ripard wrote:
> 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.

Javier Martinez Canillas

unread,
Nov 25, 2022, 6:32:22 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:26, Maxime Ripard wrote:
> It's fairly hard to figure out the instance of the CRTC affected by an
> atomic change using the default name.
>
> Since we can provide our own to the CRTC initialization functions, let's
> do so to make the debugging sessions easier.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>
> ---
> drivers/gpu/drm/vc4/vc4_crtc.c | 10 +++++++++-
> drivers/gpu/drm/vc4/vc4_drv.h | 2 ++
> drivers/gpu/drm/vc4/vc4_txp.c | 1 +
> 3 files changed, 12 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
> index 8bc30ad0904b..59e473059fa2 100644
> --- a/drivers/gpu/drm/vc4/vc4_crtc.c
> +++ b/drivers/gpu/drm/vc4/vc4_crtc.c
> @@ -1118,6 +1118,7 @@ static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
>
> static const struct vc4_pv_data bcm2835_pv0_data = {
> .base = {
> + .name = "pixelvalve-0",

I wonder if would make sense to add the SoC name too, but either way:

Javier Martinez Canillas

unread,
Nov 25, 2022, 6:41:29 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, linaro...@lists.linaro.org, Greg Kroah-Hartman, linux-k...@vger.kernel.org, Maíra Canal, linux...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, Brendan Higgins, linux-...@vger.kernel.org, Dave Stevenson
On 11/23/22 16:26, Maxime Ripard wrote:
> 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.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>
> ---

kernel test robot

unread,
Nov 25, 2022, 7:54:32 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, oe-kbu...@lists.linux.dev, David Gow, Brendan Higgins, Greg Kroah-Hartman, Dave Stevenson, Javier Martinez Canillas, dri-...@lists.freedesktop.org, linux-...@vger.kernel.org, linaro...@lists.linaro.org, Maíra Canal, linux-k...@vger.kernel.org, kuni...@googlegroups.com, linux...@vger.kernel.org
Hi Maxime,

I love your patch! Yet something to improve:

[auto build test ERROR on 35c3a2d02f0dc153a5f2f304ba33e1436b6a8d8f]

url: https://github.com/intel-lab-lkp/linux/commits/Maxime-Ripard/drm-Introduce-Kunit-Tests-to-VC4/20221123-233528
base: 35c3a2d02f0dc153a5f2f304ba33e1436b6a8d8f
patch link: https://lore.kernel.org/r/20221123-rpi-kunit-tests-v1-22-051a0bb60a16%40cerno.tech
patch subject: [PATCH 22/24] drm/vc4: tests: Introduce a mocking infrastructure
config: arc-randconfig-c024-20221123
compiler: arceb-elf-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/b7d27608f26b831b79b9e4e25bce033d5284eb4c
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Maxime-Ripard/drm-Introduce-Kunit-Tests-to-VC4/20221123-233528
git checkout b7d27608f26b831b79b9e4e25bce033d5284eb4c
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=arc SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <l...@intel.com>

All errors (new ones prefixed by >>, old ones prefixed by <<):

ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/drm/tests/drm_managed_test.o
ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/drm/vc4/tests/vc4_mock.o
ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/drm/vc4/tests/vc4_mock_crtc.o
ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/drm/vc4/tests/vc4_mock_output.o
ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/drm/vc4/tests/vc4_mock_plane.o
ERROR: modpost: "drm_kunit_helper_alloc_device" [drivers/gpu/drm/tests/drm_managed_test.ko] undefined!
ERROR: modpost: "__drm_kunit_helper_alloc_drm_device_with_driver" [drivers/gpu/drm/tests/drm_managed_test.ko] undefined!
ERROR: modpost: "drm_kunit_helper_free_device" [drivers/gpu/drm/tests/drm_managed_test.ko] undefined!
>> ERROR: modpost: "vc4_dummy_plane" [drivers/gpu/drm/vc4/tests/vc4_mock.ko] undefined!
>> ERROR: modpost: "vc4_mock_pv" [drivers/gpu/drm/vc4/tests/vc4_mock.ko] undefined!
>> ERROR: modpost: "vc4_dummy_output" [drivers/gpu/drm/vc4/tests/vc4_mock.ko] undefined!
>> ERROR: modpost: "vc4_drm_driver" [drivers/gpu/drm/vc4/tests/vc4_mock.ko] undefined!
>> ERROR: modpost: "vc5_drm_driver" [drivers/gpu/drm/vc4/tests/vc4_mock.ko] undefined!
>> ERROR: modpost: "drm_kunit_helper_alloc_device" [drivers/gpu/drm/vc4/tests/vc4_mock.ko] undefined!
>> ERROR: modpost: "__drm_kunit_helper_alloc_drm_device_with_driver" [drivers/gpu/drm/vc4/tests/vc4_mock.ko] undefined!
WARNING: modpost: suppressed 13 unresolved symbol warnings because there were too many)

--
0-DAY CI Kernel Test Service
https://01.org/lkp
config

Maíra Canal

unread,
Nov 25, 2022, 9:10:13 AM11/25/22
to Maxime Ripard, Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, Brendan Higgins, Greg Kroah-Hartman, Dave Stevenson, Javier Martinez Canillas, dri-...@lists.freedesktop.org, linux-...@vger.kernel.org, linaro...@lists.linaro.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, linux...@vger.kernel.org
On 11/23/22 12:25, Maxime Ripard wrote:
> 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.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>

Although I believe using "drm_device" on the function name is a bit
redundant (maybe drm_kunit_helper_alloc_dev or
drm_kunit_helper_alloc_device would be cleaner),

Reviewed-by: Maíra Canal <maira...@riseup.net>

Best Regards,
- Maíra Canal

> ---
> drivers/gpu/drm/tests/drm_client_modeset_test.c | 2 +-
> drivers/gpu/drm/tests/drm_kunit_helpers.c | 4 +++-
> drivers/gpu/drm/tests/drm_kunit_helpers.h | 5 ++++-
> 3 files changed, 8 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c b/drivers/gpu/drm/tests/drm_client_modeset_test.c
> index 362a5fbd82f5..e469d1634e2d 100644
> --- a/drivers/gpu/drm/tests/drm_client_modeset_test.c
> +++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c
> @@ -41,7 +41,7 @@ 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 f1662091f250..a4ad030ed101 100644
> --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c
> +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c
> @@ -36,7 +36,9 @@ static void dev_free(struct kunit_resource *res)
> root_device_unregister(dev);
> }
>
> -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;
> diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.h b/drivers/gpu/drm/tests/drm_kunit_helpers.h
> index 20ab6eec4c89..e9870c7911fe 100644
> --- a/drivers/gpu/drm/tests/drm_kunit_helpers.h
> +++ b/drivers/gpu/drm/tests/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_
>

Maxime Ripard

unread,
Nov 25, 2022, 9:33:43 AM11/25/22
to Maíra Canal, Maarten Lankhorst, Daniel Vetter, David Airlie, Thomas Zimmermann, David Gow, Brendan Higgins, Greg Kroah-Hartman, Dave Stevenson, Javier Martinez Canillas, dri-...@lists.freedesktop.org, linux-...@vger.kernel.org, linaro...@lists.linaro.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, linux...@vger.kernel.org
Hi,

On Fri, Nov 25, 2022 at 11:10:02AM -0300, Maíra Canal wrote:
> On 11/23/22 12:25, Maxime Ripard wrote:
> > 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.
> >
> > Signed-off-by: Maxime Ripard <max...@cerno.tech>
>
> Although I believe using "drm_device" on the function name is a bit
> redundant (maybe drm_kunit_helper_alloc_dev or drm_kunit_helper_alloc_device
> would be cleaner),

Yeah, I don't quite like the name either, but we'll need to also
allocate a struct device in the next few patches so we need to make the
distinction between a struct drm_device and a struct device.

Maxime
signature.asc

Maxime Ripard

unread,
Nov 28, 2022, 6:05:09 AM11/28/22
to Daniel Vetter, David Airlie, Thomas Zimmermann, Maarten Lankhorst, Maxime Ripard, Maxime Ripard, Javier Martinez Canillas, dri-...@lists.freedesktop.org, linaro...@lists.linaro.org, linux-...@vger.kernel.org, David Gow, linux...@vger.kernel.org, Ma��ra Canal, linux-k...@vger.kernel.org, Greg Kroah-Hartman, Brendan Higgins, kuni...@googlegroups.com, Dave Stevenson
On Wed, 23 Nov 2022 16:25:51 +0100, Maxime Ripard wrote:
> The drm_atomic_get_(old|new)_*_state don't modify the passed
> drm_atomic_state, so we can make it const.
>
>

Applied to drm/drm-misc (drm-misc-next).

Thanks!
Maxime

Maxime Ripard

unread,
Nov 28, 2022, 6:05:10 AM11/28/22
to Daniel Vetter, David Airlie, Thomas Zimmermann, Maarten Lankhorst, Maxime Ripard, Maxime Ripard, dri-...@lists.freedesktop.org, Javier Martinez Canillas, linaro...@lists.linaro.org, linux-...@vger.kernel.org, David Gow, linux...@vger.kernel.org, Ma��ra Canal, linux-k...@vger.kernel.org, Greg Kroah-Hartman, Brendan Higgins, kuni...@googlegroups.com, Dave Stevenson
On Wed, 23 Nov 2022 16:25:52 +0100, Maxime Ripard wrote:
> On the vc4 devices (and later), the blending is done by a single device
> called the HVS. The HVS has three FIFO that can operate in parallel, and
> route their output to 6 CRTCs and 7 encoders on the BCM2711.
>
> Each of these CRTCs and encoders have some contraints on which FIFO they
> can feed from, so we need some code to take all those constraints into
> account and assign FIFOs to CRTCs.
>
> [...]

Maxime Ripard

unread,
Nov 28, 2022, 6:05:13 AM11/28/22
to Daniel Vetter, David Airlie, Thomas Zimmermann, Maarten Lankhorst, Maxime Ripard, Maxime Ripard, linaro...@lists.linaro.org, Javier Martinez Canillas, dri-...@lists.freedesktop.org, linux-...@vger.kernel.org, David Gow, linux...@vger.kernel.org, Ma��ra Canal, linux-k...@vger.kernel.org, Greg Kroah-Hartman, Brendan Higgins, kuni...@googlegroups.com, Dave Stevenson
On Wed, 23 Nov 2022 16:25:53 +0100, Maxime Ripard wrote:
> None of our wrappers around container_of to access our objects from the
> DRM object pointer actually modify the latter.
>
> Let's make them const.
>
>

Maxime Ripard

unread,
Nov 28, 2022, 6:05:14 AM11/28/22
to Daniel Vetter, David Airlie, Thomas Zimmermann, Maarten Lankhorst, Maxime Ripard, Maxime Ripard, linaro...@lists.linaro.org, dri-...@lists.freedesktop.org, Javier Martinez Canillas, David Gow, linux-...@vger.kernel.org, linux...@vger.kernel.org, Ma��ra Canal, linux-k...@vger.kernel.org, Greg Kroah-Hartman, Brendan Higgins, kuni...@googlegroups.com, Dave Stevenson
On Wed, 23 Nov 2022 16:25:55 +0100, Maxime Ripard wrote:
> The vc4_hvs_get_(old|new)_global_state functions don't modify the
> drm_atomic_state passed as an argument, so let's make it const.

Maxime Ripard

unread,
Nov 28, 2022, 6:05:17 AM11/28/22
to Daniel Vetter, David Airlie, Thomas Zimmermann, Maarten Lankhorst, Maxime Ripard, Maxime Ripard, Javier Martinez Canillas, linaro...@lists.linaro.org, dri-...@lists.freedesktop.org, linux-...@vger.kernel.org, David Gow, linux...@vger.kernel.org, Ma��ra Canal, linux-k...@vger.kernel.org, Greg Kroah-Hartman, Brendan Higgins, kuni...@googlegroups.com, Dave Stevenson
On Wed, 23 Nov 2022 16:25:56 +0100, Maxime Ripard wrote:
> The current order of variable assignments is unneccessarily complex,
> let's make it simpler.
>
>

Maxime Ripard

unread,
Nov 28, 2022, 6:05:18 AM11/28/22
to Daniel Vetter, David Airlie, Thomas Zimmermann, Maarten Lankhorst, Maxime Ripard, Maxime Ripard, dri-...@lists.freedesktop.org, linaro...@lists.linaro.org, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow, linux...@vger.kernel.org, Ma��ra Canal, linux-k...@vger.kernel.org, Greg Kroah-Hartman, Brendan Higgins, kuni...@googlegroups.com, Dave Stevenson
On Wed, 23 Nov 2022 16:25:57 +0100, Maxime Ripard wrote:
> The TXP is integrated as a separate CRTC/Encoder/Connector combo, but
> for some reason doesn't rely on the vc4_encoder type and it's associated
> type.
>
> Let's create a type to make it consistent with the other encoders.
>
>
> [...]

Maxime Ripard

unread,
Nov 28, 2022, 6:05:20 AM11/28/22
to Daniel Vetter, David Airlie, Thomas Zimmermann, Maarten Lankhorst, Maxime Ripard, Maxime Ripard, linaro...@lists.linaro.org, dri-...@lists.freedesktop.org, Javier Martinez Canillas, David Gow, linux-...@vger.kernel.org, linux...@vger.kernel.org, Ma��ra Canal, linux-k...@vger.kernel.org, Greg Kroah-Hartman, Brendan Higgins, kuni...@googlegroups.com, Dave Stevenson
On Wed, 23 Nov 2022 16:25:58 +0100, Maxime Ripard wrote:
> It makes more sense to register the CRTC before the encoder and
> connectors, so let's move our call around.
>
>

Maxime Ripard

unread,
Nov 28, 2022, 6:05:22 AM11/28/22
to Daniel Vetter, David Airlie, Thomas Zimmermann, Maarten Lankhorst, Maxime Ripard, Maxime Ripard, Javier Martinez Canillas, linaro...@lists.linaro.org, dri-...@lists.freedesktop.org, David Gow, linux-...@vger.kernel.org, linux...@vger.kernel.org, Ma��ra Canal, linux-k...@vger.kernel.org, Greg Kroah-Hartman, Brendan Higgins, kuni...@googlegroups.com, Dave Stevenson
On Wed, 23 Nov 2022 16:25:59 +0100, Maxime Ripard wrote:
> Both users of vc4_crtc_init need the same extra initialization to set
> the pointer to the platform_device and the CRTC data. Since it's
> mandatory, let's make them both arguments of vc4_crtc_init().
>
>

Maxime Ripard

unread,
Nov 28, 2022, 6:05:24 AM11/28/22
to Daniel Vetter, David Airlie, Thomas Zimmermann, Maarten Lankhorst, Maxime Ripard, Maxime Ripard, dri-...@lists.freedesktop.org, linaro...@lists.linaro.org, Javier Martinez Canillas, David Gow, linux-...@vger.kernel.org, linux...@vger.kernel.org, Ma��ra Canal, linux-k...@vger.kernel.org, Greg Kroah-Hartman, Brendan Higgins, kuni...@googlegroups.com, Dave Stevenson
On Wed, 23 Nov 2022 16:26:02 +0100, Maxime Ripard wrote:
> It's fairly hard to figure out the instance of the CRTC affected by an
> atomic change using the default name.
>
> Since we can provide our own to the CRTC initialization functions, let's
> do so to make the debugging sessions easier.
>
>
> [...]

Maxime Ripard

unread,
Nov 28, 2022, 9:56:38 AM11/28/22
to Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, Maxime Ripard, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow
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 <air...@gmail.com>
To: Daniel Vetter <dan...@ffwll.ch>
To: Maarten Lankhorst <maarten....@linux.intel.com>
To: Maxime Ripard <mri...@kernel.org>
To: Thomas Zimmermann <tzimm...@suse.de>
Cc: Dave Stevenson <dave.st...@raspberrypi.com>
Cc: Javier Martinez Canillas <jav...@redhat.com>
Cc: Greg Kroah-Hartman <gre...@linuxfoundation.org>
Cc: Maíra Canal <maira...@riseup.net>
Cc: Brendan Higgins <brendan...@linux.dev>
Cc: David Gow <davi...@google.com>
Cc: linux-k...@vger.kernel.org
Cc: kuni...@googlegroups.com
Cc: dri-...@lists.freedesktop.org
Cc: linux-...@vger.kernel.org
Cc: linux...@vger.kernel.org
Cc: linaro...@lists.linaro.org
Signed-off-by: Maxime Ripard <max...@cerno.tech>

---
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-te...@cerno.tech

---
Maxime Ripard (17):
drm/tests: helpers: Move the helper header to include/drm
drm/tests: helpers: Document drm_kunit_device_init()
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

drivers/gpu/drm/tests/Makefile | 1 +
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 | 15 +
drivers/gpu/drm/vc4/Makefile | 7 +
drivers/gpu/drm/vc4/tests/.kunitconfig | 14 +
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 | 102 ++-
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 ++
27 files changed, 2087 insertions(+), 172 deletions(-)
---
base-commit: 199557fab92548f8e9d5207e385097213abe0cab
change-id: 20221123-rpi-kunit-tests-87a388492a73

Best regards,
--
Maxime Ripard <max...@cerno.tech>

Maxime Ripard

unread,
Nov 28, 2022, 9:56:44 AM11/28/22
to Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, Maxime Ripard, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow
We'll need to use those helpers from drivers too, so let's move it to a
more visible location.

Signed-off-by: Maxime Ripard <max...@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

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 28, 2022, 9:56:52 AM11/28/22
to Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, Maxime Ripard, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow
Commit 44a3928324e9 ("drm/tests: Add Kunit Helpers") introduced the
drm_kunit_device_init() function but didn't document it properly. Add
that documentation.

Signed-off-by: Maxime Ripard <max...@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..46a68c2fd80c 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;

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 28, 2022, 9:56:56 AM11/28/22
to Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, Maxime Ripard, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow
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.

Signed-off-by: Maxime Ripard <max...@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 46a68c2fd80c..2f67f6cf78d0 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(drm_kunit_device_init);
+EXPORT_SYMBOL(drm_kunit_helper_alloc_drm_device);

MODULE_AUTHOR("Maxime Ripard <max...@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_

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 28, 2022, 9:57:02 AM11/28/22
to Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, Maxime Ripard, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow
The device name isn't really useful, we can just define it instead of
exposing it in the API.

Signed-off-by: Maxime Ripard <max...@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 2f67f6cf78d0..16c7926d83c2 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);

Maxime Ripard

unread,
Nov 28, 2022, 9:57:08 AM11/28/22
to Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, Maxime Ripard, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow
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.

Signed-off-by: Maxime Ripard <max...@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 16c7926d83c2..9fb045fa685f 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(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(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..438b1d42b843 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,

Maxime Ripard

unread,
Nov 28, 2022, 9:57:14 AM11/28/22
to Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, Maxime Ripard, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow
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 <jav...@redhat.com>
Signed-off-by: Maxime Ripard <max...@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 9fb045fa685f..15678ab823b0 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(drm_kunit_helper_alloc_device);

@@ -45,7 +55,9 @@ EXPORT_SYMBOL(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(drm_kunit_helper_free_device);


--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 28, 2022, 9:57:20 AM11/28/22
to Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, Maxime Ripard, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow
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.

Signed-off-by: Maxime Ripard <max...@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 15678ab823b0..5d3e29353d1a 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(drm_kunit_helper_free_device);


--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 28, 2022, 9:57:26 AM11/28/22
to Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, Maxime Ripard, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow
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.

Signed-off-by: Maxime Ripard <max...@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 5d3e29353d1a..47a4a7752bb7 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(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(drm_kunit_helper_alloc_drm_device);
+EXPORT_SYMBOL(__drm_kunit_helper_alloc_drm_device);

MODULE_AUTHOR("Maxime Ripard <max...@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 438b1d42b843..0dc8fb594c5c 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..7908c516f2ab 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))

Maxime Ripard

unread,
Nov 28, 2022, 9:57:33 AM11/28/22
to Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, Maxime Ripard, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow
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.

Signed-off-by: Maxime Ripard <max...@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 47a4a7752bb7..565623c6d7fe 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(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(__drm_kunit_helper_alloc_drm_device);
+EXPORT_SYMBOL(__drm_kunit_helper_alloc_drm_device_with_driver);

MODULE_AUTHOR("Maxime Ripard <max...@cerno.tech>");
MODULE_LICENSE("GPL");
diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h
index 7908c516f2ab..5ad2c1b8480e 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

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 28, 2022, 9:57:40 AM11/28/22
to Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, Maxime Ripard, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow
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 <jav...@redhat.com>
Signed-off-by: Maxime Ripard <max...@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 94fe546d937d..486053052ba9 100644
--- a/drivers/gpu/drm/tests/Makefile
+++ b/drivers/gpu/drm/tests/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \
drm_format_test.o \
drm_framebuffer_test.o \
drm_kunit_helpers.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 <max...@cerno.tech>");
+MODULE_LICENSE("GPL");

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 28, 2022, 9:57:46 AM11/28/22
to Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, Maxime Ripard, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow
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 <jav...@redhat.com>
Signed-off-by: Maxime Ripard <max...@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);

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 28, 2022, 9:57:52 AM11/28/22
to Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, Maxime Ripard, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow
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 <jav...@redhat.com>
Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/vc4/vc4_crtc.c | 52 +++++++++++++++++++++++++++---------------
drivers/gpu/drm/vc4/vc4_drv.h | 6 +++++
2 files changed, 39 insertions(+), 19 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index 333529ed3a0d..7a2c54efecb0 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -1286,31 +1286,20 @@ 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)
+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 +1331,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,

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 28, 2022, 9:57:59 AM11/28/22
to Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, Maxime Ripard, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow
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 <jav...@redhat.com>
Signed-off-by: Maxime Ripard <max...@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 7a2c54efecb0..59e473059fa2 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;


--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 28, 2022, 9:58:05 AM11/28/22
to Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, Maxime Ripard, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow
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 <jav...@redhat.com>
Signed-off-by: Maxime Ripard <max...@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,

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 28, 2022, 9:58:11 AM11/28/22
to Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, Maxime Ripard, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow
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.

Signed-off-by: Maxime Ripard <max...@cerno.tech>
---
drivers/gpu/drm/vc4/Kconfig | 15 +++
drivers/gpu/drm/vc4/Makefile | 6 +
drivers/gpu/drm/vc4/tests/.kunitconfig | 14 ++
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..dcdcc9e5cac4 100644
--- a/drivers/gpu/drm/vc4/Kconfig
+++ b/drivers/gpu/drm/vc4/Kconfig
@@ -34,3 +34,18 @@ 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
+ 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..bfd9899ed6e0
--- /dev/null
+++ b/drivers/gpu/drm/vc4/tests/.kunitconfig
@@ -0,0 +1,14 @@
+CONFIG_ARCH_BCM=y
+CONFIG_ARCH_BCM2835=y
+CONFIG_BCM2835_MBOX=y
+CONFIG_KUNIT=y
+CONFIG_DRM=y
+CONFIG_DRM_KUNIT_TEST=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 59e473059fa2..21b40d644ace 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),

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 28, 2022, 9:58:18 AM11/28/22
to Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, Maxime Ripard, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow
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.

Signed-off-by: Maxime Ripard <max...@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 21b40d644ace..ad868c24672a 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)

--
2.38.1-b4-0.11.0-dev-d416f

Maxime Ripard

unread,
Nov 28, 2022, 9:58:25 AM11/28/22
to Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, Maxime Ripard, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow
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.

Signed-off-by: Maxime Ripard <max...@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...@samsung.com/
+ * and
+ * https://lore.kernel.org/dri-devel/20200917121623...@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
+);

--
2.38.1-b4-0.11.0-dev-d416f

Maíra Canal

unread,
Nov 28, 2022, 2:31:20 PM11/28/22
to Maxime Ripard, Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, David Gow, Greg Kroah-Hartman, Dave Stevenson, Javier Martinez Canillas, dri-...@lists.freedesktop.org, linux-...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, linux-k...@vger.kernel.org, kuni...@googlegroups.com, linux...@vger.kernel.org
On 11/28/22 11:53, Maxime Ripard wrote:
> Commit 44a3928324e9 ("drm/tests: Add Kunit Helpers") introduced the
> drm_kunit_device_init() function but didn't document it properly. Add
> that documentation.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>

Just a minor nit on naming, besides that:

Reviewed-by: Maíra Canal <mca...@igalia.com>

> ---
> 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..46a68c2fd80c 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

s/Kunit/KUnit

Best Regards,
- Maíra Canal

Maíra Canal

unread,
Nov 28, 2022, 2:37:45 PM11/28/22
to Maxime Ripard, Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, David Gow, Greg Kroah-Hartman, Dave Stevenson, Javier Martinez Canillas, dri-...@lists.freedesktop.org, linux-...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, linux-k...@vger.kernel.org, kuni...@googlegroups.com, linux...@vger.kernel.org
On 11/28/22 11:53, Maxime Ripard wrote:
> The device name isn't really useful, we can just define it instead of
> exposing it in the API.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>

Reviewed-by: Maíra Canal <mca...@igalia.com>

Best Regards
- Maíra Canal

Maíra Canal

unread,
Nov 28, 2022, 2:49:13 PM11/28/22
to Maxime Ripard, Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, David Gow, Greg Kroah-Hartman, Dave Stevenson, Javier Martinez Canillas, dri-...@lists.freedesktop.org, linux-...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, linux-k...@vger.kernel.org, kuni...@googlegroups.com, linux...@vger.kernel.org
On 11/28/22 11:53, Maxime Ripard wrote:
> 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.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>

Just a small nit below,

Reviewed-by: Maíra Canal <mca...@igalia.com>

>
> diff --git a/drivers/gpu/drm/tests/drm_probe_helper_test.c b/drivers/gpu/drm/tests/drm_probe_helper_test.c
> index be61a92b79d2..438b1d42b843 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;;

There are two semicolons by the end of this statement.

Best Regards,
- Maíra Canal

Maíra Canal

unread,
Nov 28, 2022, 3:01:55 PM11/28/22
to Maxime Ripard, Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, David Gow, Greg Kroah-Hartman, Dave Stevenson, Javier Martinez Canillas, dri-...@lists.freedesktop.org, linux-...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, linux-k...@vger.kernel.org, kuni...@googlegroups.com, linux...@vger.kernel.org
On 11/28/22 11:53, Maxime Ripard wrote:
> 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 <jav...@redhat.com>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>

Reviewed-by: Maíra Canal <mca...@igalia.com>

Best Regards,
- Maíra Canal

Maíra Canal

unread,
Nov 28, 2022, 3:09:32 PM11/28/22
to Maxime Ripard, Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, David Gow, Greg Kroah-Hartman, Dave Stevenson, Javier Martinez Canillas, dri-...@lists.freedesktop.org, linux-...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, linux-k...@vger.kernel.org, kuni...@googlegroups.com, linux...@vger.kernel.org
On 11/28/22 11:53, Maxime Ripard wrote:
> 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.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>

Reviewed-by: Maíra Canal <mca...@igalia.com>

Best Regards,
- Maíra Canal

Maíra Canal

unread,
Nov 28, 2022, 3:14:45 PM11/28/22
to Maxime Ripard, Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, David Gow, Greg Kroah-Hartman, Dave Stevenson, Javier Martinez Canillas, dri-...@lists.freedesktop.org, linux-...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, linux-k...@vger.kernel.org, kuni...@googlegroups.com, linux...@vger.kernel.org
On 11/28/22 11:53, Maxime Ripard wrote:
> 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 <jav...@redhat.com>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>

Reviewed-by: Maíra Canal <mca...@igalia.com>

Best Regards,
- Maíra Canal

Maíra Canal

unread,
Nov 28, 2022, 3:17:19 PM11/28/22
to Maxime Ripard, Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, David Gow, Greg Kroah-Hartman, Dave Stevenson, Javier Martinez Canillas, dri-...@lists.freedesktop.org, linux-...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, linux-k...@vger.kernel.org, kuni...@googlegroups.com, linux...@vger.kernel.org
On 11/28/22 11:53, Maxime Ripard wrote:
> 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 <jav...@redhat.com>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>

Reviewed-by: Maíra Canal <mca...@igalia.com>

Best Regards,
- Maíra Canal

Maíra Canal

unread,
Nov 28, 2022, 3:24:04 PM11/28/22
to Maxime Ripard, Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, David Gow, Greg Kroah-Hartman, Dave Stevenson, Javier Martinez Canillas, dri-...@lists.freedesktop.org, linux-...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, linux-k...@vger.kernel.org, kuni...@googlegroups.com, linux...@vger.kernel.org
On 11/28/22 11:53, Maxime Ripard wrote:
> 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 <jav...@redhat.com>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>

Although the commit message explains a bit about why __vc4_crtc_init is
being created, it would be nice to add a comment in the code explaining
that __vc4_crtc_init can be used for tests as it allows mocking the
plane. This way the distinction between vc4_crtc_init and
__vc4_crtc_init will be cleaner to the reader.

Reviewed-by: Maíra Canal <mca...@igalia.com>

Best Regards,
- Maíra Canal

Maíra Canal

unread,
Nov 28, 2022, 3:26:53 PM11/28/22
to Maxime Ripard, Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Dave Stevenson, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow
On 11/28/22 11:53, Maxime Ripard wrote:
> 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 <jav...@redhat.com>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>

Reviewed-by: Maíra Canal <mca...@igalia.com>

Best Regards,
- Maíra Canal

Maíra Canal

unread,
Nov 28, 2022, 3:35:58 PM11/28/22
to Maxime Ripard, Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, David Gow, Greg Kroah-Hartman, Dave Stevenson, Javier Martinez Canillas, dri-...@lists.freedesktop.org, linux-...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, linux-k...@vger.kernel.org, kuni...@googlegroups.com, linux...@vger.kernel.org
On 11/28/22 11:53, Maxime Ripard wrote:
I'm not sure if it makes sense to run the DRM tests here as well. Maybe
it would be better to separate the compilation of the drm_kunit_helpers
from the compilation of the rest of the DRM tests. This way, this
Kconfig entry could be dropped and only the vc4 tests would run.
Moreover, it would allow to run the vc4 tests with --arch=arm.

Best Regards,
- Maíra Canal

Maíra Canal

unread,
Nov 28, 2022, 3:37:21 PM11/28/22
to Maxime Ripard, Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, David Gow, Greg Kroah-Hartman, Dave Stevenson, Javier Martinez Canillas, dri-...@lists.freedesktop.org, linux-...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, linux-k...@vger.kernel.org, kuni...@googlegroups.com, linux...@vger.kernel.org
On 11/28/22 11:53, Maxime Ripard wrote:
> 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.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>

Reviewed-by: Maíra Canal <mca...@igalia.com>

Best Regards,
- Maíra Canal

Maíra Canal

unread,
Nov 28, 2022, 3:48:24 PM11/28/22
to Maxime Ripard, Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Dave Stevenson, Javier Martinez Canillas, linux-...@vger.kernel.org, David Gow
On 11/28/22 11:53, Maxime Ripard wrote:
> 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

Hi Maxime,

It is great to see some device mocking with KUnit! Other than the
comments that I pointed out in the series, I believe that a small entry
on the VC4 documentation would be nice to cover how to run the tests and
also what the tests are currently covering.

Best Regards,
- Maíra Canal

>
> To: David Airlie <air...@gmail.com>
> To: Daniel Vetter <dan...@ffwll.ch>
> To: Maarten Lankhorst <maarten....@linux.intel.com>
> To: Maxime Ripard <mri...@kernel.org>
> To: Thomas Zimmermann <tzimm...@suse.de>
> Cc: Dave Stevenson <dave.st...@raspberrypi.com>
> Cc: Javier Martinez Canillas <jav...@redhat.com>
> Cc: Greg Kroah-Hartman <gre...@linuxfoundation.org>
> Cc: Maíra Canal <maira...@riseup.net>
> Cc: Brendan Higgins <brendan...@linux.dev>
> Cc: David Gow <davi...@google.com>
> Cc: linux-k...@vger.kernel.org
> Cc: kuni...@googlegroups.com
> Cc: dri-...@lists.freedesktop.org
> Cc: linux-...@vger.kernel.org
> Cc: linux...@vger.kernel.org
> Cc: linaro...@lists.linaro.org
> Signed-off-by: Maxime Ripard <max...@cerno.tech>
>
> ---
> 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-te...@cerno.tech
>
> ---
> Maxime Ripard (17):
> drm/tests: helpers: Move the helper header to include/drm
> drm/tests: helpers: Document drm_kunit_device_init()
> 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
>
> drivers/gpu/drm/tests/Makefile | 1 +
> 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 | 15 +
> drivers/gpu/drm/vc4/Makefile | 7 +
> drivers/gpu/drm/vc4/tests/.kunitconfig | 14 +
> 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 | 102 ++-
> 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 ++
> 27 files changed, 2087 insertions(+), 172 deletions(-)
> ---
> base-commit: 199557fab92548f8e9d5207e385097213abe0cab
> change-id: 20221123-rpi-kunit-tests-87a388492a73
>
> Best regards,

Javier Martinez Canillas

unread,
Nov 30, 2022, 3:00:10 AM11/30/22
to Maxime Ripard, Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, linux-...@vger.kernel.org, David Gow
On 11/28/22 15:53, Maxime Ripard wrote:
> We'll need to use those helpers from drivers too, so let's move it to a
> more visible location.
>
> Signed-off-by: Maxime Ripard <max...@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>

I wonder if now that this header was moved outside of the tests directory,
if we should add stub functions in the header file that are just defined
but do nothing if CONFIG_DRM_KUNIT_TEST isn't enabled. So that including
it in drivers will be a no-op.

Or do you plan to conditionally include this header file in drivers? So
that is only included when CONFIG_DRM_KUNIT_TEST is enabled?

Another thing that wondered is if we want a different namespace for this
header, i.e: <drm/testing/drm_kunit_helpers.h>, to make it clear that is
not part of the DRM API but just for testing helpers.

But these are open questions really, and they can be done as follow-up:

Reviewed-by: Javier Martinez Canillas <jav...@redhat.com>

--
Best regards,

Javier Martinez Canillas
Core Platforms
Red Hat

Javier Martinez Canillas

unread,
Nov 30, 2022, 4:13:45 AM11/30/22
to Maxime Ripard, Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, linux-...@vger.kernel.org, David Gow
On 11/28/22 15:53, Maxime Ripard wrote:
> 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.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>
> ---

[...]

> -EXPORT_SYMBOL(drm_kunit_helper_alloc_drm_device);
> +EXPORT_SYMBOL(__drm_kunit_helper_alloc_drm_device);
>

I'm not sure if I would add a __ prefix to exported symbols, but I see that
this is a convention in the DRM subsystem so I'm OK with it.

Another thing that came to mind is if we want to use EXPORT_SYMBOL_GPL()
instead for the DRM KUnit helpers. But that's not related to this series.

Javier Martinez Canillas

unread,
Nov 30, 2022, 4:27:59 AM11/30/22
to Maxime Ripard, Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, linux-...@vger.kernel.org, David Gow
On 11/28/22 15:53, Maxime Ripard wrote:
> 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.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>
> ---

Javier Martinez Canillas

unread,
Nov 30, 2022, 4:59:45 AM11/30/22
to Maxime Ripard, Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, linux-...@vger.kernel.org, David Gow
On 11/28/22 15:53, 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.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>
> ---

[...]

> +
> +config DRM_VC4_KUNIT_TEST
> + bool "KUnit tests for VC4" if !KUNIT_ALL_TESTS
> + depends on DRM_VC4 && KUNIT

shouldn't this depend on DRM_KUNIT_TEST instead ?

[...]

> +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;

Since it could be vc4 or vc5, maybe can be renamed to just struct vc_dev *vc ?

> +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;
> +}

I guess many of these helpers could grow to be generic, like this one since
most drivers support the DRM_FORMAT_XRGB8888 format for their primary plane.

[...]

>
> +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;
> +

Maybe the driver could expose a helper function to get the pixelvalve data
and avoid having to expose all of these variables? For example you could
define an enum vc4_pixelvalve type and have something like the following:

const struct vc4_pv_data *vc4_crtc_get_pixelvalve_data(enum vc4_pixelvalve pv);

All these are small nits though, the patch looks great to me and I think is
awesome to have this level of testing with KUnit. Hope other drivers follow
your lead.

Javier Martinez Canillas

unread,
Nov 30, 2022, 5:10:06 AM11/30/22
to Maxime Ripard, Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, linux-...@vger.kernel.org, David Gow
On 11/28/22 15:53, 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.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>
> ---

[...]

> -#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)); \
> + })
>

Should this be made conditional on whether DRM_VC4_KUNIT_TEST is enabled ?

That is, just define the simpler macros when is disabled? The kunit_fail_current_test()
is just a no-op if CONFIG_KUNIT isn't enabled, but I think my question still stands.

Javier Martinez Canillas

unread,
Nov 30, 2022, 5:15:51 AM11/30/22
to Maxime Ripard, Daniel Vetter, Maarten Lankhorst, Maxime Ripard, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, linux-...@vger.kernel.org, David Gow
On 11/28/22 15:53, 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.
>
> Signed-off-by: Maxime Ripard <max...@cerno.tech>
> ---

Thanks for this patch. It shows how powerful KUnit can be for testing drivers.

Maxime Ripard

unread,
Dec 1, 2022, 5:28:00 AM12/1/22
to Javier Martinez Canillas, Daniel Vetter, Maarten Lankhorst, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, linux-...@vger.kernel.org, David Gow
Hi Javier,
I'm not entirely sure. I'd expect only the tests to include it, and thus
would depend on DRM_KUNIT_TEST already. But we can always add the stubs
if it's ever included in a different context.

> Another thing that wondered is if we want a different namespace for this
> header, i.e: <drm/testing/drm_kunit_helpers.h>, to make it clear that is
> not part of the DRM API but just for testing helpers.

If there's a single header, I don't think we need to create the
directory. This is also something we can consolidate later on if needed.

> But these are open questions really, and they can be done as follow-up:
>
> Reviewed-by: Javier Martinez Canillas <jav...@redhat.com>

Thanks :)
Maxime
signature.asc

Javier Martinez Canillas

unread,
Dec 1, 2022, 5:38:41 AM12/1/22
to Maxime Ripard, Daniel Vetter, Maarten Lankhorst, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, linux-...@vger.kernel.org, David Gow
Hello Maxime,

On 12/1/22 11:27, Maxime Ripard wrote:

[...]

>>
>> I wonder if now that this header was moved outside of the tests directory,
>> if we should add stub functions in the header file that are just defined
>> but do nothing if CONFIG_DRM_KUNIT_TEST isn't enabled. So that including
>> it in drivers will be a no-op.
>>
>> Or do you plan to conditionally include this header file in drivers? So
>> that is only included when CONFIG_DRM_KUNIT_TEST is enabled?
>
> I'm not entirely sure. I'd expect only the tests to include it, and thus
> would depend on DRM_KUNIT_TEST already. But we can always add the stubs
> if it's ever included in a different context.
>
>> Another thing that wondered is if we want a different namespace for this
>> header, i.e: <drm/testing/drm_kunit_helpers.h>, to make it clear that is
>> not part of the DRM API but just for testing helpers.
>
> If there's a single header, I don't think we need to create the
> directory. This is also something we can consolidate later on if needed.
>

Agree on both. It's better to land as is and then figure out if needs
to be changed once other drivers add more tests.

>> But these are open questions really, and they can be done as follow-up:
>>
>> Reviewed-by: Javier Martinez Canillas <jav...@redhat.com>
>
> Thanks :)

You are welcome!

Maxime Ripard

unread,
Dec 1, 2022, 8:03:53 AM12/1/22
to Javier Martinez Canillas, Daniel Vetter, Maarten Lankhorst, David Airlie, Thomas Zimmermann, dri-...@lists.freedesktop.org, Greg Kroah-Hartman, kuni...@googlegroups.com, linux...@vger.kernel.org, linux-k...@vger.kernel.org, linaro...@lists.linaro.org, Brendan Higgins, Maíra Canal, Dave Stevenson, linux-...@vger.kernel.org, David Gow
Hi Javier,

On Wed, Nov 30, 2022 at 10:59:37AM +0100, Javier Martinez Canillas wrote:
> On 11/28/22 15:53, 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.
> >
> > Signed-off-by: Maxime Ripard <max...@cerno.tech>
> > ---
>
> [...]
>
> > +
> > +config DRM_VC4_KUNIT_TEST
> > + bool "KUnit tests for VC4" if !KUNIT_ALL_TESTS
> > + depends on DRM_VC4 && KUNIT
>
> shouldn't this depend on DRM_KUNIT_TEST instead ?
>
> [...]

You're right, but the rework suggested by Maíra will add a select to the
helpers Kconfig symbol there so we should be safe.

> > +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;
>
> Since it could be vc4 or vc5, maybe can be renamed to just struct vc_dev *vc ?

vc4_dev is the main structure in the driver for the DRM device, so we
can't rename it easily.

Generally speaking the driver was (and still is) called vc4 after the IP
name in the original RaspberryPi SoC.

There's been a new generation since, but we supported it through the vc4
driver. Even if it's a bit ambiguous, vc4 refers to both the driver name
and is used extensively in the infrastructure, but also refers to the
initial generation we supported. vc5 is only the new generation.

I'm not sure removing the number would be less confusing.
Yeah, that's what I'd expect at some point as well :)

> > +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;
> > +
>
> Maybe the driver could expose a helper function to get the pixelvalve data
> and avoid having to expose all of these variables? For example you could
> define an enum vc4_pixelvalve type and have something like the following:
>
> const struct vc4_pv_data *vc4_crtc_get_pixelvalve_data(enum vc4_pixelvalve pv);
>
> All these are small nits though, the patch looks great to me and I think is
> awesome to have this level of testing with KUnit. Hope other drivers follow
> your lead.

I'm not sure. It adds an interface for something we don't really need,
so I'm not sure if it's really beneficial.

David pointed at that patch though, which seems more promising:
https://lore.kernel.org/linux-kselftest/20221102175959....@google.com/

Maxime
signature.asc

Maxime Ripard

unread,
Dec 1, 2022, 10:14:42 AM12/1/22
to Maxime Ripard, Maarten Lankhorst, Daniel Vetter, Thomas Zimmermann, David Airlie, linux-k...@vger.kernel.org, Greg Kroah-Hartman, dri-...@lists.freedesktop.org, Maxime Ripard, Maíra Canal, Javier Martinez Canillas, linux-...@vger.kernel.org, Brendan Higgins, Dave Stevenson, linux...@vger.kernel.org, David Gow, linaro...@lists.linaro.org, kuni...@googlegroups.com, Maíra Canal
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 <air...@gmail.com>
To: Daniel Vetter <dan...@ffwll.ch>
To: Maarten Lankhorst <maarten....@linux.intel.com>
To: Maxime Ripard <mri...@kernel.org>
To: Thomas Zimmermann <tzimm...@suse.de>
Cc: Dave Stevenson <dave.st...@raspberrypi.com>
Cc: Javier Martinez Canillas <jav...@redhat.com>
Cc: Greg Kroah-Hartman <gre...@linuxfoundation.org>
Cc: Maíra Canal <maira...@riseup.net>
Cc: Brendan Higgins <brendan...@linux.dev>
Cc: David Gow <davi...@google.com>
Cc: linux-k...@vger.kernel.org
Cc: kuni...@googlegroups.com
Cc: dri-...@lists.freedesktop.org
Cc: linux-...@vger.kernel.org
Cc: linux...@vger.kernel.org
Cc: linaro...@lists.linaro.org
Signed-off-by: Maxime Ripard <max...@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-te...@cerno.tech

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-te...@cerno.tech

---
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,
--
Maxime Ripard <max...@cerno.tech>
It is loading more messages.
0 new messages