From: Alex Hung <[email protected]>

Add KUnit coverage for the following MST functions:

- dm_dp_aux_transfer(): native read/write, partial write, error
  result remapping, and HPD disconnect quirk via fake DC link service
- dm_dp_aux_transfer_result(): error code translation
- dm_dp_aux_fill_payload_flags(): request flag decoding
- dm_mst_msg_ready_mask(): ESI mask selection
- dm_mst_select_esi_dpcd(): DPCD address/length selection
- dm_mst_atomic_best_encoder(): encoder selection by CRTC ID
- dm_dp_mst_detect(): unregistered connector early return
- dm_dp_mst_atomic_check(): no-old-CRTC early return
- dm_dp_create_fake_mst_encoders(): encoder init and CRTC mask
- dm_handle_mst_sideband_msg_ready_event(): idle no-ready-bits
- retrieve_branch_specific_data(): branch OUI parsing
- retrieve_downstream_port_device(): downstream port present
- needs_dsc_aux_workaround(): DSC workaround matching
- dm_mst_get_pbn_divider(): null link guard
- amdgpu_dm_mst_reset_mst_connector_setting(): field reset
- dm_dp_mst_is_port_support_mode(): FP-off fallback

Assisted-by: Copilot:GPT-5.5
Reviewed-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Alex Hung <[email protected]>
Signed-off-by: George Zhang <[email protected]>
---
 .../display/amdgpu_dm/amdgpu_dm_mst_types.c   |  19 +-
 .../display/amdgpu_dm/amdgpu_dm_mst_types.h   |   7 +
 .../tests/amdgpu_dm_mst_types_test.c          | 572 ++++++++++++++++++
 3 files changed, 592 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
index 83d0e1403e54..0392c71eab63 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
@@ -99,8 +99,8 @@ EXPORT_IF_KUNIT(dm_dp_aux_fill_payload_flags);
 /*
  * This function handles both native AUX and I2C-Over-AUX transactions.
  */
-static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux,
-                                 struct drm_dp_aux_msg *msg)
+STATIC_IFN_KUNIT ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux,
+                                           struct drm_dp_aux_msg *msg)
 {
        ssize_t result = 0;
        struct aux_payload payload;
@@ -167,6 +167,7 @@ static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux,
 
        return result;
 }
+EXPORT_IF_KUNIT(dm_dp_aux_transfer);
 
 static void
 dm_dp_mst_connector_destroy(struct drm_connector *connector)
@@ -518,7 +519,7 @@ static int dm_dp_mst_get_modes(struct drm_connector 
*connector)
        return ret;
 }
 
-static struct drm_encoder *
+STATIC_IFN_KUNIT struct drm_encoder *
 dm_mst_atomic_best_encoder(struct drm_connector *connector,
                           struct drm_atomic_state *state)
 {
@@ -529,8 +530,9 @@ dm_mst_atomic_best_encoder(struct drm_connector *connector,
 
        return &adev->dm.mst_encoders[acrtc->crtc_id].base;
 }
+EXPORT_IF_KUNIT(dm_mst_atomic_best_encoder);
 
-static int
+STATIC_IFN_KUNIT int
 dm_dp_mst_detect(struct drm_connector *connector,
                 struct drm_modeset_acquire_ctx *ctx, bool force)
 {
@@ -600,9 +602,10 @@ dm_dp_mst_detect(struct drm_connector *connector,
 
        return connection_status;
 }
+EXPORT_IF_KUNIT(dm_dp_mst_detect);
 
-static int dm_dp_mst_atomic_check(struct drm_connector *connector,
-                                 struct drm_atomic_state *state)
+STATIC_IFN_KUNIT int dm_dp_mst_atomic_check(struct drm_connector *connector,
+                                           struct drm_atomic_state *state)
 {
        struct amdgpu_dm_connector *aconnector = 
to_amdgpu_dm_connector(connector);
        struct drm_dp_mst_topology_mgr *mst_mgr = 
&aconnector->mst_root->mst_mgr;
@@ -610,6 +613,7 @@ static int dm_dp_mst_atomic_check(struct drm_connector 
*connector,
 
        return drm_dp_atomic_release_time_slots(state, mst_mgr, mst_port);
 }
+EXPORT_IF_KUNIT(dm_dp_mst_atomic_check);
 
 static const struct drm_connector_helper_funcs 
dm_dp_mst_connector_helper_funcs = {
        .get_modes = dm_dp_mst_get_modes,
@@ -650,6 +654,7 @@ dm_dp_create_fake_mst_encoders(struct amdgpu_device *adev)
                drm_encoder_helper_add(encoder, 
&amdgpu_dm_encoder_helper_funcs);
        }
 }
+EXPORT_IF_KUNIT(dm_dp_create_fake_mst_encoders);
 
 static struct drm_connector *
 dm_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
@@ -855,6 +860,7 @@ void dm_handle_mst_sideband_msg_ready_event(
        if (process_count == max_process_count)
                DRM_DEBUG_DRIVER("Loop exceeded max iterations\n");
 }
+EXPORT_IF_KUNIT(dm_handle_mst_sideband_msg_ready_event);
 
 static void dm_handle_mst_down_rep_msg_ready(struct drm_dp_mst_topology_mgr 
*mgr)
 {
@@ -2108,3 +2114,4 @@ enum dc_status dm_dp_mst_is_port_support_mode(
 #endif
        return DC_OK;
 }
+EXPORT_IF_KUNIT(dm_dp_mst_is_port_support_mode);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
index 5e5190671923..f0887812ae60 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
@@ -108,8 +108,15 @@ bool retrieve_branch_specific_data(struct 
amdgpu_dm_connector *aconnector);
 ssize_t dm_dp_aux_transfer_result(ssize_t result,
                                  enum aux_return_code_type operation_result);
 void dm_dp_aux_fill_payload_flags(u8 request, struct aux_payload *payload);
+ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg);
 u8 dm_mst_msg_ready_mask(enum mst_msg_ready_type msg_rdy_type);
 void dm_mst_select_esi_dpcd(u8 dpcd_rev, int *dpcd_addr, u8 
*dpcd_bytes_to_read);
+struct drm_encoder *dm_mst_atomic_best_encoder(struct drm_connector *connector,
+                                                struct drm_atomic_state 
*state);
+int dm_dp_mst_atomic_check(struct drm_connector *connector,
+                                  struct drm_atomic_state *state);
+int dm_dp_mst_detect(struct drm_connector *connector,
+                            struct drm_modeset_acquire_ctx *ctx, bool force);
 #endif
 
 #endif
diff --git 
a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c
index e3b171992be1..638e6fa97a11 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c
@@ -7,6 +7,8 @@
 
 #include <kunit/test.h>
 
+#include <drm/drm_drv.h>
+#include <drm/drm_kunit_helpers.h>
 #include <drm/display/drm_dp.h>
 #include <drm/display/drm_dp_helper.h>
 #include <drm/display/drm_dp_mst_helper.h>
@@ -18,12 +20,67 @@
 #include "amdgpu_mode.h"
 #include "amdgpu_dm.h"
 #include "amdgpu_dm_mst_types.h"
+#include "inc/link_service.h"
 
 /*
  * Minimal mock DPCD backing store and AUX transfer callback used to exercise
  * the DPCD read paths without real hardware.
  */
 static u8 dm_mst_test_dpcd[0x10];
+static u8 dm_mst_test_desc_dpcd[0x10];
+static struct aux_payload dm_mst_test_last_payload;
+static int dm_mst_test_aux_transfer_raw_result;
+static enum aux_return_code_type dm_mst_test_aux_transfer_raw_operation_result;
+
+static int dm_mst_test_aux_transfer_raw(struct ddc_service *ddc,
+                                               struct aux_payload *payload,
+                                               enum aux_return_code_type 
*operation_result)
+{
+       size_t i;
+
+       dm_mst_test_last_payload = *payload;
+       *operation_result = dm_mst_test_aux_transfer_raw_operation_result;
+
+       if (dm_mst_test_aux_transfer_raw_result)
+               return dm_mst_test_aux_transfer_raw_result;
+
+       if (payload->write)
+               return 0;
+
+       for (i = 0; i < payload->length; i++)
+               payload->data[i] = dm_mst_test_dpcd[(payload->address + i) & 
0xf];
+
+       return payload->length;
+}
+
+static void dm_mst_test_setup_dm_aux(struct amdgpu_dm_dp_aux *dm_aux,
+                                           struct ddc_service *ddc,
+                                           struct dc_link *link,
+                                           struct dc *dc,
+                                           struct link_service *link_srv,
+                                           struct dc_context *ctx,
+                                           struct amdgpu_device *adev)
+{
+       memset(&dm_mst_test_last_payload, 0, sizeof(dm_mst_test_last_payload));
+       dm_mst_test_aux_transfer_raw_result = 0;
+       dm_mst_test_aux_transfer_raw_operation_result = AUX_RET_SUCCESS;
+       link_srv->aux_transfer_raw = dm_mst_test_aux_transfer_raw;
+       dc->link_srv = link_srv;
+       link->dc = dc;
+       ctx->driver_context = adev;
+       ddc->link = link;
+       ddc->ctx = ctx;
+       dm_aux->ddc_service = ddc;
+       dm_aux->aux.name = "dm_mst_test_dm_aux";
+       dm_aux->aux.transfer = dm_dp_aux_transfer;
+       drm_dp_aux_init(&dm_aux->aux);
+       drm_dp_dpcd_set_probe(&dm_aux->aux, false);
+}
+
+static const struct dc_link_status *dm_mst_test_get_status(const struct 
dc_link *link)
+{
+       return &link->link_status;
+}
 
 static ssize_t dm_mst_test_aux_transfer(struct drm_dp_aux *aux,
                                        struct drm_dp_aux_msg *msg)
@@ -45,6 +102,21 @@ static ssize_t dm_mst_test_aux_transfer(struct drm_dp_aux 
*aux,
        }
 }
 
+static ssize_t dm_mst_test_desc_aux_transfer(struct drm_dp_aux *aux,
+                                            struct drm_dp_aux_msg *msg)
+{
+       size_t i;
+
+       if ((msg->request & ~DP_AUX_I2C_MOT) != DP_AUX_NATIVE_READ)
+               return -EINVAL;
+
+       for (i = 0; i < msg->size; i++)
+               ((u8 *)msg->buffer)[i] = dm_mst_test_desc_dpcd[msg->address + i 
- DP_BRANCH_OUI];
+
+       msg->reply = DP_AUX_NATIVE_REPLY_ACK;
+       return msg->size;
+}
+
 /* Tests for needs_dsc_aux_workaround */
 
 /**
@@ -285,6 +357,51 @@ static void dm_mst_test_retrieve_branch_no_parent(struct 
kunit *test)
        KUNIT_EXPECT_FALSE(test, retrieve_branch_specific_data(aconnector));
 }
 
+/**
+ * dm_mst_test_retrieve_branch_reads_oui - Test branch OUI parsing
+ * @test: KUnit test context
+ *
+ * Verify that retrieve_branch_specific_data() reads the immediate upstream
+ * branch descriptor and caches its IEEE OUI value on the connector.
+ */
+static void dm_mst_test_retrieve_branch_reads_oui(struct kunit *test)
+{
+       struct amdgpu_dm_connector *aconnector;
+       struct drm_dp_mst_topology_mgr *mgr;
+       struct drm_dp_mst_branch *branch;
+       struct drm_dp_mst_port *port;
+       struct drm_dp_aux *aux;
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       mgr = kunit_kzalloc(test, sizeof(*mgr), GFP_KERNEL);
+       branch = kunit_kzalloc(test, sizeof(*branch), GFP_KERNEL);
+       port = kunit_kzalloc(test, sizeof(*port), GFP_KERNEL);
+       aux = kunit_kzalloc(test, sizeof(*aux), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+       KUNIT_ASSERT_NOT_NULL(test, mgr);
+       KUNIT_ASSERT_NOT_NULL(test, branch);
+       KUNIT_ASSERT_NOT_NULL(test, port);
+       KUNIT_ASSERT_NOT_NULL(test, aux);
+
+       memset(dm_mst_test_desc_dpcd, 0, sizeof(dm_mst_test_desc_dpcd));
+       dm_mst_test_desc_dpcd[0] = 0x12;
+       dm_mst_test_desc_dpcd[1] = 0x34;
+       dm_mst_test_desc_dpcd[2] = 0x56;
+
+       aux->name = "dm_mst_test_desc_aux";
+       aux->transfer = dm_mst_test_desc_aux_transfer;
+       drm_dp_aux_init(aux);
+       drm_dp_dpcd_set_probe(aux, false);
+       mgr->aux = aux;
+       port->parent = branch;
+       port->mgr = mgr;
+       port->aux.drm_dev = NULL;
+       aconnector->mst_output_port = port;
+
+       KUNIT_EXPECT_TRUE(test, retrieve_branch_specific_data(aconnector));
+       KUNIT_EXPECT_EQ(test, aconnector->branch_ieee_oui, 0x123456U);
+}
+
 /**
  * dm_mst_test_aux_result_success - AUX_RET_SUCCESS preserves the input result.
  * @test: KUnit test context.
@@ -340,6 +457,246 @@ static void dm_mst_test_aux_result_timeout(struct kunit 
*test)
                        (ssize_t)-ETIMEDOUT);
 }
 
+/**
+ * dm_mst_test_aux_transfer_native_read - native AUX read through DM callback.
+ * @test: KUnit test context.
+ *
+ * The DM AUX transfer callback should build a read payload, call the DC link
+ * service, and return the number of bytes provided by the fake backend.
+ */
+static void dm_mst_test_aux_transfer_native_read(struct kunit *test)
+{
+       struct amdgpu_dm_dp_aux *dm_aux;
+       struct amdgpu_device *adev;
+       struct ddc_service *ddc;
+       struct dc_link *link;
+       struct dc *dc;
+       struct link_service *link_srv;
+       struct dc_context *ctx;
+       u8 buffer[3] = { 0 };
+       ssize_t ret;
+
+       dm_aux = kunit_kzalloc(test, sizeof(*dm_aux), GFP_KERNEL);
+       adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+       ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL);
+       link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+       dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+       link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
+       ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, dm_aux);
+       KUNIT_ASSERT_NOT_NULL(test, adev);
+       KUNIT_ASSERT_NOT_NULL(test, ddc);
+       KUNIT_ASSERT_NOT_NULL(test, link);
+       KUNIT_ASSERT_NOT_NULL(test, dc);
+       KUNIT_ASSERT_NOT_NULL(test, link_srv);
+       KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+       memset(dm_mst_test_dpcd, 0, sizeof(dm_mst_test_dpcd));
+       dm_mst_test_dpcd[4] = 0xaa;
+       dm_mst_test_dpcd[5] = 0xbb;
+       dm_mst_test_dpcd[6] = 0xcc;
+       dm_mst_test_setup_dm_aux(dm_aux, ddc, link, dc, link_srv, ctx, adev);
+
+       ret = drm_dp_dpcd_read(&dm_aux->aux, 4, buffer, sizeof(buffer));
+
+       KUNIT_EXPECT_EQ(test, ret, (ssize_t)sizeof(buffer));
+       KUNIT_EXPECT_EQ(test, buffer[0], (u8)0xaa);
+       KUNIT_EXPECT_EQ(test, buffer[1], (u8)0xbb);
+       KUNIT_EXPECT_EQ(test, buffer[2], (u8)0xcc);
+       KUNIT_EXPECT_FALSE(test, dm_mst_test_last_payload.write);
+       KUNIT_EXPECT_FALSE(test, dm_mst_test_last_payload.i2c_over_aux);
+       KUNIT_EXPECT_EQ(test, dm_mst_test_last_payload.address, 4U);
+}
+
+/**
+ * dm_mst_test_aux_transfer_native_write - native AUX write through DM 
callback.
+ * @test: KUnit test context.
+ *
+ * A successful write with an ACK reply should report the requested write size
+ * and pass a write payload into the fake DC link service.
+ */
+static void dm_mst_test_aux_transfer_native_write(struct kunit *test)
+{
+       struct amdgpu_dm_dp_aux *dm_aux;
+       struct amdgpu_device *adev;
+       struct ddc_service *ddc;
+       struct dc_link *link;
+       struct dc *dc;
+       struct link_service *link_srv;
+       struct dc_context *ctx;
+       u8 buffer[2] = { 0x11, 0x22 };
+       ssize_t ret;
+
+       dm_aux = kunit_kzalloc(test, sizeof(*dm_aux), GFP_KERNEL);
+       adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+       ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL);
+       link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+       dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+       link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
+       ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, dm_aux);
+       KUNIT_ASSERT_NOT_NULL(test, adev);
+       KUNIT_ASSERT_NOT_NULL(test, ddc);
+       KUNIT_ASSERT_NOT_NULL(test, link);
+       KUNIT_ASSERT_NOT_NULL(test, dc);
+       KUNIT_ASSERT_NOT_NULL(test, link_srv);
+       KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+       dm_mst_test_setup_dm_aux(dm_aux, ddc, link, dc, link_srv, ctx, adev);
+
+       ret = drm_dp_dpcd_write(&dm_aux->aux, 7, buffer, sizeof(buffer));
+
+       KUNIT_EXPECT_EQ(test, ret, (ssize_t)sizeof(buffer));
+       KUNIT_EXPECT_TRUE(test, dm_mst_test_last_payload.write);
+       KUNIT_EXPECT_FALSE(test, dm_mst_test_last_payload.i2c_over_aux);
+       KUNIT_EXPECT_EQ(test, dm_mst_test_last_payload.address, 7U);
+       KUNIT_EXPECT_EQ(test, dm_mst_test_last_payload.length,
+                       (u32)sizeof(buffer));
+}
+
+/**
+ * dm_mst_test_aux_transfer_partial_write - partial write reports byte count.
+ * @test: KUnit test context.
+ *
+ * A positive write result from the DC link service should be interpreted as a
+ * partial write and replaced with the first payload byte.
+ */
+static void dm_mst_test_aux_transfer_partial_write(struct kunit *test)
+{
+       struct amdgpu_dm_dp_aux *dm_aux;
+       struct amdgpu_device *adev;
+       struct ddc_service *ddc;
+       struct dc_link *link;
+       struct dc *dc;
+       struct link_service *link_srv;
+       struct dc_context *ctx;
+       u8 buffer[2] = { 1, 0xaa };
+       struct drm_dp_aux_msg msg = {
+               .address = 7,
+               .request = DP_AUX_NATIVE_WRITE,
+               .buffer = buffer,
+               .size = sizeof(buffer),
+       };
+       ssize_t ret;
+
+       dm_aux = kunit_kzalloc(test, sizeof(*dm_aux), GFP_KERNEL);
+       adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+       ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL);
+       link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+       dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+       link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
+       ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, dm_aux);
+       KUNIT_ASSERT_NOT_NULL(test, adev);
+       KUNIT_ASSERT_NOT_NULL(test, ddc);
+       KUNIT_ASSERT_NOT_NULL(test, link);
+       KUNIT_ASSERT_NOT_NULL(test, dc);
+       KUNIT_ASSERT_NOT_NULL(test, link_srv);
+       KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+       dm_mst_test_setup_dm_aux(dm_aux, ddc, link, dc, link_srv, ctx, adev);
+       dm_mst_test_aux_transfer_raw_result = 1;
+
+       ret = dm_dp_aux_transfer(&dm_aux->aux, &msg);
+
+       KUNIT_EXPECT_EQ(test, ret, (ssize_t)buffer[0]);
+       KUNIT_EXPECT_TRUE(test, dm_mst_test_last_payload.write);
+       KUNIT_EXPECT_EQ(test, dm_mst_test_last_payload.address, 7U);
+}
+
+/**
+ * dm_mst_test_aux_transfer_error_result - transfer errors are remapped.
+ * @test: KUnit test context.
+ *
+ * A negative DC link service result should be converted through
+ * dm_dp_aux_transfer_result() using the returned AUX operation result.
+ */
+static void dm_mst_test_aux_transfer_error_result(struct kunit *test)
+{
+       struct amdgpu_dm_dp_aux *dm_aux;
+       struct amdgpu_device *adev;
+       struct ddc_service *ddc;
+       struct dc_link *link;
+       struct dc *dc;
+       struct link_service *link_srv;
+       struct dc_context *ctx;
+       u8 buffer[2] = { 0 };
+       ssize_t ret;
+
+       dm_aux = kunit_kzalloc(test, sizeof(*dm_aux), GFP_KERNEL);
+       adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+       ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL);
+       link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+       dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+       link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
+       ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, dm_aux);
+       KUNIT_ASSERT_NOT_NULL(test, adev);
+       KUNIT_ASSERT_NOT_NULL(test, ddc);
+       KUNIT_ASSERT_NOT_NULL(test, link);
+       KUNIT_ASSERT_NOT_NULL(test, dc);
+       KUNIT_ASSERT_NOT_NULL(test, link_srv);
+       KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+       dm_mst_test_setup_dm_aux(dm_aux, ddc, link, dc, link_srv, ctx, adev);
+       dm_mst_test_aux_transfer_raw_result = -EIO;
+       dm_mst_test_aux_transfer_raw_operation_result = AUX_RET_ERROR_TIMEOUT;
+
+       ret = drm_dp_dpcd_read(&dm_aux->aux, 4, buffer, sizeof(buffer));
+
+       KUNIT_EXPECT_EQ(test, ret, (ssize_t)-ETIMEDOUT);
+       KUNIT_EXPECT_FALSE(test, dm_mst_test_last_payload.write);
+       KUNIT_EXPECT_EQ(test, dm_mst_test_last_payload.address, 4U);
+}
+
+/**
+ * dm_mst_test_aux_transfer_hpd_discon_quirk - HPD disconnect quirk succeeds.
+ * @test: KUnit test context.
+ *
+ * AUX_RET_ERROR_HPD_DISCON on the sideband down request address should be
+ * treated as a successful transfer when the platform quirk is enabled.
+ */
+static void dm_mst_test_aux_transfer_hpd_discon_quirk(struct kunit *test)
+{
+       struct amdgpu_dm_dp_aux *dm_aux;
+       struct amdgpu_device *adev;
+       struct ddc_service *ddc;
+       struct dc_link *link;
+       struct dc *dc;
+       struct link_service *link_srv;
+       struct dc_context *ctx;
+       u8 buffer[2] = { 2, 0 };
+       ssize_t ret;
+
+       dm_aux = kunit_kzalloc(test, sizeof(*dm_aux), GFP_KERNEL);
+       adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+       ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL);
+       link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+       dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+       link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
+       ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, dm_aux);
+       KUNIT_ASSERT_NOT_NULL(test, adev);
+       KUNIT_ASSERT_NOT_NULL(test, ddc);
+       KUNIT_ASSERT_NOT_NULL(test, link);
+       KUNIT_ASSERT_NOT_NULL(test, dc);
+       KUNIT_ASSERT_NOT_NULL(test, link_srv);
+       KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+       dm_mst_test_setup_dm_aux(dm_aux, ddc, link, dc, link_srv, ctx, adev);
+       adev->dm.aux_hpd_discon_quirk = true;
+       dm_mst_test_aux_transfer_raw_result = -EIO;
+       dm_mst_test_aux_transfer_raw_operation_result = 
AUX_RET_ERROR_HPD_DISCON;
+
+       ret = drm_dp_dpcd_write(&dm_aux->aux, DP_SIDEBAND_MSG_DOWN_REQ_BASE,
+                                        buffer, sizeof(buffer));
+
+       KUNIT_EXPECT_EQ(test, ret, (ssize_t)sizeof(buffer));
+       KUNIT_EXPECT_TRUE(test, dm_mst_test_last_payload.write);
+       KUNIT_EXPECT_EQ(test, dm_mst_test_last_payload.address,
+                       DP_SIDEBAND_MSG_DOWN_REQ_BASE);
+}
+
 /**
  * dm_mst_test_fill_payload_flags_native_write - native write request decode.
  * @test: KUnit test context.
@@ -463,6 +820,203 @@ static void dm_mst_test_select_esi_dpcd_esi(struct kunit 
*test)
                        (int)(DP_PSR_ERROR_STATUS - DP_SINK_COUNT_ESI));
 }
 
+/**
+ * dm_mst_test_sideband_msg_ready_no_ready_bits - Test idle sideband event
+ * @test: KUnit test context
+ *
+ * Verify that dm_handle_mst_sideband_msg_ready_event() returns cleanly when
+ * the ESI read succeeds but no DOWN_REP/UP_REQ ready bits are set.
+ */
+static void dm_mst_test_sideband_msg_ready_no_ready_bits(struct kunit *test)
+{
+       struct amdgpu_dm_connector *aconnector;
+       struct link_service *link_srv;
+       struct dc_link *link;
+       struct dc *dc;
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
+       link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+       dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+       KUNIT_ASSERT_NOT_NULL(test, link_srv);
+       KUNIT_ASSERT_NOT_NULL(test, link);
+       KUNIT_ASSERT_NOT_NULL(test, dc);
+
+       mutex_init(&aconnector->handle_mst_msg_ready);
+       link_srv->get_status = dm_mst_test_get_status;
+       dc->link_srv = link_srv;
+       link->dc = dc;
+       link->dpcd_caps.dpcd_rev.raw = DPCD_REV_14;
+       link->link_status.dpcd_caps = &link->dpcd_caps;
+       aconnector->dc_link = link;
+       aconnector->dm_dp_aux.aux.name = "dm_mst_test_sideband_aux";
+       aconnector->dm_dp_aux.aux.transfer = dm_mst_test_aux_transfer;
+       drm_dp_aux_init(&aconnector->dm_dp_aux.aux);
+       drm_dp_dpcd_set_probe(&aconnector->dm_dp_aux.aux, false);
+       memset(dm_mst_test_dpcd, 0, sizeof(dm_mst_test_dpcd));
+
+       dm_handle_mst_sideband_msg_ready_event(&aconnector->mst_mgr,
+                                              DOWN_REP_MSG_RDY_EVENT);
+
+       KUNIT_EXPECT_EQ(test, dm_mst_test_dpcd[1], (u8)0);
+}
+
+/**
+ * dm_mst_test_atomic_best_encoder - Test MST encoder selection
+ * @test: KUnit test context
+ *
+ * Verify that dm_mst_atomic_best_encoder() selects the MST encoder indexed by
+ * the CRTC ID in the connector's new atomic state. This uses structural DRM
+ * mocks only; registering connector/CRTC objects is unnecessary for this 
helper.
+ */
+static void dm_mst_test_atomic_best_encoder(struct kunit *test)
+{
+       struct drm_connector_state connector_state = { 0 };
+       struct drm_atomic_state state = { 0 };
+       struct amdgpu_dm_connector *aconnector;
+       struct amdgpu_device *adev;
+       struct amdgpu_crtc *acrtc;
+       unsigned int connector_index = 3;
+
+       adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, adev);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+       KUNIT_ASSERT_NOT_NULL(test, acrtc);
+
+       aconnector->base.dev = &adev->ddev;
+       aconnector->base.index = connector_index;
+       acrtc->crtc_id = 2;
+       connector_state.connector = &aconnector->base;
+       connector_state.crtc = &acrtc->base;
+       state.num_connector = connector_index + 1;
+       state.connectors = kunit_kzalloc(test,
+                                        sizeof(*state.connectors) * 
state.num_connector,
+                                        GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, state.connectors);
+       state.connectors[connector_index].ptr = &aconnector->base;
+       state.connectors[connector_index].new_state = &connector_state;
+
+       KUNIT_EXPECT_PTR_EQ(test, dm_mst_atomic_best_encoder(&aconnector->base, 
&state),
+                            &adev->dm.mst_encoders[2].base);
+}
+
+/**
+ * dm_mst_test_create_fake_mst_encoders - Test fake MST encoder setup
+ * @test: KUnit test context
+ *
+ * Verify that dm_dp_create_fake_mst_encoders() initializes the requested MST
+ * encoders as DPMST encoders with the CRTC mask derived from the device state.
+ */
+static void dm_mst_test_create_fake_mst_encoders(struct kunit *test)
+{
+       struct amdgpu_device *adev;
+       struct drm_device *drm;
+       struct device *dev;
+       int i;
+
+       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(*adev),
+                                                  offsetof(struct 
amdgpu_device, ddev),
+                                                  DRIVER_MODESET | 
DRIVER_ATOMIC);
+       KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
+       adev = drm_to_adev(drm);
+       adev->dm.display_indexes_num = 3;
+       adev->mode_info.num_crtc = 3;
+
+       dm_dp_create_fake_mst_encoders(adev);
+
+       for (i = 0; i < adev->dm.display_indexes_num; i++) {
+               struct drm_encoder *encoder = &adev->dm.mst_encoders[i].base;
+
+               KUNIT_EXPECT_PTR_EQ(test, encoder->dev, drm);
+               KUNIT_EXPECT_EQ(test, encoder->encoder_type, 
DRM_MODE_ENCODER_DPMST);
+               KUNIT_EXPECT_EQ(test, encoder->possible_crtcs, 0x7U);
+               KUNIT_EXPECT_TRUE(test, encoder->helper_private != NULL);
+       }
+}
+
+/**
+ * dm_mst_test_atomic_check_no_old_crtc - Test atomic check no-op path
+ * @test: KUnit test context
+ *
+ * Verify that dm_dp_mst_atomic_check() returns success when the MST port's old
+ * connector state has no CRTC, before MST topology state is required.
+ */
+static void dm_mst_test_atomic_check_no_old_crtc(struct kunit *test)
+{
+       struct drm_connector_state old_conn_state = { 0 };
+       struct drm_connector_state new_conn_state = { 0 };
+       struct drm_atomic_state state = { 0 };
+       struct amdgpu_dm_connector *aconnector;
+       struct amdgpu_dm_connector *root;
+       struct drm_dp_mst_port *port;
+       unsigned int connector_index = 2;
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       root = kunit_kzalloc(test, sizeof(*root), GFP_KERNEL);
+       port = kunit_kzalloc(test, sizeof(*port), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+       KUNIT_ASSERT_NOT_NULL(test, root);
+       KUNIT_ASSERT_NOT_NULL(test, port);
+
+       aconnector->base.index = connector_index;
+       aconnector->mst_root = root;
+       aconnector->mst_output_port = port;
+       port->connector = &aconnector->base;
+       old_conn_state.connector = &aconnector->base;
+       new_conn_state.connector = &aconnector->base;
+       state.num_connector = connector_index + 1;
+       state.connectors = kunit_kzalloc(test,
+                                        sizeof(*state.connectors) * 
state.num_connector,
+                                        GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, state.connectors);
+       state.connectors[connector_index].ptr = &aconnector->base;
+       state.connectors[connector_index].old_state = &old_conn_state;
+       state.connectors[connector_index].new_state = &new_conn_state;
+
+       KUNIT_EXPECT_EQ(test, dm_dp_mst_atomic_check(&aconnector->base, 
&state), 0);
+}
+
+/**
+ * dm_mst_test_detect_unregistered - Test detect skips unregistered connector
+ * @test: KUnit test context
+ *
+ * Verify that dm_dp_mst_detect() returns disconnected for an unregistered
+ * connector before calling into the MST topology helper.
+ */
+static void dm_mst_test_detect_unregistered(struct kunit *test)
+{
+       struct amdgpu_dm_connector *aconnector;
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+
+       aconnector->base.registration_state = DRM_CONNECTOR_UNREGISTERED;
+
+       KUNIT_EXPECT_EQ(test,
+                       dm_dp_mst_detect(&aconnector->base, NULL, false),
+                       (int)connector_status_disconnected);
+}
+
+/**
+ * dm_mst_test_fp_guarded_public_stubs - Test FP-off public fallbacks
+ * @test: KUnit test context
+ *
+ * When CONFIG_DRM_AMD_DC_FP is disabled, the public DSC validation helper
+ * has no FP body and must return DC_OK without touching its arguments.
+ */
+static void dm_mst_test_fp_guarded_public_stubs(struct kunit *test)
+{
+       KUNIT_EXPECT_EQ(test, dm_dp_mst_is_port_support_mode(NULL, NULL),
+                       (enum dc_status)DC_OK);
+}
+
 static struct kunit_case dm_mst_types_test_cases[] = {
        /* needs_dsc_aux_workaround tests */
        KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_match),
@@ -480,11 +1034,17 @@ static struct kunit_case dm_mst_types_test_cases[] = {
        KUNIT_CASE(dm_mst_test_retrieve_downstream_present),
        /* retrieve_branch_specific_data tests */
        KUNIT_CASE(dm_mst_test_retrieve_branch_no_parent),
+       KUNIT_CASE(dm_mst_test_retrieve_branch_reads_oui),
        /* dm_dp_aux_transfer_result tests */
        KUNIT_CASE(dm_mst_test_aux_result_success),
        KUNIT_CASE(dm_mst_test_aux_result_eio),
        KUNIT_CASE(dm_mst_test_aux_result_ebusy),
        KUNIT_CASE(dm_mst_test_aux_result_timeout),
+       KUNIT_CASE(dm_mst_test_aux_transfer_native_read),
+       KUNIT_CASE(dm_mst_test_aux_transfer_native_write),
+       KUNIT_CASE(dm_mst_test_aux_transfer_partial_write),
+       KUNIT_CASE(dm_mst_test_aux_transfer_error_result),
+       KUNIT_CASE(dm_mst_test_aux_transfer_hpd_discon_quirk),
        /* dm_dp_aux_fill_payload_flags tests */
        KUNIT_CASE(dm_mst_test_fill_payload_flags_native_write),
        KUNIT_CASE(dm_mst_test_fill_payload_flags_native_read),
@@ -495,6 +1055,18 @@ static struct kunit_case dm_mst_types_test_cases[] = {
        /* dm_mst_select_esi_dpcd tests */
        KUNIT_CASE(dm_mst_test_select_esi_dpcd_legacy),
        KUNIT_CASE(dm_mst_test_select_esi_dpcd_esi),
+       /* dm_handle_mst_sideband_msg_ready_event tests */
+       KUNIT_CASE(dm_mst_test_sideband_msg_ready_no_ready_bits),
+       /* dm_mst_atomic_best_encoder tests */
+       KUNIT_CASE(dm_mst_test_atomic_best_encoder),
+       /* dm_dp_create_fake_mst_encoders tests */
+       KUNIT_CASE(dm_mst_test_create_fake_mst_encoders),
+       /* dm_dp_mst_atomic_check tests */
+       KUNIT_CASE(dm_mst_test_atomic_check_no_old_crtc),
+       /* dm_dp_mst_detect tests */
+       KUNIT_CASE(dm_mst_test_detect_unregistered),
+       /* CONFIG_DRM_AMD_DC_FP disabled public paths */
+       KUNIT_CASE(dm_mst_test_fp_guarded_public_stubs),
        {}
 };
 
-- 
2.53.0


Reply via email to