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 = ▮ + 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
