From: Alex Hung <[email protected]>

[WHAT]
Add KUnit tests for amdgpu_dm_dmub_reg_read() and
amdgpu_dm_dmub_reg_write(), the register accessors the DMUB service is
created with.

[HOW]
Both accessors reach the hardware through cgs_read_register() and
cgs_write_register(), which dispatch through the ops table at the start
of struct cgs_device. Add a fake CGS device that records the offset and
value of each access, so the callbacks can be driven without real MMIO.

The test writes a value through the write callback and reads it back
through the read callback, confirming both forward to the DC context of
the device passed as their opaque user context.

Assisted-by: Copilot:Claude-Opus-5
Reviewed-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Alex Hung <[email protected]>
Signed-off-by: Ivan Lipski <[email protected]>
---
 .../amd/display/amdgpu_dm/amdgpu_dm_dmub.c    |  8 +-
 .../amd/display/amdgpu_dm/amdgpu_dm_dmub.h    |  2 +
 .../amdgpu_dm/tests/amdgpu_dm_dmub_test.c     | 95 ++++++++++++++++++-
 3 files changed, 101 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c
index 9ff25bc8ee5ae..29990cfe4265e 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c
@@ -468,20 +468,22 @@ enum dmub_ips_disable_type dm_get_default_ips_mode(
 }
 EXPORT_IF_KUNIT(dm_get_default_ips_mode);
 
-static uint32_t amdgpu_dm_dmub_reg_read(void *ctx, uint32_t address)
+STATIC_IFN_KUNIT uint32_t amdgpu_dm_dmub_reg_read(void *ctx, uint32_t address)
 {
        struct amdgpu_device *adev = ctx;
 
        return dm_read_reg(adev->dm.dc->ctx, address);
 }
+EXPORT_IF_KUNIT(amdgpu_dm_dmub_reg_read);
 
-static void amdgpu_dm_dmub_reg_write(void *ctx, uint32_t address,
-                                    uint32_t value)
+STATIC_IFN_KUNIT void amdgpu_dm_dmub_reg_write(void *ctx, uint32_t address,
+                                              uint32_t value)
 {
        struct amdgpu_device *adev = ctx;
 
        return dm_write_reg(adev->dm.dc->ctx, address, value);
 }
+EXPORT_IF_KUNIT(amdgpu_dm_dmub_reg_write);
 
 int dm_dmub_sw_init(struct amdgpu_device *adev)
 {
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h
index 31d5f8c265c0d..735e2b4299521 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h
@@ -73,6 +73,8 @@ struct dmub_cmd_fused_request;
 void *dm_dmub_get_vbios_bounding_box(struct amdgpu_device *adev);
 void abort_fused_io(struct dc_context *ctx,
                    const struct dmub_cmd_fused_request *request);
+uint32_t amdgpu_dm_dmub_reg_read(void *ctx, uint32_t address);
+void amdgpu_dm_dmub_reg_write(void *ctx, uint32_t address, uint32_t value);
 #endif
 
 #endif /* AMDGPU_DM_AMDGPU_DM_DMUB_H_ */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c
index ead43eeb38c77..4bba282abd69d 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c
@@ -1347,6 +1347,86 @@ static void dm_test_abort_fused_io_no_dmub_srv(struct 
kunit *test)
        abort_fused_io(ctx, req);
 }
 
+/*
+ * Fake CGS device: the DMUB register callbacks reach the hardware through
+ * cgs_read_register()/cgs_write_register(), which dispatch via the ops table
+ * at the start of struct cgs_device.
+ */
+
+#define DM_TEST_GPINT_STATUS_MASK      0xF0000000U
+
+struct dm_test_cgs_ctx {
+       struct cgs_device dev;
+       u32 last_write;
+       unsigned int write_calls;
+       unsigned int read_calls;
+       unsigned int last_write_offset;
+       unsigned int last_read_offset;
+};
+
+static struct dm_test_cgs_ctx dm_test_cgs;
+
+static uint32_t dm_test_cgs_read_register(struct cgs_device *cgs_device,
+                                         unsigned int offset)
+{
+       dm_test_cgs.read_calls++;
+       dm_test_cgs.last_read_offset = offset;
+
+       /* The firmware acks a GPINT by clearing the status nibble. */
+       return dm_test_cgs.last_write & ~DM_TEST_GPINT_STATUS_MASK;
+}
+
+static void dm_test_cgs_write_register(struct cgs_device *cgs_device,
+                                      unsigned int offset, uint32_t value)
+{
+       dm_test_cgs.write_calls++;
+       dm_test_cgs.last_write_offset = offset;
+       dm_test_cgs.last_write = value;
+}
+
+static const struct cgs_ops dm_test_cgs_ops = {
+       .read_register = dm_test_cgs_read_register,
+       .write_register = dm_test_cgs_write_register,
+};
+
+static int dm_test_dmub_hw_access_init(struct kunit *test)
+{
+       dm_test_cgs = (struct dm_test_cgs_ctx) { .dev.ops = &dm_test_cgs_ops };
+
+       return 0;
+}
+
+/* Tests for amdgpu_dm_dmub_reg_read() and amdgpu_dm_dmub_reg_write() */
+
+/**
+ * dm_test_dmub_reg_write_then_read - Test the DMUB register access callbacks
+ * @test: The KUnit test context
+ *
+ * The callbacks the DMUB service is created with must forward to the DC
+ * context of the device passed as their opaque user context.
+ */
+static void dm_test_dmub_reg_write_then_read(struct kunit *test)
+{
+       struct amdgpu_device *adev = dm_test_alloc_adev_with_dc(test);
+       struct dc_perf_trace *perf_trace;
+
+       perf_trace = kunit_kzalloc(test, sizeof(*perf_trace), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, perf_trace);
+
+       adev->dm.dc->ctx->cgs_device = &dm_test_cgs.dev;
+       adev->dm.dc->ctx->perf_trace = perf_trace;
+
+       amdgpu_dm_dmub_reg_write(adev, 0x1234, 0x0BADF00D);
+
+       KUNIT_EXPECT_EQ(test, dm_test_cgs.write_calls, 1U);
+       KUNIT_EXPECT_EQ(test, dm_test_cgs.last_write_offset, 0x1234U);
+       KUNIT_EXPECT_EQ(test, dm_test_cgs.last_write, 0x0BADF00DU);
+
+       KUNIT_EXPECT_EQ(test, amdgpu_dm_dmub_reg_read(adev, 0x1234), 
0x0BADF00DU);
+       KUNIT_EXPECT_EQ(test, dm_test_cgs.read_calls, 1U);
+       KUNIT_EXPECT_EQ(test, dm_test_cgs.last_read_offset, 0x1234U);
+}
+
 static struct kunit_case amdgpu_dm_dmub_tests[] = {
        /* dm_register_dmub_notify_callback() */
        KUNIT_CASE(dm_test_register_dmub_notify_callback_null_callback),
@@ -1412,7 +1492,20 @@ static struct kunit_suite amdgpu_dm_dmub_test_suite = {
        .test_cases = amdgpu_dm_dmub_tests,
 };
 
-kunit_test_suite(amdgpu_dm_dmub_test_suite);
+static struct kunit_case amdgpu_dm_dmub_hw_access_tests[] = {
+       /* amdgpu_dm_dmub_reg_read() and amdgpu_dm_dmub_reg_write() */
+       KUNIT_CASE(dm_test_dmub_reg_write_then_read),
+       {}
+};
+
+static struct kunit_suite amdgpu_dm_dmub_hw_access_test_suite = {
+       .name = "amdgpu_dm_dmub_hw_access",
+       .init = dm_test_dmub_hw_access_init,
+       .test_cases = amdgpu_dm_dmub_hw_access_tests,
+};
+
+kunit_test_suites(&amdgpu_dm_dmub_test_suite,
+                 &amdgpu_dm_dmub_hw_access_test_suite);
 
 MODULE_AUTHOR("AMD");
 MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_dmub");
-- 
2.43.0

Reply via email to