drm/amd/display: Add KUnit tests for mst_types

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 <bhawanpreet.lakha@amd.com>
Signed-off-by: Alex Hung <alex.hung@amd.com>
Signed-off-by: George Zhang <george.zhang@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Alex Hung
2026-06-17 18:11:42 -06:00
committed by Alex Deucher
parent b292f97d30
commit 04bed7922f
3 changed files with 593 additions and 7 deletions

View File

@@ -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_commit *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_commit *state)
STATIC_IFN_KUNIT int dm_dp_mst_atomic_check(struct drm_connector *connector,
struct drm_atomic_commit *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);

View File

@@ -64,7 +64,7 @@ struct aux_payload;
struct dc_state;
struct dc_stream_state;
struct dm_atomic_state;
struct drm_atomic_state;
struct drm_atomic_commit;
struct drm_dp_mst_topology_mgr;
uint32_t dm_mst_get_pbn_divider(struct dc_link *link);
@@ -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_commit *state);
int dm_dp_mst_atomic_check(struct drm_connector *connector,
struct drm_atomic_commit *state);
int dm_dp_mst_detect(struct drm_connector *connector,
struct drm_modeset_acquire_ctx *ctx, bool force);
#endif
#endif

View File

@@ -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_commit 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_commit 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),
{}
};