summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Hung <alex.hung@amd.com>2026-05-29 17:16:58 -0600
committerAlex Deucher <alexander.deucher@amd.com>2026-06-17 16:16:52 -0400
commit3576a045cd200688cad25a7ad321bb1708f6bb6f (patch)
tree26511ad064c8e46010c274c6487cea0809eb0bfd
parent652021e4be963b5ec1c86ba844fd40d0e01decc9 (diff)
drm/amd/display: Add more KUnit tests for amdgpu_dm_mst_types
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 <bhawanpreet.lakha@amd.com> Signed-off-by: Alex Hung <alex.hung@amd.com> Signed-off-by: Chenyu Chen <chen-yu.chen@amd.com> Tested-by: Daniel Wheeler <daniel.wheeler@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c143
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h12
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c385
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 9e1916f8f99b..b6bfe56eeb68 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
@@ -54,6 +54,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.
*/
static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux,
@@ -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 208629ca3721..2aefab5264d0 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),
{}
};