From: Alex Hung <[email protected]>

The following existing functions are also exported for the test module:

- needs_dsc_aux_workaround: detect branches needing the DSC AUX workaround
- dm_mst_get_pbn_divider: compute the PBN divider from link bandwidth
- amdgpu_dm_mst_reset_mst_connector_setting: reset per-connector MST state
- retrieve_downstream_port_device: read downstream port presence from DPCD
- retrieve_branch_specific_data: read branch OUI from the upstream device

Several self-contained pieces of logic are extracted from larger functions
into small testable helpers.

- dm_dp_aux_transfer_result: AUX return-code to errno mapping
- dm_dp_aux_fill_payload_flags: AUX request bit decode
- dm_mst_msg_ready_mask: MST sideband ESI mask selection
- dm_mst_select_esi_dpcd: DPCD ESI address/length selection

Assisted-by: Copilot:Claude-Opus-4.6

Reviewed-by: Bhawanpreet Lakha <[email protected]>
Signed-off-by: Alex Hung <[email protected]>
Signed-off-by: Chenyu Chen <[email protected]>
---
 .../display/amdgpu_dm/amdgpu_dm_mst_types.c   | 143 ++++---
 .../display/amdgpu_dm/amdgpu_dm_mst_types.h   |  12 +
 .../tests/amdgpu_dm_mst_types_test.c          | 385 ++++++++++++++++++
 3 files changed, 491 insertions(+), 49 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 09a6a1deb5db..83d0e1403e54 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
@@ -34,6 +34,7 @@
 #include "dm_services.h"
 #include "amdgpu.h"
 #include "amdgpu_dm.h"
+#include "dmub_cmd.h"
 #include "amdgpu_dm_mst_types.h"
 #include "amdgpu_dm_hdcp.h"
 
@@ -44,7 +45,6 @@
 #include "ddc_service_types.h"
 #include "dpcd_defs.h"
 
-#include "dmub_cmd.h"
 #if defined(CONFIG_DEBUG_FS)
 #include "amdgpu_dm_debugfs.h"
 #endif
@@ -53,6 +53,49 @@
 
 #define PEAK_FACTOR_X1000 1006
 
+/*
+ * Translate a failed AUX transaction's operation result into an errno-style
+ * return value. @result is returned unchanged for AUX_RET_SUCCESS.
+ */
+STATIC_IFN_KUNIT ssize_t dm_dp_aux_transfer_result(ssize_t result,
+                                                  enum aux_return_code_type 
operation_result)
+{
+       switch (operation_result) {
+       case AUX_RET_SUCCESS:
+               break;
+       case AUX_RET_ERROR_HPD_DISCON:
+       case AUX_RET_ERROR_UNKNOWN:
+       case AUX_RET_ERROR_INVALID_OPERATION:
+       case AUX_RET_ERROR_PROTOCOL_ERROR:
+               result = -EIO;
+               break;
+       case AUX_RET_ERROR_INVALID_REPLY:
+       case AUX_RET_ERROR_ENGINE_ACQUIRE:
+               result = -EBUSY;
+               break;
+       case AUX_RET_ERROR_TIMEOUT:
+               result = -ETIMEDOUT;
+               break;
+       }
+
+       return result;
+}
+EXPORT_IF_KUNIT(dm_dp_aux_transfer_result);
+
+/*
+ * Derive the AUX payload transaction flags from a DP AUX request field.
+ */
+STATIC_IFN_KUNIT void dm_dp_aux_fill_payload_flags(u8 request,
+                                                  struct aux_payload *payload)
+{
+       payload->i2c_over_aux = (request & DP_AUX_NATIVE_WRITE) == 0;
+       payload->write = (request & DP_AUX_I2C_READ) == 0;
+       payload->mot = (request & DP_AUX_I2C_MOT) != 0;
+       payload->write_status_update =
+                       (request & DP_AUX_I2C_WRITE_STATUS_UPDATE) != 0;
+}
+EXPORT_IF_KUNIT(dm_dp_aux_fill_payload_flags);
+
 /*
  * This function handles both native AUX and I2C-Over-AUX transactions.
  */
@@ -73,11 +116,7 @@ static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux,
        payload.data = msg->buffer;
        payload.length = msg->size;
        payload.reply = &msg->reply;
-       payload.i2c_over_aux = (msg->request & DP_AUX_NATIVE_WRITE) == 0;
-       payload.write = (msg->request & DP_AUX_I2C_READ) == 0;
-       payload.mot = (msg->request & DP_AUX_I2C_MOT) != 0;
-       payload.write_status_update =
-                       (msg->request & DP_AUX_I2C_WRITE_STATUS_UPDATE) != 0;
+       dm_dp_aux_fill_payload_flags(msg->request, &payload);
        payload.defer_delay = 0;
 
        if (payload.write) {
@@ -117,23 +156,7 @@ static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux,
        }
 
        if (result < 0) {
-               switch (operation_result) {
-               case AUX_RET_SUCCESS:
-                       break;
-               case AUX_RET_ERROR_HPD_DISCON:
-               case AUX_RET_ERROR_UNKNOWN:
-               case AUX_RET_ERROR_INVALID_OPERATION:
-               case AUX_RET_ERROR_PROTOCOL_ERROR:
-                       result = -EIO;
-                       break;
-               case AUX_RET_ERROR_INVALID_REPLY:
-               case AUX_RET_ERROR_ENGINE_ACQUIRE:
-                       result = -EBUSY;
-                       break;
-               case AUX_RET_ERROR_TIMEOUT:
-                       result = -ETIMEDOUT;
-                       break;
-               }
+               result = dm_dp_aux_transfer_result(result, operation_result);
 
                drm_dbg_dp(adev_to_drm(adev), "DP AUX transfer fail:%d\n", 
operation_result);
        }
@@ -184,7 +207,7 @@ amdgpu_dm_mst_connector_late_register(struct drm_connector 
*connector)
 }
 
 
-static inline void
+STATIC_IFN_KUNIT void
 amdgpu_dm_mst_reset_mst_connector_setting(struct amdgpu_dm_connector 
*aconnector)
 {
        aconnector->drm_edid = NULL;
@@ -193,6 +216,7 @@ amdgpu_dm_mst_reset_mst_connector_setting(struct 
amdgpu_dm_connector *aconnector
        aconnector->mst_local_bw = 0;
        aconnector->vc_full_pbn = 0;
 }
+EXPORT_IF_KUNIT(amdgpu_dm_mst_reset_mst_connector_setting);
 
 static void
 amdgpu_dm_mst_connector_early_unregister(struct drm_connector *connector)
@@ -313,7 +337,7 @@ static bool validate_dsc_caps_on_connector(struct 
amdgpu_dm_connector *aconnecto
 }
 #endif
 
-static bool retrieve_downstream_port_device(struct amdgpu_dm_connector 
*aconnector)
+STATIC_IFN_KUNIT bool retrieve_downstream_port_device(struct 
amdgpu_dm_connector *aconnector)
 {
        union dp_downstream_port_present ds_port_present;
 
@@ -331,8 +355,9 @@ static bool retrieve_downstream_port_device(struct 
amdgpu_dm_connector *aconnect
 
        return true;
 }
+EXPORT_IF_KUNIT(retrieve_downstream_port_device);
 
-static bool retrieve_branch_specific_data(struct amdgpu_dm_connector 
*aconnector)
+STATIC_IFN_KUNIT bool retrieve_branch_specific_data(struct amdgpu_dm_connector 
*aconnector)
 {
        struct drm_connector *connector = &aconnector->base;
        struct drm_dp_mst_port *port = aconnector->mst_output_port;
@@ -359,6 +384,7 @@ static bool retrieve_branch_specific_data(struct 
amdgpu_dm_connector *aconnector
 
        return true;
 }
+EXPORT_IF_KUNIT(retrieve_branch_specific_data);
 
 static int dm_dp_mst_get_modes(struct drm_connector *connector)
 {
@@ -708,6 +734,44 @@ dm_dp_add_mst_connector(struct drm_dp_mst_topology_mgr 
*mgr,
        return connector;
 }
 
+/*
+ * Select the ESI[1] mask used to filter the MST sideband ready bits for a
+ * given message-ready event type.
+ */
+STATIC_IFN_KUNIT u8 dm_mst_msg_ready_mask(enum mst_msg_ready_type msg_rdy_type)
+{
+       switch (msg_rdy_type) {
+       case DOWN_REP_MSG_RDY_EVENT:
+               /* Only handle DOWN_REP_MSG_RDY case*/
+               return DP_DOWN_REP_MSG_RDY;
+       case UP_REQ_MSG_RDY_EVENT:
+               /* Only handle UP_REQ_MSG_RDY case*/
+               return DP_UP_REQ_MSG_RDY;
+       default:
+               /* Handle both cases*/
+               return DP_DOWN_REP_MSG_RDY | DP_UP_REQ_MSG_RDY;
+       }
+}
+EXPORT_IF_KUNIT(dm_mst_msg_ready_mask);
+
+/*
+ * Select the DPCD ESI address and read length based on the DPCD revision.
+ */
+STATIC_IFN_KUNIT void dm_mst_select_esi_dpcd(u8 dpcd_rev, int *dpcd_addr,
+                                            u8 *dpcd_bytes_to_read)
+{
+       if (dpcd_rev < 0x12) {
+               *dpcd_bytes_to_read = DP_LANE0_1_STATUS - DP_SINK_COUNT;
+               /* DPCD 0x200 - 0x201 for downstream IRQ */
+               *dpcd_addr = DP_SINK_COUNT;
+       } else {
+               *dpcd_bytes_to_read = DP_PSR_ERROR_STATUS - DP_SINK_COUNT_ESI;
+               /* DPCD 0x2002 - 0x2005 for downstream IRQ */
+               *dpcd_addr = DP_SINK_COUNT_ESI;
+       }
+}
+EXPORT_IF_KUNIT(dm_mst_select_esi_dpcd);
+
 void dm_handle_mst_sideband_msg_ready_event(
        struct drm_dp_mst_topology_mgr *mgr,
        enum mst_msg_ready_type msg_rdy_type)
@@ -726,15 +790,8 @@ void dm_handle_mst_sideband_msg_ready_event(
 
        const struct dc_link_status *link_status = 
dc_link_get_status(aconnector->dc_link);
 
-       if (link_status->dpcd_caps->dpcd_rev.raw < 0x12) {
-               dpcd_bytes_to_read = DP_LANE0_1_STATUS - DP_SINK_COUNT;
-               /* DPCD 0x200 - 0x201 for downstream IRQ */
-               dpcd_addr = DP_SINK_COUNT;
-       } else {
-               dpcd_bytes_to_read = DP_PSR_ERROR_STATUS - DP_SINK_COUNT_ESI;
-               /* DPCD 0x2002 - 0x2005 for downstream IRQ */
-               dpcd_addr = DP_SINK_COUNT_ESI;
-       }
+       dm_mst_select_esi_dpcd(link_status->dpcd_caps->dpcd_rev.raw, &dpcd_addr,
+                              &dpcd_bytes_to_read);
 
        mutex_lock(&aconnector->handle_mst_msg_ready);
 
@@ -756,20 +813,7 @@ void dm_handle_mst_sideband_msg_ready_event(
 
                DRM_DEBUG_DRIVER("ESI %02x %02x %02x\n", esi[0], esi[1], 
esi[2]);
 
-               switch (msg_rdy_type) {
-               case DOWN_REP_MSG_RDY_EVENT:
-                       /* Only handle DOWN_REP_MSG_RDY case*/
-                       esi[1] &= DP_DOWN_REP_MSG_RDY;
-                       break;
-               case UP_REQ_MSG_RDY_EVENT:
-                       /* Only handle UP_REQ_MSG_RDY case*/
-                       esi[1] &= DP_UP_REQ_MSG_RDY;
-                       break;
-               default:
-                       /* Handle both cases*/
-                       esi[1] &= (DP_DOWN_REP_MSG_RDY | DP_UP_REQ_MSG_RDY);
-                       break;
-               }
+               esi[1] &= dm_mst_msg_ready_mask(msg_rdy_type);
 
                if (!esi[1])
                        break;
@@ -866,6 +910,7 @@ uint32_t dm_mst_get_pbn_divider(struct dc_link *link)
 
        return dfixed_const(pbn_div_x100) / 100;
 }
+EXPORT_IF_KUNIT(dm_mst_get_pbn_divider);
 
 struct dsc_mst_fairness_params {
        struct dc_crtc_timing *timing;
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 5a7065e53645..5e5190671923 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
@@ -60,6 +60,7 @@ enum mst_msg_ready_type {
 struct amdgpu_device;
 struct amdgpu_display_manager;
 struct amdgpu_dm_connector;
+struct aux_payload;
 struct dc_state;
 struct dc_stream_state;
 struct dm_atomic_state;
@@ -100,4 +101,15 @@ enum dc_status dm_dp_mst_is_port_support_mode(
        struct amdgpu_dm_connector *aconnector,
        struct dc_stream_state *stream);
 
+#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+void amdgpu_dm_mst_reset_mst_connector_setting(struct amdgpu_dm_connector 
*aconnector);
+bool retrieve_downstream_port_device(struct amdgpu_dm_connector *aconnector);
+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);
+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);
+#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 e21386819ea1..e3b171992be1 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,10 +7,44 @@
 
 #include <kunit/test.h>
 
+#include <drm/display/drm_dp.h>
+#include <drm/display/drm_dp_helper.h>
+#include <drm/display/drm_dp_mst_helper.h>
+
 #include "dc.h"
 #include "dpcd_defs.h"
+#include "dmub_cmd.h"
+#include "amdgpu.h"
+#include "amdgpu_mode.h"
+#include "amdgpu_dm.h"
 #include "amdgpu_dm_mst_types.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 ssize_t dm_mst_test_aux_transfer(struct drm_dp_aux *aux,
+                                       struct drm_dp_aux_msg *msg)
+{
+       size_t i;
+
+       switch (msg->request & ~DP_AUX_I2C_MOT) {
+       case DP_AUX_NATIVE_READ:
+               for (i = 0; i < msg->size; i++)
+                       ((u8 *)msg->buffer)[i] =
+                               dm_mst_test_dpcd[(msg->address + i) & 0xf];
+               msg->reply = DP_AUX_NATIVE_REPLY_ACK;
+               return msg->size;
+       case DP_AUX_NATIVE_WRITE:
+               msg->reply = DP_AUX_NATIVE_REPLY_ACK;
+               return msg->size;
+       default:
+               return -EINVAL;
+       }
+}
+
 /* Tests for needs_dsc_aux_workaround */
 
 /**
@@ -103,6 +137,332 @@ static void 
dm_mst_test_needs_dsc_aux_workaround_low_sink_count(struct kunit *te
        KUNIT_EXPECT_FALSE(test, needs_dsc_aux_workaround(&link));
 }
 
+/**
+ * dm_mst_test_needs_dsc_aux_workaround_zero_sink_count - Test workaround 
skipped for zero sinks
+ * @test: KUnit test context
+ *
+ * Verify that needs_dsc_aux_workaround() returns false when the sink
+ * count is zero, even if device ID and DPCD rev match.
+ */
+static void dm_mst_test_needs_dsc_aux_workaround_zero_sink_count(struct kunit 
*test)
+{
+       struct dc_link link = {0};
+
+       link.dpcd_caps.branch_dev_id = DP_BRANCH_DEVICE_ID_90CC24;
+       link.dpcd_caps.dpcd_rev.raw = DPCD_REV_14;
+       link.dpcd_caps.sink_count.bits.SINK_COUNT = 0;
+
+       KUNIT_EXPECT_FALSE(test, needs_dsc_aux_workaround(&link));
+}
+
+/* Tests for dm_mst_get_pbn_divider */
+
+/**
+ * dm_mst_test_pbn_divider_null_link - Test pbn_divider with NULL link
+ * @test: KUnit test context
+ *
+ * Verify that dm_mst_get_pbn_divider() returns 0 when passed a NULL
+ * link pointer without crashing.
+ */
+static void dm_mst_test_pbn_divider_null_link(struct kunit *test)
+{
+       KUNIT_EXPECT_EQ(test, dm_mst_get_pbn_divider(NULL), 0U);
+}
+
+/* Tests for amdgpu_dm_mst_reset_mst_connector_setting */
+
+/**
+ * dm_mst_test_reset_connector_setting - Test MST connector setting reset
+ * @test: KUnit test context
+ *
+ * Verify that amdgpu_dm_mst_reset_mst_connector_setting() clears the cached
+ * EDID, DSC AUX, passthrough AUX, local bandwidth, and VC PBN state.
+ */
+static void dm_mst_test_reset_connector_setting(struct kunit *test)
+{
+       struct amdgpu_dm_connector *aconnector;
+       struct drm_dp_mst_port *port;
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       port = kunit_kzalloc(test, sizeof(*port), GFP_KERNEL);
+
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+       KUNIT_ASSERT_NOT_NULL(test, port);
+
+       aconnector->drm_edid = (const struct drm_edid *)test;
+       aconnector->dsc_aux = (struct drm_dp_aux *)test;
+       aconnector->mst_output_port = port;
+       aconnector->mst_output_port->passthrough_aux = (struct drm_dp_aux 
*)test;
+       aconnector->mst_local_bw = 12345;
+       aconnector->vc_full_pbn = 678;
+
+       amdgpu_dm_mst_reset_mst_connector_setting(aconnector);
+
+       KUNIT_EXPECT_TRUE(test, aconnector->drm_edid == NULL);
+       KUNIT_EXPECT_TRUE(test, aconnector->dsc_aux == NULL);
+       KUNIT_EXPECT_TRUE(test, aconnector->mst_output_port->passthrough_aux == 
NULL);
+       KUNIT_EXPECT_EQ(test, aconnector->mst_local_bw, 0U);
+       KUNIT_EXPECT_EQ(test, aconnector->vc_full_pbn, 0U);
+}
+
+/* Tests for retrieve_downstream_port_device */
+
+/**
+ * dm_mst_test_retrieve_downstream_no_aux - Test retrieval bails out without 
AUX
+ * @test: KUnit test context
+ *
+ * Verify that retrieve_downstream_port_device() returns false when the
+ * connector has no DSC AUX channel and therefore cannot read DPCD.
+ */
+static void dm_mst_test_retrieve_downstream_no_aux(struct kunit *test)
+{
+       struct amdgpu_dm_connector *aconnector;
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+
+       aconnector->dsc_aux = NULL;
+
+       KUNIT_EXPECT_FALSE(test, retrieve_downstream_port_device(aconnector));
+}
+
+/**
+ * dm_mst_test_retrieve_downstream_present - Test retrieval parses DPCD 0x05
+ * @test: KUnit test context
+ *
+ * Verify that retrieve_downstream_port_device() reads 
DP_DOWNSTREAMPORT_PRESENT
+ * over a mock AUX channel and caches the parsed downstream port fields.
+ */
+static void dm_mst_test_retrieve_downstream_present(struct kunit *test)
+{
+       struct amdgpu_dm_connector *aconnector;
+       struct drm_dp_aux *aux;
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       aux = kunit_kzalloc(test, sizeof(*aux), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+       KUNIT_ASSERT_NOT_NULL(test, aux);
+
+       memset(dm_mst_test_dpcd, 0, sizeof(dm_mst_test_dpcd));
+       /* PORT_PRESENT = 1, PORT_TYPE = 2 (0b101) */
+       dm_mst_test_dpcd[DP_DOWNSTREAMPORT_PRESENT] = 0x05;
+
+       aux->name = "dm_mst_test_aux";
+       aux->transfer = dm_mst_test_aux_transfer;
+       drm_dp_aux_init(aux);
+       drm_dp_dpcd_set_probe(aux, false);
+       aconnector->dsc_aux = aux;
+
+       KUNIT_EXPECT_TRUE(test, retrieve_downstream_port_device(aconnector));
+       KUNIT_EXPECT_EQ(test,
+                       
(int)aconnector->mst_downstream_port_present.fields.PORT_PRESENT, 1);
+       KUNIT_EXPECT_EQ(test,
+                       
(int)aconnector->mst_downstream_port_present.fields.PORT_TYPE, 2);
+}
+
+/* Tests for retrieve_branch_specific_data */
+
+/**
+ * dm_mst_test_retrieve_branch_no_parent - Test branch lookup needs a parent 
port
+ * @test: KUnit test context
+ *
+ * Verify that retrieve_branch_specific_data() returns false when the MST
+ * output port has no parent branch device to query.
+ */
+static void dm_mst_test_retrieve_branch_no_parent(struct kunit *test)
+{
+       struct amdgpu_dm_connector *aconnector;
+       struct drm_dp_mst_port *port;
+
+       aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+       port = kunit_kzalloc(test, sizeof(*port), GFP_KERNEL);
+       KUNIT_ASSERT_NOT_NULL(test, aconnector);
+       KUNIT_ASSERT_NOT_NULL(test, port);
+
+       port->parent = NULL;
+       aconnector->mst_output_port = port;
+
+       KUNIT_EXPECT_FALSE(test, retrieve_branch_specific_data(aconnector));
+}
+
+/**
+ * dm_mst_test_aux_result_success - AUX_RET_SUCCESS preserves the input result.
+ * @test: KUnit test context.
+ *
+ * On success the original (negative) transfer result must be returned 
unchanged.
+ */
+static void dm_mst_test_aux_result_success(struct kunit *test)
+{
+       KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-5, AUX_RET_SUCCESS), 
(ssize_t)-5);
+       KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(3, AUX_RET_SUCCESS), 
(ssize_t)3);
+}
+
+/**
+ * dm_mst_test_aux_result_eio - HPD/unknown/protocol errors map to -EIO.
+ * @test: KUnit test context.
+ *
+ * AUX_RET_ERROR_HPD_DISCON, AUX_RET_ERROR_UNKNOWN,
+ * AUX_RET_ERROR_INVALID_OPERATION and AUX_RET_ERROR_PROTOCOL_ERROR all map to 
-EIO.
+ */
+static void dm_mst_test_aux_result_eio(struct kunit *test)
+{
+       KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, 
AUX_RET_ERROR_HPD_DISCON),
+                       (ssize_t)-EIO);
+       KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, 
AUX_RET_ERROR_UNKNOWN),
+                       (ssize_t)-EIO);
+       KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, 
AUX_RET_ERROR_INVALID_OPERATION),
+                       (ssize_t)-EIO);
+       KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, 
AUX_RET_ERROR_PROTOCOL_ERROR),
+                       (ssize_t)-EIO);
+}
+
+/**
+ * dm_mst_test_aux_result_ebusy - invalid reply / engine acquire map to -EBUSY.
+ * @test: KUnit test context.
+ *
+ * AUX_RET_ERROR_INVALID_REPLY and AUX_RET_ERROR_ENGINE_ACQUIRE map to -EBUSY.
+ */
+static void dm_mst_test_aux_result_ebusy(struct kunit *test)
+{
+       KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, 
AUX_RET_ERROR_INVALID_REPLY),
+                       (ssize_t)-EBUSY);
+       KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, 
AUX_RET_ERROR_ENGINE_ACQUIRE),
+                       (ssize_t)-EBUSY);
+}
+
+/**
+ * dm_mst_test_aux_result_timeout - AUX_RET_ERROR_TIMEOUT maps to -ETIMEDOUT.
+ * @test: KUnit test context.
+ */
+static void dm_mst_test_aux_result_timeout(struct kunit *test)
+{
+       KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, 
AUX_RET_ERROR_TIMEOUT),
+                       (ssize_t)-ETIMEDOUT);
+}
+
+/**
+ * dm_mst_test_fill_payload_flags_native_write - native write request decode.
+ * @test: KUnit test context.
+ *
+ * DP_AUX_NATIVE_WRITE clears i2c_over_aux and sets write; no I2C bits set.
+ */
+static void dm_mst_test_fill_payload_flags_native_write(struct kunit *test)
+{
+       struct aux_payload payload = { 0 };
+
+       dm_dp_aux_fill_payload_flags(DP_AUX_NATIVE_WRITE, &payload);
+
+       KUNIT_EXPECT_FALSE(test, payload.i2c_over_aux);
+       KUNIT_EXPECT_TRUE(test, payload.write);
+       KUNIT_EXPECT_FALSE(test, payload.mot);
+       KUNIT_EXPECT_FALSE(test, payload.write_status_update);
+}
+
+/**
+ * dm_mst_test_fill_payload_flags_native_read - native read request decode.
+ * @test: KUnit test context.
+ *
+ * DP_AUX_NATIVE_READ keeps i2c_over_aux clear; the I2C_READ bit clears write.
+ */
+static void dm_mst_test_fill_payload_flags_native_read(struct kunit *test)
+{
+       struct aux_payload payload = { 0 };
+
+       dm_dp_aux_fill_payload_flags(DP_AUX_NATIVE_READ, &payload);
+
+       KUNIT_EXPECT_FALSE(test, payload.i2c_over_aux);
+       KUNIT_EXPECT_FALSE(test, payload.write);
+       KUNIT_EXPECT_FALSE(test, payload.mot);
+}
+
+/**
+ * dm_mst_test_fill_payload_flags_i2c_read_mot - I2C read with MOT request 
decode.
+ * @test: KUnit test context.
+ *
+ * DP_AUX_I2C_READ sets i2c_over_aux and clears write; DP_AUX_I2C_MOT sets mot.
+ */
+static void dm_mst_test_fill_payload_flags_i2c_read_mot(struct kunit *test)
+{
+       struct aux_payload payload = { 0 };
+
+       dm_dp_aux_fill_payload_flags(DP_AUX_I2C_READ | DP_AUX_I2C_MOT, 
&payload);
+
+       KUNIT_EXPECT_TRUE(test, payload.i2c_over_aux);
+       KUNIT_EXPECT_FALSE(test, payload.write);
+       KUNIT_EXPECT_TRUE(test, payload.mot);
+}
+
+/**
+ * dm_mst_test_fill_payload_flags_write_status - write status update decode.
+ * @test: KUnit test context.
+ *
+ * DP_AUX_I2C_WRITE_STATUS_UPDATE sets write_status_update.
+ */
+static void dm_mst_test_fill_payload_flags_write_status(struct kunit *test)
+{
+       struct aux_payload payload = { 0 };
+
+       dm_dp_aux_fill_payload_flags(DP_AUX_I2C_WRITE | 
DP_AUX_I2C_WRITE_STATUS_UPDATE,
+                                    &payload);
+
+       KUNIT_EXPECT_TRUE(test, payload.i2c_over_aux);
+       KUNIT_EXPECT_TRUE(test, payload.write_status_update);
+}
+
+/**
+ * dm_mst_test_msg_ready_mask - ESI mask selection per message-ready type.
+ * @test: KUnit test context.
+ *
+ * DOWN_REP and UP_REQ each select their single bit; other types select both.
+ */
+static void dm_mst_test_msg_ready_mask(struct kunit *test)
+{
+       KUNIT_EXPECT_EQ(test, dm_mst_msg_ready_mask(DOWN_REP_MSG_RDY_EVENT),
+                       (u8)DP_DOWN_REP_MSG_RDY);
+       KUNIT_EXPECT_EQ(test, dm_mst_msg_ready_mask(UP_REQ_MSG_RDY_EVENT),
+                       (u8)DP_UP_REQ_MSG_RDY);
+       KUNIT_EXPECT_EQ(test, dm_mst_msg_ready_mask(DOWN_OR_UP_MSG_RDY_EVENT),
+                       (u8)(DP_DOWN_REP_MSG_RDY | DP_UP_REQ_MSG_RDY));
+       KUNIT_EXPECT_EQ(test, dm_mst_msg_ready_mask(NONE_MSG_RDY_EVENT),
+                       (u8)(DP_DOWN_REP_MSG_RDY | DP_UP_REQ_MSG_RDY));
+}
+
+/**
+ * dm_mst_test_select_esi_dpcd_legacy - pre-1.2 DPCD ESI address/length.
+ * @test: KUnit test context.
+ *
+ * For DPCD rev < 0x12 the legacy DP_SINK_COUNT address/length pair is 
selected.
+ */
+static void dm_mst_test_select_esi_dpcd_legacy(struct kunit *test)
+{
+       int dpcd_addr = -1;
+       u8 dpcd_bytes_to_read = 0;
+
+       dm_mst_select_esi_dpcd(0x11, &dpcd_addr, &dpcd_bytes_to_read);
+
+       KUNIT_EXPECT_EQ(test, dpcd_addr, DP_SINK_COUNT);
+       KUNIT_EXPECT_EQ(test, (int)dpcd_bytes_to_read,
+                       (int)(DP_LANE0_1_STATUS - DP_SINK_COUNT));
+}
+
+/**
+ * dm_mst_test_select_esi_dpcd_esi - 1.2+ DPCD ESI address/length.
+ * @test: KUnit test context.
+ *
+ * For DPCD rev >= 0x12 the ESI DP_SINK_COUNT_ESI address/length pair is 
selected.
+ */
+static void dm_mst_test_select_esi_dpcd_esi(struct kunit *test)
+{
+       int dpcd_addr = -1;
+       u8 dpcd_bytes_to_read = 0;
+
+       dm_mst_select_esi_dpcd(0x14, &dpcd_addr, &dpcd_bytes_to_read);
+
+       KUNIT_EXPECT_EQ(test, dpcd_addr, DP_SINK_COUNT_ESI);
+       KUNIT_EXPECT_EQ(test, (int)dpcd_bytes_to_read,
+                       (int)(DP_PSR_ERROR_STATUS - DP_SINK_COUNT_ESI));
+}
+
 static struct kunit_case dm_mst_types_test_cases[] = {
        /* needs_dsc_aux_workaround tests */
        KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_match),
@@ -110,6 +470,31 @@ static struct kunit_case dm_mst_types_test_cases[] = {
        KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_wrong_dev_id),
        KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_wrong_rev),
        KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_low_sink_count),
+       KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_zero_sink_count),
+       /* dm_mst_get_pbn_divider tests */
+       KUNIT_CASE(dm_mst_test_pbn_divider_null_link),
+       /* amdgpu_dm_mst_reset_mst_connector_setting tests */
+       KUNIT_CASE(dm_mst_test_reset_connector_setting),
+       /* retrieve_downstream_port_device tests */
+       KUNIT_CASE(dm_mst_test_retrieve_downstream_no_aux),
+       KUNIT_CASE(dm_mst_test_retrieve_downstream_present),
+       /* retrieve_branch_specific_data tests */
+       KUNIT_CASE(dm_mst_test_retrieve_branch_no_parent),
+       /* 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),
+       /* 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),
+       KUNIT_CASE(dm_mst_test_fill_payload_flags_i2c_read_mot),
+       KUNIT_CASE(dm_mst_test_fill_payload_flags_write_status),
+       /* dm_mst_msg_ready_mask tests */
+       KUNIT_CASE(dm_mst_test_msg_ready_mask),
+       /* dm_mst_select_esi_dpcd tests */
+       KUNIT_CASE(dm_mst_test_select_esi_dpcd_legacy),
+       KUNIT_CASE(dm_mst_test_select_esi_dpcd_esi),
        {}
 };
 
-- 
2.43.0

Reply via email to