From: Alex Hung <[email protected]>

Add KUnit tests for amdgpu_dm_audio.c.

Tests cover:
- amdgpu_dm_audio_init(): early exit when audio is disabled
- amdgpu_dm_audio_fini(): early exit when audio is not enabled
- fill_audio_info(): manufacturer and product ID propagation,
  display name copy, speaker allocation flags, CEA revision
  gating of audio mode copying (including the zero-mode case),
  and latency field propagation
- amdgpu_dm_audio_component_bind()/unbind(): component ops, device,
  and audio_component pointer are wired up on bind and cleared on
  unbind
- amdgpu_dm_audio_eld_notify(): callback is forwarded with the
  correct port and audio pointer, and the no-op guard paths for a
  missing component, audio_ops, or pin_eld_notify callback

Assisted-by: Copilot:Claude-Opus-4.8

Reviewed-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Alex Hung <[email protected]>
Signed-off-by: Chenyu Chen <[email protected]>
---
 .../amd/display/amdgpu_dm/amdgpu_dm_audio.c   |  27 +-
 .../amd/display/amdgpu_dm/amdgpu_dm_audio.h   |  12 +
 .../drm/amd/display/amdgpu_dm/tests/Makefile  |   1 +
 .../amdgpu_dm/tests/amdgpu_dm_audio_test.c    | 490 ++++++++++++++++++
 4 files changed, 527 insertions(+), 3 deletions(-)
 create mode 100644 
drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_audio_test.c

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.c
index 70b7dbded275..1ae121387297 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.c
@@ -26,6 +26,7 @@
 #include "amdgpu.h"
 #include "amdgpu_dm.h"
 #include "amdgpu_dm_audio.h"
+#include "amdgpu_dm_kunit_helpers.h"
 #include "dc.h"
 
 #include <linux/component.h>
@@ -83,7 +84,7 @@ static const struct drm_audio_component_ops 
amdgpu_dm_audio_component_ops = {
        .get_eld = amdgpu_dm_audio_component_get_eld,
 };
 
-static int amdgpu_dm_audio_component_bind(struct device *kdev,
+STATIC_IFN_KUNIT int amdgpu_dm_audio_component_bind(struct device *kdev,
                                       struct device *hda_kdev, void *data)
 {
        struct drm_device *dev = dev_get_drvdata(kdev);
@@ -96,8 +97,9 @@ static int amdgpu_dm_audio_component_bind(struct device *kdev,
 
        return 0;
 }
+EXPORT_IF_KUNIT(amdgpu_dm_audio_component_bind);
 
-static void amdgpu_dm_audio_component_unbind(struct device *kdev,
+STATIC_IFN_KUNIT void amdgpu_dm_audio_component_unbind(struct device *kdev,
                                          struct device *hda_kdev, void *data)
 {
        struct amdgpu_device *adev = drm_to_adev(dev_get_drvdata(kdev));
@@ -107,6 +109,7 @@ static void amdgpu_dm_audio_component_unbind(struct device 
*kdev,
        acomp->dev = NULL;
        adev->dm.audio_component = NULL;
 }
+EXPORT_IF_KUNIT(amdgpu_dm_audio_component_unbind);
 
 static const struct component_ops amdgpu_dm_audio_component_bind_ops = {
        .bind   = amdgpu_dm_audio_component_bind,
@@ -144,6 +147,7 @@ int amdgpu_dm_audio_init(struct amdgpu_device *adev)
 
        return 0;
 }
+EXPORT_IF_KUNIT(amdgpu_dm_audio_init);
 
 void amdgpu_dm_audio_fini(struct amdgpu_device *adev)
 {
@@ -162,8 +166,9 @@ void amdgpu_dm_audio_fini(struct amdgpu_device *adev)
 
        adev->mode_info.audio.enabled = false;
 }
+EXPORT_IF_KUNIT(amdgpu_dm_audio_fini);
 
-static void amdgpu_dm_audio_eld_notify(struct amdgpu_device *adev, int pin)
+STATIC_IFN_KUNIT void amdgpu_dm_audio_eld_notify(struct amdgpu_device *adev, 
int pin)
 {
        struct drm_audio_component *acomp = adev->dm.audio_component;
 
@@ -174,6 +179,7 @@ static void amdgpu_dm_audio_eld_notify(struct amdgpu_device 
*adev, int pin)
                                                 pin, -1);
        }
 }
+EXPORT_IF_KUNIT(amdgpu_dm_audio_eld_notify);
 
 void amdgpu_dm_fill_audio_info(struct audio_info *audio_info,
                     const struct drm_connector *drm_connector,
@@ -219,6 +225,7 @@ void amdgpu_dm_fill_audio_info(struct audio_info 
*audio_info,
        /* TODO: For DP, video and audio latency should be calculated from DPCD 
caps */
 
 }
+EXPORT_IF_KUNIT(amdgpu_dm_fill_audio_info);
 
 void amdgpu_dm_commit_audio(struct drm_device *dev,
                            struct drm_atomic_state *state)
@@ -300,3 +307,17 @@ void amdgpu_dm_commit_audio(struct drm_device *dev,
                amdgpu_dm_audio_eld_notify(adev, inst);
        }
 }
+
+#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+int amdgpu_dm_audio_get_param(void)
+{
+       return amdgpu_audio;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_audio_get_param);
+
+void amdgpu_dm_audio_set_param(int val)
+{
+       amdgpu_audio = val;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_audio_set_param);
+#endif
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.h 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.h
index efd412bba178..48391305c3c2 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.h
@@ -41,4 +41,16 @@ void amdgpu_dm_fill_audio_info(struct audio_info *audio_info,
                     const struct drm_connector *drm_connector,
                     const struct dc_sink *dc_sink);
 
+#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+struct device;
+
+int amdgpu_dm_audio_component_bind(struct device *kdev,
+                                  struct device *hda_kdev, void *data);
+void amdgpu_dm_audio_component_unbind(struct device *kdev,
+                                     struct device *hda_kdev, void *data);
+void amdgpu_dm_audio_eld_notify(struct amdgpu_device *adev, int pin);
+int amdgpu_dm_audio_get_param(void);
+void amdgpu_dm_audio_set_param(int val);
+#endif
+
 #endif /* __AMDGPU_DM_AUDIO_H__ */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile 
b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile
index ddd9fce66232..5bb43b3bc439 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile
@@ -13,6 +13,7 @@ ccflags-y += -I$(src)/../../../include
 
 obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_crc_test.o
 obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_hdcp_test.o
+obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_audio_test.o
 obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_color_test.o
 obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_colorop_test.o
 obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_backlight_test.o
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_audio_test.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_audio_test.c
new file mode 100644
index 000000000000..79ff5d9b3fa5
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_audio_test.c
@@ -0,0 +1,490 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * KUnit tests for amdgpu_dm_audio.c
+ *
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <kunit/test.h>
+
+#include <drm/drm_audio_component.h>
+
+#include "dc.h"
+#include "amdgpu.h"
+#include "amdgpu_mode.h"
+#include "amdgpu_dm.h"
+#include "amdgpu_dm_audio.h"
+
+/* Tests for amdgpu_dm_audio_init() */
+
+/**
+ * dm_test_audio_init_disabled - Test audio init exits when audio is disabled
+ * @test: The KUnit test context
+ */
+static void dm_test_audio_init_disabled(struct kunit *test)
+{
+       struct amdgpu_device *adev;
+       int saved_audio = amdgpu_dm_audio_get_param();
+
+       adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+       amdgpu_dm_audio_set_param(0);
+
+       KUNIT_EXPECT_EQ(test, amdgpu_dm_audio_init(adev), 0);
+       KUNIT_EXPECT_FALSE(test, adev->mode_info.audio.enabled);
+       KUNIT_EXPECT_FALSE(test, adev->dm.audio_registered);
+
+       amdgpu_dm_audio_set_param(saved_audio);
+}
+
+/* Tests for amdgpu_dm_audio_fini() */
+
+/**
+ * dm_test_audio_fini_without_enabled_audio - Test fini exits when audio is 
not enabled
+ * @test: The KUnit test context
+ */
+static void dm_test_audio_fini_without_enabled_audio(struct kunit *test)
+{
+       struct amdgpu_device *adev;
+       int saved_audio = amdgpu_dm_audio_get_param();
+
+       adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+       amdgpu_dm_audio_set_param(1);
+       adev->mode_info.audio.enabled = false;
+       adev->dm.audio_registered = true;
+
+       amdgpu_dm_audio_fini(adev);
+
+       KUNIT_EXPECT_FALSE(test, adev->mode_info.audio.enabled);
+       KUNIT_EXPECT_TRUE(test, adev->dm.audio_registered);
+
+       amdgpu_dm_audio_set_param(saved_audio);
+}
+
+/* Tests for amdgpu_dm_fill_audio_info() */
+
+/**
+ * dm_test_fill_audio_info_ids_name_flags - Test Fill audio info ids name flags
+ * @test: The KUnit test context
+ */
+static void dm_test_fill_audio_info_ids_name_flags(struct kunit *test)
+{
+       struct audio_info *audio_info;
+       struct drm_connector *connector;
+       struct dc_sink *dc_sink;
+       const char *name = "DM-AUDIO-PANEL";
+
+       audio_info = kunit_kzalloc(test, sizeof(*audio_info), GFP_KERNEL);
+       connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+       dc_sink = kunit_kzalloc(test, sizeof(*dc_sink), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_info);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_sink);
+
+       dc_sink->edid_caps.manufacturer_id = 0x1234;
+       dc_sink->edid_caps.product_id = 0xABCD;
+       dc_sink->edid_caps.speaker_flags = 0x5;
+       strscpy(dc_sink->edid_caps.display_name, name,
+               AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS);
+
+       connector->display_info.cea_rev = 1;
+
+       amdgpu_dm_fill_audio_info(audio_info, connector, dc_sink);
+
+       KUNIT_EXPECT_EQ(test, audio_info->manufacture_id, 0x1234U);
+       KUNIT_EXPECT_EQ(test, audio_info->product_id, 0xABCDU);
+       KUNIT_EXPECT_EQ(test, audio_info->flags.all, 0x5U);
+       KUNIT_EXPECT_STREQ(test, audio_info->display_name, name);
+}
+
+/**
+ * dm_test_fill_audio_info_cea_lt_3_skips_modes - Test Fill audio info cea lt 
3 skips modes
+ * @test: The KUnit test context
+ */
+static void dm_test_fill_audio_info_cea_lt_3_skips_modes(struct kunit *test)
+{
+       struct audio_info *audio_info;
+       struct drm_connector *connector;
+       struct dc_sink *dc_sink;
+
+       audio_info = kunit_kzalloc(test, sizeof(*audio_info), GFP_KERNEL);
+       connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+       dc_sink = kunit_kzalloc(test, sizeof(*dc_sink), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_info);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_sink);
+
+       connector->display_info.cea_rev = 2;
+       dc_sink->edid_caps.audio_mode_count = 2;
+       dc_sink->edid_caps.audio_modes[0].format_code = 1;
+       dc_sink->edid_caps.audio_modes[0].channel_count = 2;
+       dc_sink->edid_caps.audio_modes[0].sample_rate = 0x07;
+       dc_sink->edid_caps.audio_modes[0].sample_size = 16;
+
+       amdgpu_dm_fill_audio_info(audio_info, connector, dc_sink);
+
+       KUNIT_EXPECT_EQ(test, audio_info->mode_count, 0U);
+}
+
+/**
+ * dm_test_fill_audio_info_cea_ge_3_copies_modes - Test Fill audio info cea ge 
3 copies modes
+ * @test: The KUnit test context
+ */
+static void dm_test_fill_audio_info_cea_ge_3_copies_modes(struct kunit *test)
+{
+       struct audio_info *audio_info;
+       struct drm_connector *connector;
+       struct dc_sink *dc_sink;
+
+       audio_info = kunit_kzalloc(test, sizeof(*audio_info), GFP_KERNEL);
+       connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+       dc_sink = kunit_kzalloc(test, sizeof(*dc_sink), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_info);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_sink);
+
+       connector->display_info.cea_rev = 3;
+       dc_sink->edid_caps.audio_mode_count = 2;
+
+       dc_sink->edid_caps.audio_modes[0].format_code = 1;
+       dc_sink->edid_caps.audio_modes[0].channel_count = 2;
+       dc_sink->edid_caps.audio_modes[0].sample_rate = 0x07;
+       dc_sink->edid_caps.audio_modes[0].sample_size = 16;
+
+       dc_sink->edid_caps.audio_modes[1].format_code = 11;
+       dc_sink->edid_caps.audio_modes[1].channel_count = 6;
+       dc_sink->edid_caps.audio_modes[1].sample_rate = 0x1F;
+       dc_sink->edid_caps.audio_modes[1].sample_size = 24;
+
+       amdgpu_dm_fill_audio_info(audio_info, connector, dc_sink);
+
+       KUNIT_EXPECT_EQ(test, audio_info->mode_count, 2U);
+
+       KUNIT_EXPECT_EQ(test, (int)audio_info->modes[0].format_code, 1);
+       KUNIT_EXPECT_EQ(test, audio_info->modes[0].channel_count, 2);
+       KUNIT_EXPECT_EQ(test, audio_info->modes[0].sample_rates.all, 0x07U);
+       KUNIT_EXPECT_EQ(test, audio_info->modes[0].sample_size, 16);
+
+       KUNIT_EXPECT_EQ(test, (int)audio_info->modes[1].format_code, 11);
+       KUNIT_EXPECT_EQ(test, audio_info->modes[1].channel_count, 6);
+       KUNIT_EXPECT_EQ(test, audio_info->modes[1].sample_rates.all, 0x1FU);
+       KUNIT_EXPECT_EQ(test, audio_info->modes[1].sample_size, 24);
+}
+
+/**
+ * dm_test_fill_audio_info_latency_present - Test Fill audio info latency 
present
+ * @test: The KUnit test context
+ */
+static void dm_test_fill_audio_info_latency_present(struct kunit *test)
+{
+       struct audio_info *audio_info;
+       struct drm_connector *connector;
+       struct dc_sink *dc_sink;
+
+       audio_info = kunit_kzalloc(test, sizeof(*audio_info), GFP_KERNEL);
+       connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+       dc_sink = kunit_kzalloc(test, sizeof(*dc_sink), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_info);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_sink);
+
+       connector->display_info.cea_rev = 3;
+       connector->latency_present[0] = true;
+       connector->video_latency[0] = 11;
+       connector->audio_latency[0] = 22;
+
+       amdgpu_dm_fill_audio_info(audio_info, connector, dc_sink);
+
+       KUNIT_EXPECT_EQ(test, audio_info->video_latency, 11U);
+       KUNIT_EXPECT_EQ(test, audio_info->audio_latency, 22U);
+}
+
+/**
+ * dm_test_fill_audio_info_latency_absent_keeps_zero - Test Fill audio info 
latency absent keeps zero
+ * @test: The KUnit test context
+ */
+static void dm_test_fill_audio_info_latency_absent_keeps_zero(struct kunit 
*test)
+{
+       struct audio_info *audio_info;
+       struct drm_connector *connector;
+       struct dc_sink *dc_sink;
+
+       audio_info = kunit_kzalloc(test, sizeof(*audio_info), GFP_KERNEL);
+       connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+       dc_sink = kunit_kzalloc(test, sizeof(*dc_sink), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_info);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_sink);
+
+       connector->display_info.cea_rev = 3;
+       connector->latency_present[0] = false;
+       connector->video_latency[0] = 99;
+       connector->audio_latency[0] = 88;
+
+       amdgpu_dm_fill_audio_info(audio_info, connector, dc_sink);
+
+       KUNIT_EXPECT_EQ(test, audio_info->video_latency, 0U);
+       KUNIT_EXPECT_EQ(test, audio_info->audio_latency, 0U);
+}
+
+/**
+ * dm_test_fill_audio_info_cea_ge_3_zero_modes - Test cea >= 3 with zero modes
+ * @test: The KUnit test context
+ *
+ * When cea_rev >= 3 but the sink reports no audio modes, mode_count must be
+ * copied as 0 and no mode entries should be populated.
+ */
+static void dm_test_fill_audio_info_cea_ge_3_zero_modes(struct kunit *test)
+{
+       struct audio_info *audio_info;
+       struct drm_connector *connector;
+       struct dc_sink *dc_sink;
+
+       audio_info = kunit_kzalloc(test, sizeof(*audio_info), GFP_KERNEL);
+       connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+       dc_sink = kunit_kzalloc(test, sizeof(*dc_sink), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_info);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_sink);
+
+       connector->display_info.cea_rev = 3;
+       dc_sink->edid_caps.audio_mode_count = 0;
+
+       amdgpu_dm_fill_audio_info(audio_info, connector, dc_sink);
+
+       KUNIT_EXPECT_EQ(test, audio_info->mode_count, 0U);
+       KUNIT_EXPECT_EQ(test, (int)audio_info->modes[0].format_code, 0);
+}
+
+/* Tests for amdgpu_dm_audio_component_bind()/unbind() */
+
+/**
+ * dm_test_audio_component_bind_sets_fields - Test bind wires up audio 
component
+ * @test: The KUnit test context
+ *
+ * Binding must publish the DRM audio component ops, record the kernel device,
+ * and store the component pointer in the display manager.
+ */
+static void dm_test_audio_component_bind_sets_fields(struct kunit *test)
+{
+       struct amdgpu_device *adev;
+       struct device *kdev;
+       struct drm_audio_component *acomp;
+       int ret;
+
+       adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+       kdev = kunit_kzalloc(test, sizeof(*kdev), GFP_KERNEL);
+       acomp = kunit_kzalloc(test, sizeof(*acomp), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, kdev);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acomp);
+
+       dev_set_drvdata(kdev, &adev->ddev);
+
+       ret = amdgpu_dm_audio_component_bind(kdev, NULL, acomp);
+
+       KUNIT_EXPECT_EQ(test, ret, 0);
+       KUNIT_EXPECT_NOT_NULL(test, acomp->ops);
+       KUNIT_EXPECT_PTR_EQ(test, acomp->dev, kdev);
+       KUNIT_EXPECT_PTR_EQ(test, adev->dm.audio_component, acomp);
+}
+
+/**
+ * dm_test_audio_component_unbind_clears_fields - Test unbind tears down 
component
+ * @test: The KUnit test context
+ *
+ * Unbinding must clear the component ops, the kernel device, and the display
+ * manager's stored component pointer.
+ */
+static void dm_test_audio_component_unbind_clears_fields(struct kunit *test)
+{
+       struct amdgpu_device *adev;
+       struct device *kdev;
+       struct drm_audio_component *acomp;
+
+       adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+       kdev = kunit_kzalloc(test, sizeof(*kdev), GFP_KERNEL);
+       acomp = kunit_kzalloc(test, sizeof(*acomp), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, kdev);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acomp);
+
+       dev_set_drvdata(kdev, &adev->ddev);
+
+       /* Pretend a prior bind already happened. */
+       acomp->dev = kdev;
+       adev->dm.audio_component = acomp;
+
+       amdgpu_dm_audio_component_unbind(kdev, NULL, acomp);
+
+       KUNIT_EXPECT_NULL(test, acomp->ops);
+       KUNIT_EXPECT_NULL(test, acomp->dev);
+       KUNIT_EXPECT_NULL(test, adev->dm.audio_component);
+}
+
+/* Tests for amdgpu_dm_audio_eld_notify() */
+
+static int dm_test_eld_notify_count;
+static int dm_test_eld_notify_port;
+static void *dm_test_eld_notify_ptr;
+
+static void dm_test_pin_eld_notify(void *audio_ptr, int port, int pipe)
+{
+       dm_test_eld_notify_count++;
+       dm_test_eld_notify_port = port;
+       dm_test_eld_notify_ptr = audio_ptr;
+}
+
+/**
+ * dm_test_eld_notify_invokes_callback - Test ELD notify forwards to hda driver
+ * @test: The KUnit test context
+ *
+ * When a component with a pin_eld_notify callback is registered, the notify
+ * helper must invoke it with the audio pointer and the requested pin.
+ */
+static void dm_test_eld_notify_invokes_callback(struct kunit *test)
+{
+       struct amdgpu_device *adev;
+       struct drm_audio_component *acomp;
+       struct drm_audio_component_audio_ops *audio_ops;
+       int marker = 0;
+
+       adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+       acomp = kunit_kzalloc(test, sizeof(*acomp), GFP_KERNEL);
+       audio_ops = kunit_kzalloc(test, sizeof(*audio_ops), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acomp);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_ops);
+
+       audio_ops->audio_ptr = &marker;
+       audio_ops->pin_eld_notify = dm_test_pin_eld_notify;
+       acomp->audio_ops = audio_ops;
+       adev->dm.audio_component = acomp;
+
+       dm_test_eld_notify_count = 0;
+       dm_test_eld_notify_port = -100;
+       dm_test_eld_notify_ptr = NULL;
+
+       amdgpu_dm_audio_eld_notify(adev, 7);
+
+       KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 1);
+       KUNIT_EXPECT_EQ(test, dm_test_eld_notify_port, 7);
+       KUNIT_EXPECT_PTR_EQ(test, dm_test_eld_notify_ptr, (void *)&marker);
+}
+
+/**
+ * dm_test_eld_notify_no_component - Test ELD notify is a no-op without 
component
+ * @test: The KUnit test context
+ *
+ * With no registered audio component, the notify helper must return without
+ * invoking any callback.
+ */
+static void dm_test_eld_notify_no_component(struct kunit *test)
+{
+       struct amdgpu_device *adev;
+
+       adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+       adev->dm.audio_component = NULL;
+
+       dm_test_eld_notify_count = 0;
+
+       amdgpu_dm_audio_eld_notify(adev, 3);
+
+       KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 0);
+}
+
+/**
+ * dm_test_eld_notify_null_audio_ops - Test ELD notify is a no-op without 
audio_ops
+ * @test: The KUnit test context
+ *
+ * A component without audio_ops must not trigger any callback.
+ */
+static void dm_test_eld_notify_null_audio_ops(struct kunit *test)
+{
+       struct amdgpu_device *adev;
+       struct drm_audio_component *acomp;
+
+       adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+       acomp = kunit_kzalloc(test, sizeof(*acomp), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acomp);
+
+       acomp->audio_ops = NULL;
+       adev->dm.audio_component = acomp;
+
+       dm_test_eld_notify_count = 0;
+
+       amdgpu_dm_audio_eld_notify(adev, 3);
+
+       KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 0);
+}
+
+/**
+ * dm_test_eld_notify_null_callback - Test ELD notify is a no-op without 
callback
+ * @test: The KUnit test context
+ *
+ * audio_ops present but with a NULL pin_eld_notify must not crash or call
+ * anything.
+ */
+static void dm_test_eld_notify_null_callback(struct kunit *test)
+{
+       struct amdgpu_device *adev;
+       struct drm_audio_component *acomp;
+       struct drm_audio_component_audio_ops *audio_ops;
+
+       adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+       acomp = kunit_kzalloc(test, sizeof(*acomp), GFP_KERNEL);
+       audio_ops = kunit_kzalloc(test, sizeof(*audio_ops), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acomp);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_ops);
+
+       audio_ops->pin_eld_notify = NULL;
+       acomp->audio_ops = audio_ops;
+       adev->dm.audio_component = acomp;
+
+       dm_test_eld_notify_count = 0;
+
+       amdgpu_dm_audio_eld_notify(adev, 3);
+
+       KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 0);
+}
+
+static struct kunit_case dm_audio_test_cases[] = {
+       /* amdgpu_dm_audio_init */
+       KUNIT_CASE(dm_test_audio_init_disabled),
+       /* amdgpu_dm_audio_fini */
+       KUNIT_CASE(dm_test_audio_fini_without_enabled_audio),
+       /* amdgpu_dm_fill_audio_info */
+       KUNIT_CASE(dm_test_fill_audio_info_ids_name_flags),
+       KUNIT_CASE(dm_test_fill_audio_info_cea_lt_3_skips_modes),
+       KUNIT_CASE(dm_test_fill_audio_info_cea_ge_3_copies_modes),
+       KUNIT_CASE(dm_test_fill_audio_info_cea_ge_3_zero_modes),
+       KUNIT_CASE(dm_test_fill_audio_info_latency_present),
+       KUNIT_CASE(dm_test_fill_audio_info_latency_absent_keeps_zero),
+       /* amdgpu_dm_audio_component_bind/unbind */
+       KUNIT_CASE(dm_test_audio_component_bind_sets_fields),
+       KUNIT_CASE(dm_test_audio_component_unbind_clears_fields),
+       /* amdgpu_dm_audio_eld_notify */
+       KUNIT_CASE(dm_test_eld_notify_invokes_callback),
+       KUNIT_CASE(dm_test_eld_notify_no_component),
+       KUNIT_CASE(dm_test_eld_notify_null_audio_ops),
+       KUNIT_CASE(dm_test_eld_notify_null_callback),
+       {}
+};
+
+static struct kunit_suite dm_audio_test_suite = {
+       .name = "amdgpu_dm_audio",
+       .test_cases = dm_audio_test_cases,
+};
+
+kunit_test_suite(dm_audio_test_suite);
+
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_audio");
+MODULE_AUTHOR("AMD");
-- 
2.43.0

Reply via email to