mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-23 18:56:40 -04:00
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:
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user