[PATCH 19/49] drm/amd/display: add KUnit tests for audio commit path

Fangzhi Zuo <[email protected]>
Newsgroups org.freedesktop.lists.amd-gfx
Message-ID <[email protected]>
From: Alex Hung <[email protected]>

[WHAT]
Add KUnit coverage for amdgpu_dm_commit_audio(), the remaining function
reported as untested by gcov.

The tests build hand-rolled drm_atomic_state arrays and back
dc_stream_get_status() with a fake dc->current_state to cover the
removal and addition ELD notifications along with their guard/skip
paths: writeback connector, detached connector, missing new CRTC state,
no modeset, and absent stream status.

Reviewed-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Alex Hung <[email protected]>
Signed-off-by: Fangzhi Zuo <[email protected]>
Tested-by: Dan Wheeler <[email protected]>
---
 .../amd/display/amdgpu_dm/amdgpu_dm_audio.c   |   1 +
 .../amdgpu_dm/tests/amdgpu_dm_audio_test.c    | 532 ++++++++++++++++++
 2 files changed, 533 insertions(+)

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 4b10c683df8e..199f339839b2 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
@@ -321,6 +321,7 @@ void amdgpu_dm_commit_audio(struct drm_device *dev,
 		amdgpu_dm_audio_eld_notify(adev, inst);
 	}
 }
+EXPORT_IF_KUNIT(amdgpu_dm_commit_audio);
 
 #if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
 int amdgpu_dm_audio_get_param(void)
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
index d30b7f644f97..040a7a9c0335 100644
--- 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
@@ -8,9 +8,11 @@
 #include <kunit/test.h>
 #include <linux/device.h>
 
+#include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_audio_component.h>
 #include <drm/drm_connector.h>
+#include <drm/drm_crtc.h>
 #include <drm/drm_eld.h>
 #include <drm/drm_kunit_helpers.h>
 #include <drm/drm_probe_helper.h>
@@ -37,6 +39,32 @@ static void dm_test_audio_connector_cleanup(void *data)
 	drm_connector_cleanup(data);
 }
 
+static struct drm_atomic_commit *dm_test_audio_alloc_atomic_state(struct kunit *test,
+								 unsigned int num_connector,
+								 unsigned int num_crtc)
+{
+	struct drm_atomic_commit *state;
+
+	state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, state);
+
+	state->num_connector = num_connector;
+	if (num_connector) {
+		state->connectors = kunit_kcalloc(test, num_connector,
+						  sizeof(*state->connectors),
+						  GFP_KERNEL);
+		KUNIT_ASSERT_NOT_NULL(test, state->connectors);
+	}
+
+	if (num_crtc) {
+		state->crtcs = kunit_kcalloc(test, num_crtc, sizeof(*state->crtcs),
+					     GFP_KERNEL);
+		KUNIT_ASSERT_NOT_NULL(test, state->crtcs);
+	}
+
+	return state;
+}
+
 /* Tests for amdgpu_dm_audio_init() */
 
 /**
@@ -411,6 +439,22 @@ static void dm_test_pin_eld_notify(void *audio_ptr, int port, int pipe)
 	dm_test_eld_notify_ptr = audio_ptr;
 }
 
+static void dm_test_audio_setup_notify_component(struct kunit *test,
+						 struct amdgpu_device *adev)
+{
+	struct drm_audio_component *acomp;
+	struct drm_audio_component_audio_ops *audio_ops;
+
+	acomp = kunit_kzalloc(test, sizeof(*acomp), GFP_KERNEL);
+	audio_ops = kunit_kzalloc(test, sizeof(*audio_ops), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, acomp);
+	KUNIT_ASSERT_NOT_NULL(test, audio_ops);
+
+	audio_ops->pin_eld_notify = dm_test_pin_eld_notify;
+	acomp->audio_ops = audio_ops;
+	adev->dm.audio_component = acomp;
+}
+
 /**
  * dm_test_eld_notify_invokes_callback - Test ELD notify forwards to hda driver
  * @test: The KUnit test context
@@ -685,6 +729,484 @@ static void dm_test_audio_component_get_eld_no_match(struct kunit *test)
 	KUNIT_EXPECT_EQ(test, buf[0], 0x5a);
 }
 
+/* Tests for amdgpu_dm_commit_audio() */
+
+/**
+ * dm_test_commit_audio_notifies_removed_connector - Test removal notification
+ * @test: The KUnit test context
+ *
+ * When a connector loses its CRTC, commit_audio should clear its audio
+ * instance and notify the audio component for the old pin.
+ */
+static void dm_test_commit_audio_notifies_removed_connector(struct kunit *test)
+{
+	struct amdgpu_device *adev = dm_kunit_alloc_adev(test);
+	struct drm_atomic_commit *state;
+	struct amdgpu_dm_connector *aconnector;
+	struct drm_connector_state *old_conn_state;
+	struct drm_connector_state *new_conn_state;
+	struct drm_crtc *crtc;
+
+	state = dm_test_audio_alloc_atomic_state(test, 1, 0);
+	aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+	old_conn_state = kunit_kzalloc(test, sizeof(*old_conn_state), GFP_KERNEL);
+	new_conn_state = kunit_kzalloc(test, sizeof(*new_conn_state), GFP_KERNEL);
+	crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, aconnector);
+	KUNIT_ASSERT_NOT_NULL(test, old_conn_state);
+	KUNIT_ASSERT_NOT_NULL(test, new_conn_state);
+	KUNIT_ASSERT_NOT_NULL(test, crtc);
+
+	mutex_init(&adev->dm.audio_lock);
+	dm_test_audio_setup_notify_component(test, adev);
+	aconnector->audio_inst = 5;
+	aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA;
+	old_conn_state->crtc = crtc;
+	state->connectors[0].ptr = &aconnector->base;
+	state->connectors[0].old_state = old_conn_state;
+	state->connectors[0].new_state = new_conn_state;
+
+	dm_test_eld_notify_count = 0;
+	dm_test_eld_notify_port = -1;
+
+	amdgpu_dm_commit_audio(&adev->ddev, state);
+
+	KUNIT_EXPECT_EQ(test, aconnector->audio_inst, -1);
+	KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 1);
+	KUNIT_EXPECT_EQ(test, dm_test_eld_notify_port, 5);
+}
+
+/**
+ * dm_test_commit_audio_notifies_added_connector - Test addition notification
+ * @test: The KUnit test context
+ *
+ * When a modeset enables a connector with a stream status, commit_audio should
+ * store the stream audio instance and notify the audio component for that pin.
+ */
+static void dm_test_commit_audio_notifies_added_connector(struct kunit *test)
+{
+	struct amdgpu_device *adev = dm_kunit_alloc_adev(test);
+	struct drm_atomic_commit *state;
+	struct amdgpu_dm_connector *aconnector;
+	struct drm_connector_state *old_conn_state;
+	struct drm_connector_state *new_conn_state;
+	struct drm_crtc *crtc;
+	struct dm_crtc_state *dm_crtc_state;
+	struct dc_stream_state *stream;
+
+	state = dm_test_audio_alloc_atomic_state(test, 1, 1);
+	aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+	old_conn_state = kunit_kzalloc(test, sizeof(*old_conn_state), GFP_KERNEL);
+	new_conn_state = kunit_kzalloc(test, sizeof(*new_conn_state), GFP_KERNEL);
+	crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL);
+	dm_crtc_state = kunit_kzalloc(test, sizeof(*dm_crtc_state), GFP_KERNEL);
+	stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, aconnector);
+	KUNIT_ASSERT_NOT_NULL(test, old_conn_state);
+	KUNIT_ASSERT_NOT_NULL(test, new_conn_state);
+	KUNIT_ASSERT_NOT_NULL(test, crtc);
+	KUNIT_ASSERT_NOT_NULL(test, dm_crtc_state);
+	KUNIT_ASSERT_NOT_NULL(test, stream);
+
+	adev->dm.dc = kunit_kzalloc(test, sizeof(*adev->dm.dc), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, adev->dm.dc);
+	adev->dm.dc->current_state = kunit_kzalloc(test,
+							 sizeof(*adev->dm.dc->current_state),
+							 GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, adev->dm.dc->current_state);
+	stream->ctx = kunit_kzalloc(test, sizeof(*stream->ctx), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, stream->ctx);
+	stream->ctx->dc = adev->dm.dc;
+	adev->dm.dc->current_state->stream_count = 1;
+	adev->dm.dc->current_state->streams[0] = stream;
+	adev->dm.dc->current_state->stream_status[0].audio_inst = 8;
+
+	mutex_init(&adev->dm.audio_lock);
+	dm_test_audio_setup_notify_component(test, adev);
+	aconnector->audio_inst = -1;
+	aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA;
+	crtc->index = 0;
+	new_conn_state->crtc = crtc;
+	dm_crtc_state->base.crtc = crtc;
+	dm_crtc_state->base.mode_changed = true;
+	dm_crtc_state->stream = stream;
+	state->connectors[0].ptr = &aconnector->base;
+	state->connectors[0].old_state = old_conn_state;
+	state->connectors[0].new_state = new_conn_state;
+	state->crtcs[0].ptr = crtc;
+	state->crtcs[0].new_state = &dm_crtc_state->base;
+
+	dm_test_eld_notify_count = 0;
+	dm_test_eld_notify_port = -1;
+
+	amdgpu_dm_commit_audio(&adev->ddev, state);
+
+	KUNIT_EXPECT_EQ(test, aconnector->audio_inst, 8);
+	KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 2);
+	KUNIT_EXPECT_EQ(test, dm_test_eld_notify_port, 8);
+}
+
+/**
+ * dm_test_commit_audio_skips_writeback_removal - Test writeback removal skip
+ * @test: The KUnit test context
+ *
+ * Writeback connectors do not represent an HDA audio pin, so removal-style
+ * notifications should leave their audio instance untouched.
+ */
+static void dm_test_commit_audio_skips_writeback_removal(struct kunit *test)
+{
+	struct amdgpu_device *adev = dm_kunit_alloc_adev(test);
+	struct drm_atomic_commit *state;
+	struct amdgpu_dm_connector *aconnector;
+	struct drm_connector_state *old_conn_state;
+	struct drm_connector_state *new_conn_state;
+	struct drm_crtc *crtc;
+
+	state = dm_test_audio_alloc_atomic_state(test, 1, 0);
+	aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+	old_conn_state = kunit_kzalloc(test, sizeof(*old_conn_state), GFP_KERNEL);
+	new_conn_state = kunit_kzalloc(test, sizeof(*new_conn_state), GFP_KERNEL);
+	crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, aconnector);
+	KUNIT_ASSERT_NOT_NULL(test, old_conn_state);
+	KUNIT_ASSERT_NOT_NULL(test, new_conn_state);
+	KUNIT_ASSERT_NOT_NULL(test, crtc);
+
+	mutex_init(&adev->dm.audio_lock);
+	dm_test_audio_setup_notify_component(test, adev);
+	aconnector->audio_inst = 9;
+	aconnector->base.connector_type = DRM_MODE_CONNECTOR_WRITEBACK;
+	old_conn_state->crtc = crtc;
+	state->connectors[0].ptr = &aconnector->base;
+	state->connectors[0].old_state = old_conn_state;
+	state->connectors[0].new_state = new_conn_state;
+
+	dm_test_eld_notify_count = 0;
+
+	amdgpu_dm_commit_audio(&adev->ddev, state);
+
+	KUNIT_EXPECT_EQ(test, aconnector->audio_inst, 9);
+	KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 0);
+}
+
+/**
+ * dm_test_commit_audio_skips_without_new_crtc_state - Test missing CRTC state
+ * @test: The KUnit test context
+ *
+ * A connector still attached to the same CRTC should not notify if the atomic
+ * state does not contain a new CRTC state for that CRTC.
+ */
+static void dm_test_commit_audio_skips_without_new_crtc_state(struct kunit *test)
+{
+	struct amdgpu_device *adev = dm_kunit_alloc_adev(test);
+	struct drm_atomic_commit *state;
+	struct amdgpu_dm_connector *aconnector;
+	struct drm_connector_state *old_conn_state;
+	struct drm_connector_state *new_conn_state;
+	struct drm_crtc *crtc;
+
+	state = dm_test_audio_alloc_atomic_state(test, 1, 1);
+	aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+	old_conn_state = kunit_kzalloc(test, sizeof(*old_conn_state), GFP_KERNEL);
+	new_conn_state = kunit_kzalloc(test, sizeof(*new_conn_state), GFP_KERNEL);
+	crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, aconnector);
+	KUNIT_ASSERT_NOT_NULL(test, old_conn_state);
+	KUNIT_ASSERT_NOT_NULL(test, new_conn_state);
+	KUNIT_ASSERT_NOT_NULL(test, crtc);
+
+	mutex_init(&adev->dm.audio_lock);
+	dm_test_audio_setup_notify_component(test, adev);
+	aconnector->audio_inst = 4;
+	aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA;
+	crtc->index = 0;
+	old_conn_state->crtc = crtc;
+	new_conn_state->crtc = crtc;
+	state->connectors[0].ptr = &aconnector->base;
+	state->connectors[0].old_state = old_conn_state;
+	state->connectors[0].new_state = new_conn_state;
+
+	dm_test_eld_notify_count = 0;
+
+	amdgpu_dm_commit_audio(&adev->ddev, state);
+
+	KUNIT_EXPECT_EQ(test, aconnector->audio_inst, 4);
+	KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 0);
+}
+
+/**
+ * dm_test_commit_audio_skips_without_stream_status - Test missing DC status
+ * @test: The KUnit test context
+ *
+ * If the new CRTC stream is absent from the current DC state, additions should
+ * be skipped because there is no audio instance to publish.
+ */
+static void dm_test_commit_audio_skips_without_stream_status(struct kunit *test)
+{
+	struct amdgpu_device *adev = dm_kunit_alloc_adev(test);
+	struct drm_atomic_commit *state;
+	struct amdgpu_dm_connector *aconnector;
+	struct drm_connector_state *old_conn_state;
+	struct drm_connector_state *new_conn_state;
+	struct drm_crtc *crtc;
+	struct dm_crtc_state *dm_crtc_state;
+	struct dc_stream_state *stream;
+
+	state = dm_test_audio_alloc_atomic_state(test, 1, 1);
+	aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+	old_conn_state = kunit_kzalloc(test, sizeof(*old_conn_state), GFP_KERNEL);
+	new_conn_state = kunit_kzalloc(test, sizeof(*new_conn_state), GFP_KERNEL);
+	crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL);
+	dm_crtc_state = kunit_kzalloc(test, sizeof(*dm_crtc_state), GFP_KERNEL);
+	stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, aconnector);
+	KUNIT_ASSERT_NOT_NULL(test, old_conn_state);
+	KUNIT_ASSERT_NOT_NULL(test, new_conn_state);
+	KUNIT_ASSERT_NOT_NULL(test, crtc);
+	KUNIT_ASSERT_NOT_NULL(test, dm_crtc_state);
+	KUNIT_ASSERT_NOT_NULL(test, stream);
+
+	adev->dm.dc = kunit_kzalloc(test, sizeof(*adev->dm.dc), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, adev->dm.dc);
+	adev->dm.dc->current_state = kunit_kzalloc(test,
+							 sizeof(*adev->dm.dc->current_state),
+							 GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, adev->dm.dc->current_state);
+	stream->ctx = kunit_kzalloc(test, sizeof(*stream->ctx), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, stream->ctx);
+	stream->ctx->dc = adev->dm.dc;
+
+	mutex_init(&adev->dm.audio_lock);
+	dm_test_audio_setup_notify_component(test, adev);
+	aconnector->audio_inst = -1;
+	aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA;
+	crtc->index = 0;
+	new_conn_state->crtc = crtc;
+	dm_crtc_state->base.crtc = crtc;
+	dm_crtc_state->base.mode_changed = true;
+	dm_crtc_state->stream = stream;
+	state->connectors[0].ptr = &aconnector->base;
+	state->connectors[0].old_state = old_conn_state;
+	state->connectors[0].new_state = new_conn_state;
+	state->crtcs[0].ptr = crtc;
+	state->crtcs[0].new_state = &dm_crtc_state->base;
+
+	dm_test_eld_notify_count = 0;
+
+	amdgpu_dm_commit_audio(&adev->ddev, state);
+
+	KUNIT_EXPECT_EQ(test, aconnector->audio_inst, -1);
+	KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 1);
+}
+
+/**
+ * dm_test_commit_audio_skips_detached_connector - Test detached connector skip
+ * @test: The KUnit test context
+ *
+ * A connector that remains detached across the commit should not notify or
+ * alter the stored audio instance.
+ */
+static void dm_test_commit_audio_skips_detached_connector(struct kunit *test)
+{
+	struct amdgpu_device *adev = dm_kunit_alloc_adev(test);
+	struct drm_atomic_commit *state;
+	struct amdgpu_dm_connector *aconnector;
+	struct drm_connector_state *old_conn_state;
+	struct drm_connector_state *new_conn_state;
+
+	state = dm_test_audio_alloc_atomic_state(test, 1, 0);
+	aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+	old_conn_state = kunit_kzalloc(test, sizeof(*old_conn_state), GFP_KERNEL);
+	new_conn_state = kunit_kzalloc(test, sizeof(*new_conn_state), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, aconnector);
+	KUNIT_ASSERT_NOT_NULL(test, old_conn_state);
+	KUNIT_ASSERT_NOT_NULL(test, new_conn_state);
+
+	aconnector->audio_inst = 11;
+	aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA;
+	state->connectors[0].ptr = &aconnector->base;
+	state->connectors[0].old_state = old_conn_state;
+	state->connectors[0].new_state = new_conn_state;
+
+	dm_test_eld_notify_count = 0;
+
+	amdgpu_dm_commit_audio(&adev->ddev, state);
+
+	KUNIT_EXPECT_EQ(test, aconnector->audio_inst, 11);
+	KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 0);
+}
+
+/**
+ * dm_test_commit_audio_skips_without_modeset - Test no-modeset skip
+ * @test: The KUnit test context
+ *
+ * A connector that stays on the same CRTC should not notify if that CRTC does
+ * not need a modeset.
+ */
+static void dm_test_commit_audio_skips_without_modeset(struct kunit *test)
+{
+	struct amdgpu_device *adev = dm_kunit_alloc_adev(test);
+	struct drm_atomic_commit *state;
+	struct amdgpu_dm_connector *aconnector;
+	struct drm_connector_state *old_conn_state;
+	struct drm_connector_state *new_conn_state;
+	struct drm_crtc *crtc;
+	struct dm_crtc_state *dm_crtc_state;
+
+	state = dm_test_audio_alloc_atomic_state(test, 1, 1);
+	aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+	old_conn_state = kunit_kzalloc(test, sizeof(*old_conn_state), GFP_KERNEL);
+	new_conn_state = kunit_kzalloc(test, sizeof(*new_conn_state), GFP_KERNEL);
+	crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL);
+	dm_crtc_state = kunit_kzalloc(test, sizeof(*dm_crtc_state), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, aconnector);
+	KUNIT_ASSERT_NOT_NULL(test, old_conn_state);
+	KUNIT_ASSERT_NOT_NULL(test, new_conn_state);
+	KUNIT_ASSERT_NOT_NULL(test, crtc);
+	KUNIT_ASSERT_NOT_NULL(test, dm_crtc_state);
+
+	aconnector->audio_inst = 12;
+	aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA;
+	crtc->index = 0;
+	old_conn_state->crtc = crtc;
+	new_conn_state->crtc = crtc;
+	dm_crtc_state->base.crtc = crtc;
+	state->connectors[0].ptr = &aconnector->base;
+	state->connectors[0].old_state = old_conn_state;
+	state->connectors[0].new_state = new_conn_state;
+	state->crtcs[0].ptr = crtc;
+	state->crtcs[0].new_state = &dm_crtc_state->base;
+
+	dm_test_eld_notify_count = 0;
+
+	amdgpu_dm_commit_audio(&adev->ddev, state);
+
+	KUNIT_EXPECT_EQ(test, aconnector->audio_inst, 12);
+	KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 0);
+}
+
+/**
+ * dm_test_commit_audio_skips_addition_without_stream - Test NULL stream skip
+ * @test: The KUnit test context
+ *
+ * A modeset still sends the removal-side ELD notification, but the addition
+ * side must skip if the new DM CRTC state has no stream.
+ */
+static void dm_test_commit_audio_skips_addition_without_stream(struct kunit *test)
+{
+	struct amdgpu_device *adev = dm_kunit_alloc_adev(test);
+	struct drm_atomic_commit *state;
+	struct amdgpu_dm_connector *aconnector;
+	struct drm_connector_state *old_conn_state;
+	struct drm_connector_state *new_conn_state;
+	struct drm_crtc *crtc;
+	struct dm_crtc_state *dm_crtc_state;
+
+	state = dm_test_audio_alloc_atomic_state(test, 1, 1);
+	aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+	old_conn_state = kunit_kzalloc(test, sizeof(*old_conn_state), GFP_KERNEL);
+	new_conn_state = kunit_kzalloc(test, sizeof(*new_conn_state), GFP_KERNEL);
+	crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL);
+	dm_crtc_state = kunit_kzalloc(test, sizeof(*dm_crtc_state), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, aconnector);
+	KUNIT_ASSERT_NOT_NULL(test, old_conn_state);
+	KUNIT_ASSERT_NOT_NULL(test, new_conn_state);
+	KUNIT_ASSERT_NOT_NULL(test, crtc);
+	KUNIT_ASSERT_NOT_NULL(test, dm_crtc_state);
+
+	mutex_init(&adev->dm.audio_lock);
+	dm_test_audio_setup_notify_component(test, adev);
+	aconnector->audio_inst = 13;
+	aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA;
+	crtc->index = 0;
+	old_conn_state->crtc = crtc;
+	new_conn_state->crtc = crtc;
+	dm_crtc_state->base.crtc = crtc;
+	dm_crtc_state->base.mode_changed = true;
+	state->connectors[0].ptr = &aconnector->base;
+	state->connectors[0].old_state = old_conn_state;
+	state->connectors[0].new_state = new_conn_state;
+	state->crtcs[0].ptr = crtc;
+	state->crtcs[0].new_state = &dm_crtc_state->base;
+
+	dm_test_eld_notify_count = 0;
+	dm_test_eld_notify_port = -1;
+
+	amdgpu_dm_commit_audio(&adev->ddev, state);
+
+	KUNIT_EXPECT_EQ(test, aconnector->audio_inst, -1);
+	KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 1);
+	KUNIT_EXPECT_EQ(test, dm_test_eld_notify_port, 13);
+}
+
+/**
+ * dm_test_commit_audio_skips_writeback_addition - Test writeback addition skip
+ * @test: The KUnit test context
+ *
+ * Even with a valid modeset and stream status, writeback connectors must not
+ * publish an HDA audio instance.
+ */
+static void dm_test_commit_audio_skips_writeback_addition(struct kunit *test)
+{
+	struct amdgpu_device *adev = dm_kunit_alloc_adev(test);
+	struct drm_atomic_commit *state;
+	struct amdgpu_dm_connector *aconnector;
+	struct drm_connector_state *old_conn_state;
+	struct drm_connector_state *new_conn_state;
+	struct drm_crtc *crtc;
+	struct dm_crtc_state *dm_crtc_state;
+	struct dc_stream_state *stream;
+
+	state = dm_test_audio_alloc_atomic_state(test, 1, 1);
+	aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+	old_conn_state = kunit_kzalloc(test, sizeof(*old_conn_state), GFP_KERNEL);
+	new_conn_state = kunit_kzalloc(test, sizeof(*new_conn_state), GFP_KERNEL);
+	crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL);
+	dm_crtc_state = kunit_kzalloc(test, sizeof(*dm_crtc_state), GFP_KERNEL);
+	stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, aconnector);
+	KUNIT_ASSERT_NOT_NULL(test, old_conn_state);
+	KUNIT_ASSERT_NOT_NULL(test, new_conn_state);
+	KUNIT_ASSERT_NOT_NULL(test, crtc);
+	KUNIT_ASSERT_NOT_NULL(test, dm_crtc_state);
+	KUNIT_ASSERT_NOT_NULL(test, stream);
+
+	adev->dm.dc = kunit_kzalloc(test, sizeof(*adev->dm.dc), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, adev->dm.dc);
+	adev->dm.dc->current_state = kunit_kzalloc(test,
+							 sizeof(*adev->dm.dc->current_state),
+							 GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, adev->dm.dc->current_state);
+	stream->ctx = kunit_kzalloc(test, sizeof(*stream->ctx), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, stream->ctx);
+	stream->ctx->dc = adev->dm.dc;
+	adev->dm.dc->current_state->stream_count = 1;
+	adev->dm.dc->current_state->streams[0] = stream;
+	adev->dm.dc->current_state->stream_status[0].audio_inst = 14;
+
+	mutex_init(&adev->dm.audio_lock);
+	dm_test_audio_setup_notify_component(test, adev);
+	aconnector->audio_inst = 15;
+	aconnector->base.connector_type = DRM_MODE_CONNECTOR_WRITEBACK;
+	crtc->index = 0;
+	new_conn_state->crtc = crtc;
+	dm_crtc_state->base.crtc = crtc;
+	dm_crtc_state->base.mode_changed = true;
+	dm_crtc_state->stream = stream;
+	state->connectors[0].ptr = &aconnector->base;
+	state->connectors[0].old_state = old_conn_state;
+	state->connectors[0].new_state = new_conn_state;
+	state->crtcs[0].ptr = crtc;
+	state->crtcs[0].new_state = &dm_crtc_state->base;
+
+	dm_test_eld_notify_count = 0;
+
+	amdgpu_dm_commit_audio(&adev->ddev, state);
+
+	KUNIT_EXPECT_EQ(test, aconnector->audio_inst, 15);
+	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),
@@ -712,6 +1234,16 @@ static struct kunit_case dm_audio_test_cases[] = {
 	/* amdgpu_dm_audio_component_get_eld */
 	KUNIT_CASE(dm_test_audio_component_get_eld_copies_matching_connector),
 	KUNIT_CASE(dm_test_audio_component_get_eld_no_match),
+	/* amdgpu_dm_commit_audio */
+	KUNIT_CASE(dm_test_commit_audio_notifies_removed_connector),
+	KUNIT_CASE(dm_test_commit_audio_notifies_added_connector),
+	KUNIT_CASE(dm_test_commit_audio_skips_writeback_removal),
+	KUNIT_CASE(dm_test_commit_audio_skips_without_new_crtc_state),
+	KUNIT_CASE(dm_test_commit_audio_skips_without_stream_status),
+	KUNIT_CASE(dm_test_commit_audio_skips_detached_connector),
+	KUNIT_CASE(dm_test_commit_audio_skips_without_modeset),
+	KUNIT_CASE(dm_test_commit_audio_skips_addition_without_stream),
+	KUNIT_CASE(dm_test_commit_audio_skips_writeback_addition),
 	{}
 };
 
-- 
2.53.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.