mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 22:14:03 +02:00
drm/amd/display: Test HPD interrupt handling
Add KUnit coverage for the HPD interrupt-handling helpers: the HPD-RX offload worker, the HDMI HPD debounce worker, handle_hpd_irq_helper(), handle_hpd_irq(), schedule_hpd_rx_offload_work() and handle_hpd_rx_irq(). Expose these statics for KUnit and add the stub link-service callbacks, sink helpers and fixtures the tests rely on. Assisted-by: Copilot:Claude-Opus-4.8 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:
parent
747e1b0dc7
commit
4c28526e9a
|
|
@ -1075,7 +1075,7 @@ static void force_connector_state(
|
|||
mutex_unlock(&aconnector->hpd_lock);
|
||||
}
|
||||
|
||||
static void dm_handle_hpd_rx_offload_work(struct work_struct *work)
|
||||
STATIC_IFN_KUNIT void dm_handle_hpd_rx_offload_work(struct work_struct *work)
|
||||
{
|
||||
struct hpd_rx_irq_offload_work *offload_work;
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
|
|
@ -1168,6 +1168,7 @@ static void dm_handle_hpd_rx_offload_work(struct work_struct *work)
|
|||
kfree(offload_work);
|
||||
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_handle_hpd_rx_offload_work);
|
||||
|
||||
struct hpd_rx_irq_offload_work_queue *amdgpu_dm_hpd_rx_irq_create_workqueue(struct amdgpu_device *adev)
|
||||
{
|
||||
|
|
@ -1309,8 +1310,9 @@ void amdgpu_dm_hdmi_hpd_debounce_work(struct work_struct *work)
|
|||
dc_allow_idle_optimizations(dc, true);
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_hdmi_hpd_debounce_work);
|
||||
|
||||
static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector,
|
||||
STATIC_IFN_KUNIT void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector,
|
||||
enum dc_detect_reason reason)
|
||||
{
|
||||
struct drm_connector *connector = &aconnector->base;
|
||||
|
|
@ -1408,16 +1410,18 @@ static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector,
|
|||
}
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(handle_hpd_irq_helper);
|
||||
|
||||
static void handle_hpd_irq(void *param)
|
||||
STATIC_IFN_KUNIT void handle_hpd_irq(void *param)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector = (struct amdgpu_dm_connector *)param;
|
||||
|
||||
handle_hpd_irq_helper(aconnector, DETECT_REASON_HPD);
|
||||
|
||||
}
|
||||
EXPORT_IF_KUNIT(handle_hpd_irq);
|
||||
|
||||
static void schedule_hpd_rx_offload_work(struct amdgpu_device *adev, struct hpd_rx_irq_offload_work_queue *offload_wq,
|
||||
STATIC_IFN_KUNIT void schedule_hpd_rx_offload_work(struct amdgpu_device *adev, struct hpd_rx_irq_offload_work_queue *offload_wq,
|
||||
union hpd_irq_data hpd_irq_data)
|
||||
{
|
||||
struct hpd_rx_irq_offload_work *offload_work = kzalloc_obj(*offload_work);
|
||||
|
|
@ -1435,8 +1439,9 @@ static void schedule_hpd_rx_offload_work(struct amdgpu_device *adev, struct hpd_
|
|||
queue_work(offload_wq->wq, &offload_work->work);
|
||||
drm_dbg_kms(adev_to_drm(adev), "queue work to handle hpd_rx offload work");
|
||||
}
|
||||
EXPORT_IF_KUNIT(schedule_hpd_rx_offload_work);
|
||||
|
||||
static void handle_hpd_rx_irq(void *param)
|
||||
STATIC_IFN_KUNIT void handle_hpd_rx_irq(void *param)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector = (struct amdgpu_dm_connector *)param;
|
||||
struct drm_connector *connector = &aconnector->base;
|
||||
|
|
@ -1569,6 +1574,7 @@ static void handle_hpd_rx_irq(void *param)
|
|||
|
||||
mutex_unlock(&aconnector->hpd_lock);
|
||||
}
|
||||
EXPORT_IF_KUNIT(handle_hpd_rx_irq);
|
||||
|
||||
/**
|
||||
* dmub_hpd_callback - DMUB HPD interrupt processing callback.
|
||||
|
|
|
|||
|
|
@ -161,6 +161,14 @@ void amdgpu_dm_irq_schedule_work(struct amdgpu_device *adev,
|
|||
enum dc_irq_source irq_source);
|
||||
void amdgpu_dm_irq_immediate_work(struct amdgpu_device *adev,
|
||||
enum dc_irq_source irq_source);
|
||||
void dm_handle_hpd_rx_offload_work(struct work_struct *work);
|
||||
void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector,
|
||||
enum dc_detect_reason reason);
|
||||
void handle_hpd_irq(void *param);
|
||||
void schedule_hpd_rx_offload_work(struct amdgpu_device *adev,
|
||||
struct hpd_rx_irq_offload_work_queue *offload_wq,
|
||||
union hpd_irq_data hpd_irq_data);
|
||||
void handle_hpd_rx_irq(void *param);
|
||||
#endif
|
||||
|
||||
#endif /* __AMDGPU_DM_IRQ_H__ */
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include "link_service.h"
|
||||
#include "dmub/dmub_srv.h"
|
||||
#include "dal_asic_id.h"
|
||||
#include "../../amdgpu/amdgpu_reset.h"
|
||||
|
||||
static void dm_test_irq_handler(void *arg)
|
||||
{
|
||||
|
|
@ -41,6 +42,148 @@ static void dm_test_irq_handler_count(void *arg)
|
|||
(*count)++;
|
||||
}
|
||||
|
||||
static bool dm_test_detect_connection_none(struct dc_link *link,
|
||||
enum dc_connection_type *type)
|
||||
{
|
||||
*type = dc_connection_none;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dm_test_detect_link_false(struct dc_link *link,
|
||||
enum dc_detect_reason reason)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool dm_test_detect_connection_single(struct dc_link *link,
|
||||
enum dc_connection_type *type)
|
||||
{
|
||||
*type = dc_connection_single;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Recording stubs for the dm_handle_hpd_rx_offload_work() DP-IRQ branches. */
|
||||
static int dm_test_automated_test_count;
|
||||
static int dm_test_handle_link_loss_count;
|
||||
|
||||
static void dm_test_dp_handle_automated_test(struct dc_link *link)
|
||||
{
|
||||
dm_test_automated_test_count++;
|
||||
}
|
||||
|
||||
static void dm_test_dp_handle_link_loss(struct dc_link *link)
|
||||
{
|
||||
dm_test_handle_link_loss_count++;
|
||||
}
|
||||
|
||||
static bool dm_test_dp_parse_link_loss_true(struct dc_link *link,
|
||||
union hpd_irq_data *hpd_irq_dpcd_data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dm_test_dp_should_allow_hpd_rx_irq_true(const struct dc_link *link)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static enum dc_status dm_test_dp_read_hpd_rx_irq_data_ok(struct dc_link *link,
|
||||
union hpd_irq_data *irq_data)
|
||||
{
|
||||
return DC_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a refcounted dc_sink for tests without pulling in the DC-core
|
||||
* dc_sink_create()/dc_sink_release() symbols. Production code under test still
|
||||
* uses dc_sink_retain()/dc_sink_release() (resolved inside the amdgpu module).
|
||||
* Use kzalloc (not kunit_kzalloc) so the final kref_put frees it exactly once.
|
||||
*/
|
||||
static struct dc_sink *dm_test_sink_create(struct dc_link *link)
|
||||
{
|
||||
struct dc_sink *sink = kzalloc(sizeof(*sink), GFP_KERNEL);
|
||||
|
||||
if (!sink)
|
||||
return NULL;
|
||||
|
||||
sink->link = link;
|
||||
sink->ctx = link->ctx;
|
||||
kref_init(&sink->refcount);
|
||||
|
||||
return sink;
|
||||
}
|
||||
|
||||
static void dm_test_sink_free(struct kref *kref)
|
||||
{
|
||||
struct dc_sink *sink = container_of(kref, struct dc_sink, refcount);
|
||||
|
||||
kfree(sink->dc_container_id);
|
||||
kfree(sink);
|
||||
}
|
||||
|
||||
static void dm_test_sink_release(struct dc_sink *sink)
|
||||
{
|
||||
kref_put(&sink->refcount, dm_test_sink_free);
|
||||
}
|
||||
|
||||
static bool dm_test_handle_hpd_rx_no_work(struct dc_link *link,
|
||||
union hpd_irq_data *hpd_irq_data,
|
||||
bool *link_loss,
|
||||
bool defer_handling,
|
||||
bool *has_left_work)
|
||||
{
|
||||
*link_loss = false;
|
||||
*has_left_work = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool dm_test_handle_hpd_rx_automated(struct dc_link *link,
|
||||
union hpd_irq_data *hpd_irq_data,
|
||||
bool *link_loss,
|
||||
bool defer_handling,
|
||||
bool *has_left_work)
|
||||
{
|
||||
*link_loss = false;
|
||||
*has_left_work = true;
|
||||
hpd_irq_data->bytes.device_service_irq.bits.AUTOMATED_TEST = 1;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool dm_test_handle_hpd_rx_msg_rdy(struct dc_link *link,
|
||||
union hpd_irq_data *hpd_irq_data,
|
||||
bool *link_loss,
|
||||
bool defer_handling,
|
||||
bool *has_left_work)
|
||||
{
|
||||
*link_loss = false;
|
||||
*has_left_work = true;
|
||||
hpd_irq_data->bytes.device_service_irq.bits.UP_REQ_MSG_RDY = 1;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool dm_test_handle_hpd_rx_link_loss(struct dc_link *link,
|
||||
union hpd_irq_data *hpd_irq_data,
|
||||
bool *link_loss,
|
||||
bool defer_handling,
|
||||
bool *has_left_work)
|
||||
{
|
||||
*link_loss = true;
|
||||
*has_left_work = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool dm_test_allow_hpd_rx_irq_true(const struct dc_link *link)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static struct dc *dm_test_alloc_dc_with_ctx(struct kunit *test)
|
||||
{
|
||||
struct dc_context *ctx;
|
||||
|
|
@ -1902,6 +2045,773 @@ static void dm_test_hpd_init_fini_irq_ref(struct kunit *test)
|
|||
amdgpu_dm_hpd_fini(adev);
|
||||
}
|
||||
|
||||
/* Tests for dm_handle_hpd_rx_offload_work() */
|
||||
|
||||
/**
|
||||
* dm_test_hpd_rx_offload_work_no_connector - Test missing connector early exit
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_hpd_rx_offload_work_no_connector(struct kunit *test)
|
||||
{
|
||||
struct hpd_rx_irq_offload_work_queue *offload_wq;
|
||||
struct hpd_rx_irq_offload_work *offload_work;
|
||||
struct amdgpu_device *adev;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
|
||||
offload_wq = kunit_kzalloc(test, sizeof(*offload_wq), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, offload_wq);
|
||||
|
||||
offload_work = kzalloc(sizeof(*offload_work), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, offload_work);
|
||||
offload_work->offload_wq = offload_wq;
|
||||
offload_work->adev = adev;
|
||||
INIT_WORK(&offload_work->work, dm_handle_hpd_rx_offload_work);
|
||||
|
||||
dm_handle_hpd_rx_offload_work(&offload_work->work);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_hpd_rx_offload_work_no_connection - Test no connection early exit
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_hpd_rx_offload_work_no_connection(struct kunit *test)
|
||||
{
|
||||
struct hpd_rx_irq_offload_work_queue *offload_wq;
|
||||
struct hpd_rx_irq_offload_work *offload_work;
|
||||
struct amdgpu_dm_connector *aconn;
|
||||
struct link_service *link_srv;
|
||||
struct amdgpu_device *adev;
|
||||
struct dc_link *link;
|
||||
struct dc *dc;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
mutex_init(&adev->dm.dc_lock);
|
||||
|
||||
offload_wq = kunit_kzalloc(test, sizeof(*offload_wq), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, offload_wq);
|
||||
|
||||
aconn = dm_kunit_alloc_connector(test, adev, NULL);
|
||||
mutex_init(&aconn->hpd_lock);
|
||||
offload_wq->aconnector = aconn;
|
||||
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc);
|
||||
|
||||
link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link_srv);
|
||||
link_srv->detect_connection_type = dm_test_detect_connection_none;
|
||||
dc->link_srv = link_srv;
|
||||
|
||||
link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
|
||||
link->dc = dc;
|
||||
aconn->dc_link = link;
|
||||
|
||||
offload_work = kzalloc(sizeof(*offload_work), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, offload_work);
|
||||
offload_work->offload_wq = offload_wq;
|
||||
offload_work->adev = adev;
|
||||
INIT_WORK(&offload_work->work, dm_handle_hpd_rx_offload_work);
|
||||
|
||||
dm_handle_hpd_rx_offload_work(&offload_work->work);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_hpd_rx_offload_work_automated_test - Test AUTOMATED_TEST branch
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* With a present connection and the AUTOMATED_TEST service-IRQ bit set, the
|
||||
* worker runs dc_link_dp_handle_automated_test() (stubbed via link_srv) and
|
||||
* writes the test response with core_link_write_dpcd(). timing_changed is left
|
||||
* false so force_connector_state() (which needs a registered DRM device) is
|
||||
* skipped, and aux_access_disabled makes the DPCD write a safe no-op.
|
||||
*/
|
||||
static void dm_test_hpd_rx_offload_work_automated_test(struct kunit *test)
|
||||
{
|
||||
struct hpd_rx_irq_offload_work_queue *offload_wq;
|
||||
struct hpd_rx_irq_offload_work *offload_work;
|
||||
struct amdgpu_dm_connector *aconn;
|
||||
struct link_service *link_srv;
|
||||
struct amdgpu_device *adev;
|
||||
struct dc_context *ctx;
|
||||
struct dc_link *link;
|
||||
struct dc *dc;
|
||||
|
||||
dm_test_automated_test_count = 0;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
mutex_init(&adev->dm.dc_lock);
|
||||
adev->reset_domain = kunit_kzalloc(test, sizeof(*adev->reset_domain),
|
||||
GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev->reset_domain);
|
||||
|
||||
offload_wq = kunit_kzalloc(test, sizeof(*offload_wq), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, offload_wq);
|
||||
spin_lock_init(&offload_wq->offload_lock);
|
||||
|
||||
aconn = dm_kunit_alloc_connector(test, adev, NULL);
|
||||
mutex_init(&aconn->hpd_lock);
|
||||
aconn->timing_changed = false;
|
||||
offload_wq->aconnector = aconn;
|
||||
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc);
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link_srv);
|
||||
link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
|
||||
|
||||
link_srv->detect_connection_type = dm_test_detect_connection_single;
|
||||
link_srv->dp_handle_automated_test = dm_test_dp_handle_automated_test;
|
||||
dc->link_srv = link_srv;
|
||||
dc->ctx = ctx;
|
||||
ctx->dc = dc;
|
||||
link->dc = dc;
|
||||
link->ctx = ctx;
|
||||
link->aux_access_disabled = true;
|
||||
link->connector_signal = SIGNAL_TYPE_DISPLAY_PORT;
|
||||
aconn->dc_link = link;
|
||||
|
||||
offload_work = kzalloc(sizeof(*offload_work), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, offload_work);
|
||||
offload_work->offload_wq = offload_wq;
|
||||
offload_work->adev = adev;
|
||||
offload_work->data.bytes.device_service_irq.bits.AUTOMATED_TEST = 1;
|
||||
INIT_WORK(&offload_work->work, dm_handle_hpd_rx_offload_work);
|
||||
|
||||
dm_handle_hpd_rx_offload_work(&offload_work->work);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm_test_automated_test_count, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_hpd_rx_offload_work_link_loss - Test link-loss branch
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* With a present non-eDP connection and no service-IRQ bits set, the worker
|
||||
* takes the else-if link-loss path: dc_link_check_link_loss_status() and
|
||||
* dc_link_dp_allow_hpd_rx_irq() gate entry, then dc_link_dp_read_hpd_rx_irq_data()
|
||||
* returns DC_OK and a second link-loss check triggers dc_link_dp_handle_link_loss().
|
||||
* All four are stubbed via link_srv.
|
||||
*/
|
||||
static void dm_test_hpd_rx_offload_work_link_loss(struct kunit *test)
|
||||
{
|
||||
struct hpd_rx_irq_offload_work_queue *offload_wq;
|
||||
struct hpd_rx_irq_offload_work *offload_work;
|
||||
struct amdgpu_dm_connector *aconn;
|
||||
struct link_service *link_srv;
|
||||
struct amdgpu_device *adev;
|
||||
struct dc_context *ctx;
|
||||
struct dc_link *link;
|
||||
struct dc *dc;
|
||||
|
||||
dm_test_handle_link_loss_count = 0;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
mutex_init(&adev->dm.dc_lock);
|
||||
adev->reset_domain = kunit_kzalloc(test, sizeof(*adev->reset_domain),
|
||||
GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev->reset_domain);
|
||||
|
||||
offload_wq = kunit_kzalloc(test, sizeof(*offload_wq), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, offload_wq);
|
||||
spin_lock_init(&offload_wq->offload_lock);
|
||||
|
||||
aconn = dm_kunit_alloc_connector(test, adev, NULL);
|
||||
mutex_init(&aconn->hpd_lock);
|
||||
offload_wq->aconnector = aconn;
|
||||
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc);
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link_srv);
|
||||
link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
|
||||
|
||||
link_srv->detect_connection_type = dm_test_detect_connection_single;
|
||||
link_srv->dp_parse_link_loss_status = dm_test_dp_parse_link_loss_true;
|
||||
link_srv->dp_should_allow_hpd_rx_irq = dm_test_dp_should_allow_hpd_rx_irq_true;
|
||||
link_srv->dp_read_hpd_rx_irq_data = dm_test_dp_read_hpd_rx_irq_data_ok;
|
||||
link_srv->dp_handle_link_loss = dm_test_dp_handle_link_loss;
|
||||
dc->link_srv = link_srv;
|
||||
dc->ctx = ctx;
|
||||
ctx->dc = dc;
|
||||
link->dc = dc;
|
||||
link->ctx = ctx;
|
||||
link->connector_signal = SIGNAL_TYPE_DISPLAY_PORT;
|
||||
aconn->dc_link = link;
|
||||
|
||||
offload_work = kzalloc(sizeof(*offload_work), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, offload_work);
|
||||
offload_work->offload_wq = offload_wq;
|
||||
offload_work->adev = adev;
|
||||
INIT_WORK(&offload_work->work, dm_handle_hpd_rx_offload_work);
|
||||
|
||||
dm_handle_hpd_rx_offload_work(&offload_work->work);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm_test_handle_link_loss_count, 1);
|
||||
}
|
||||
|
||||
/* Tests for amdgpu_dm_hdmi_hpd_debounce_work() */
|
||||
|
||||
/**
|
||||
* dm_test_hdmi_hpd_debounce_detect_false - Test debounce false detect path
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_hdmi_hpd_debounce_detect_false(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconn;
|
||||
struct link_service *link_srv;
|
||||
struct amdgpu_device *adev;
|
||||
struct dc_context *ctx;
|
||||
struct dc_link *link;
|
||||
struct dc *dc;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
mutex_init(&adev->dm.dc_lock);
|
||||
|
||||
aconn = dm_kunit_alloc_connector(test, adev, NULL);
|
||||
mutex_init(&aconn->hpd_lock);
|
||||
INIT_DELAYED_WORK(&aconn->hdmi_hpd_debounce_work,
|
||||
amdgpu_dm_hdmi_hpd_debounce_work);
|
||||
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc);
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link_srv);
|
||||
link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
|
||||
|
||||
link_srv->detect_link = dm_test_detect_link_false;
|
||||
dc->ctx = ctx;
|
||||
dc->link_srv = link_srv;
|
||||
ctx->dc = dc;
|
||||
link->dc = dc;
|
||||
link->ctx = ctx;
|
||||
aconn->dc_link = link;
|
||||
|
||||
amdgpu_dm_hdmi_hpd_debounce_work(&aconn->hdmi_hpd_debounce_work.work);
|
||||
KUNIT_EXPECT_NULL(test, aconn->hdmi_prev_sink);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_hdmi_hpd_debounce_reallow_idle - Test debounce idle/sink-release tail
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* With detection stubbed to return false, the if (ret) block is skipped, but
|
||||
* the function tail is still exercised: IPS support plus an idle-allowed dmub
|
||||
* makes reallow_idle true so dc_allow_idle_optimizations() runs on both entry
|
||||
* and exit (disable_idle_power_optimizations keeps those safe early returns),
|
||||
* and a cached hdmi_prev_sink is released and cleared.
|
||||
*/
|
||||
static void dm_test_hdmi_hpd_debounce_reallow_idle(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconn;
|
||||
struct link_service *link_srv;
|
||||
struct dc_dmub_srv *dmub_srv;
|
||||
struct amdgpu_device *adev;
|
||||
struct dal_logger *logger;
|
||||
struct dc_context *ctx;
|
||||
struct dc_link *link;
|
||||
struct dc *dc;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
mutex_init(&adev->dm.dc_lock);
|
||||
|
||||
aconn = dm_kunit_alloc_connector(test, adev, NULL);
|
||||
mutex_init(&aconn->hpd_lock);
|
||||
INIT_DELAYED_WORK(&aconn->hdmi_hpd_debounce_work,
|
||||
amdgpu_dm_hdmi_hpd_debounce_work);
|
||||
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc);
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link_srv);
|
||||
link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
|
||||
logger = kunit_kzalloc(test, sizeof(*logger), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, logger);
|
||||
dmub_srv = kunit_kzalloc(test, sizeof(*dmub_srv), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dmub_srv);
|
||||
|
||||
link_srv->detect_link = dm_test_detect_link_false;
|
||||
dc->ctx = ctx;
|
||||
dc->link_srv = link_srv;
|
||||
ctx->dc = dc;
|
||||
link->dc = dc;
|
||||
link->ctx = ctx;
|
||||
aconn->dc_link = link;
|
||||
|
||||
/* DC_LOG_* dereferences ctx->logger->dev, so wire a real drm device. */
|
||||
logger->dev = &adev->ddev;
|
||||
dc->ctx->logger = logger;
|
||||
|
||||
/* Make reallow_idle true and keep dc_allow_idle*() a safe early return. */
|
||||
dmub_srv->idle_allowed = true;
|
||||
dc->ctx->dmub_srv = dmub_srv;
|
||||
dc->caps.ips_support = true;
|
||||
dc->debug.disable_idle_power_optimizations = true;
|
||||
|
||||
/* A cached previous sink must be released and cleared. */
|
||||
aconn->hdmi_prev_sink = dm_test_sink_create(link);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, aconn->hdmi_prev_sink);
|
||||
|
||||
amdgpu_dm_hdmi_hpd_debounce_work(&aconn->hdmi_hpd_debounce_work.work);
|
||||
KUNIT_EXPECT_NULL(test, aconn->hdmi_prev_sink);
|
||||
}
|
||||
|
||||
/* Tests for handle_hpd_irq()/handle_hpd_irq_helper() */
|
||||
|
||||
/**
|
||||
* dm_test_handle_hpd_irq_disabled - Test HPD helper returns when disabled
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_handle_hpd_irq_disabled(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconn;
|
||||
struct dm_connector_state *state;
|
||||
struct amdgpu_device *adev;
|
||||
struct dc_context *ctx;
|
||||
struct dc_link *link;
|
||||
struct dc *dc;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
adev->dm.disable_hpd_irq = true;
|
||||
|
||||
aconn = dm_kunit_alloc_connector(test, adev, NULL);
|
||||
state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
aconn->base.state = &state->base;
|
||||
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc);
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
|
||||
|
||||
dc->ctx = ctx;
|
||||
ctx->dc = dc;
|
||||
link->ctx = ctx;
|
||||
aconn->dc_link = link;
|
||||
aconn->fake_enable = true;
|
||||
|
||||
handle_hpd_irq_helper(aconn, DETECT_REASON_HPD);
|
||||
KUNIT_EXPECT_TRUE(test, aconn->fake_enable);
|
||||
|
||||
handle_hpd_irq(aconn);
|
||||
KUNIT_EXPECT_TRUE(test, aconn->fake_enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_handle_hpd_irq_helper_debounce_schedule - Test HDMI debounce branch
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* An HDMI link reporting a disconnect (connection type none) with a non-zero
|
||||
* debounce delay and a cached local_sink must take the debounce branch: it
|
||||
* caches local_sink in hdmi_prev_sink and schedules the delayed debounce work
|
||||
* instead of detecting immediately.
|
||||
*/
|
||||
static void dm_test_handle_hpd_irq_helper_debounce_schedule(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconn;
|
||||
struct dm_connector_state *state;
|
||||
struct link_service *link_srv;
|
||||
struct amdgpu_device *adev;
|
||||
struct dc_context *ctx;
|
||||
struct dc_link *link;
|
||||
struct dc *dc;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
|
||||
aconn = dm_kunit_alloc_connector(test, adev, NULL);
|
||||
mutex_init(&aconn->hpd_lock);
|
||||
INIT_DELAYED_WORK(&aconn->hdmi_hpd_debounce_work,
|
||||
amdgpu_dm_hdmi_hpd_debounce_work);
|
||||
|
||||
state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
aconn->base.state = &state->base;
|
||||
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc);
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link_srv);
|
||||
link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
|
||||
|
||||
link_srv->detect_connection_type = dm_test_detect_connection_none;
|
||||
dc->ctx = ctx;
|
||||
dc->link_srv = link_srv;
|
||||
ctx->dc = dc;
|
||||
link->dc = dc;
|
||||
link->ctx = ctx;
|
||||
aconn->dc_link = link;
|
||||
|
||||
/* HDMI signal + debounce delay + cached sink -> debounce branch. */
|
||||
aconn->hdmi_hpd_debounce_delay_ms = 100;
|
||||
link->connector_signal = SIGNAL_TYPE_HDMI_TYPE_A;
|
||||
link->local_sink = dm_test_sink_create(link);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link->local_sink);
|
||||
|
||||
handle_hpd_irq_helper(aconn, DETECT_REASON_HPD);
|
||||
|
||||
/* local_sink is cached for later comparison by the debounce work. */
|
||||
KUNIT_EXPECT_PTR_EQ(test, aconn->hdmi_prev_sink, link->local_sink);
|
||||
|
||||
cancel_delayed_work_sync(&aconn->hdmi_hpd_debounce_work);
|
||||
dm_test_sink_release(aconn->hdmi_prev_sink);
|
||||
dm_test_sink_release(link->local_sink);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_handle_hpd_irq_helper_debounce_release_prev - Test stale prev_sink
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* When the debounce branch is taken and a stale hdmi_prev_sink is already
|
||||
* cached from a previous HPD, it must be released before caching the current
|
||||
* local_sink. This exercises the dc_sink_release() of the previous sink.
|
||||
*/
|
||||
static void dm_test_handle_hpd_irq_helper_debounce_release_prev(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconn;
|
||||
struct dm_connector_state *state;
|
||||
struct link_service *link_srv;
|
||||
struct amdgpu_device *adev;
|
||||
struct dc_context *ctx;
|
||||
struct dc_link *link;
|
||||
struct dc *dc;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
|
||||
aconn = dm_kunit_alloc_connector(test, adev, NULL);
|
||||
mutex_init(&aconn->hpd_lock);
|
||||
INIT_DELAYED_WORK(&aconn->hdmi_hpd_debounce_work,
|
||||
amdgpu_dm_hdmi_hpd_debounce_work);
|
||||
|
||||
state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
aconn->base.state = &state->base;
|
||||
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc);
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link_srv);
|
||||
link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
|
||||
|
||||
link_srv->detect_connection_type = dm_test_detect_connection_none;
|
||||
dc->ctx = ctx;
|
||||
dc->link_srv = link_srv;
|
||||
ctx->dc = dc;
|
||||
link->dc = dc;
|
||||
link->ctx = ctx;
|
||||
aconn->dc_link = link;
|
||||
|
||||
/* HDMI signal + debounce delay + cached sink -> debounce branch. */
|
||||
aconn->hdmi_hpd_debounce_delay_ms = 100;
|
||||
link->connector_signal = SIGNAL_TYPE_HDMI_TYPE_A;
|
||||
link->local_sink = dm_test_sink_create(link);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link->local_sink);
|
||||
|
||||
/* Stale sink from a previous HPD must be released by the helper. */
|
||||
aconn->hdmi_prev_sink = dm_test_sink_create(link);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, aconn->hdmi_prev_sink);
|
||||
KUNIT_ASSERT_PTR_NE(test, aconn->hdmi_prev_sink, link->local_sink);
|
||||
|
||||
handle_hpd_irq_helper(aconn, DETECT_REASON_HPD);
|
||||
|
||||
/* Stale sink replaced by the current local_sink. */
|
||||
KUNIT_EXPECT_PTR_EQ(test, aconn->hdmi_prev_sink, link->local_sink);
|
||||
|
||||
cancel_delayed_work_sync(&aconn->hdmi_hpd_debounce_work);
|
||||
dm_test_sink_release(aconn->hdmi_prev_sink);
|
||||
dm_test_sink_release(link->local_sink);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_handle_hpd_irq_helper_detect_false - Test immediate detect branch
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* With no force, no debounce, and detection stubbed to report no connection,
|
||||
* the helper takes the else branch: dc_exit_ips_for_hw_access() is a safe
|
||||
* no-op (no IPS support) and dc_link_detect() returns false, so the connected
|
||||
* if (ret) block is skipped. fake_enable must still be cleared.
|
||||
*/
|
||||
static void dm_test_handle_hpd_irq_helper_detect_false(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconn;
|
||||
struct dm_connector_state *state;
|
||||
struct link_service *link_srv;
|
||||
struct amdgpu_device *adev;
|
||||
struct dc_context *ctx;
|
||||
struct dc_link *link;
|
||||
struct dc *dc;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
mutex_init(&adev->dm.dc_lock);
|
||||
|
||||
aconn = dm_kunit_alloc_connector(test, adev, NULL);
|
||||
mutex_init(&aconn->hpd_lock);
|
||||
INIT_DELAYED_WORK(&aconn->hdmi_hpd_debounce_work,
|
||||
amdgpu_dm_hdmi_hpd_debounce_work);
|
||||
|
||||
state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
aconn->base.state = &state->base;
|
||||
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc);
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link_srv);
|
||||
link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
|
||||
|
||||
link_srv->detect_connection_type = dm_test_detect_connection_none;
|
||||
link_srv->detect_link = dm_test_detect_link_false;
|
||||
dc->ctx = ctx;
|
||||
dc->link_srv = link_srv;
|
||||
ctx->dc = dc;
|
||||
link->dc = dc;
|
||||
link->ctx = ctx;
|
||||
aconn->dc_link = link;
|
||||
aconn->fake_enable = true;
|
||||
|
||||
/* No debounce delay and no force -> immediate-detect else branch. */
|
||||
handle_hpd_irq_helper(aconn, DETECT_REASON_HPD);
|
||||
|
||||
KUNIT_EXPECT_FALSE(test, aconn->fake_enable);
|
||||
}
|
||||
|
||||
/* Tests for handle_hpd_rx_irq()/schedule_hpd_rx_offload_work() */
|
||||
|
||||
/**
|
||||
* dm_test_handle_hpd_rx_irq_disabled - Test HPDRX handler returns when disabled
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_handle_hpd_rx_irq_disabled(struct kunit *test)
|
||||
{
|
||||
struct hpd_rx_irq_offload_work_queue *offload_wq;
|
||||
struct amdgpu_dm_connector *aconn;
|
||||
struct link_service *link_srv;
|
||||
struct amdgpu_device *adev;
|
||||
struct dc_context *ctx;
|
||||
struct dc_link *link;
|
||||
struct dc *dc;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
adev->dm.disable_hpd_irq = true;
|
||||
|
||||
offload_wq = kunit_kzalloc(test, sizeof(*offload_wq), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, offload_wq);
|
||||
adev->dm.hpd_rx_offload_wq = offload_wq;
|
||||
|
||||
aconn = dm_kunit_alloc_connector(test, adev, NULL);
|
||||
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc);
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link_srv);
|
||||
link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
|
||||
|
||||
link_srv->dp_handle_hpd_rx_irq = dm_test_handle_hpd_rx_no_work;
|
||||
dc->ctx = ctx;
|
||||
dc->link_srv = link_srv;
|
||||
ctx->dc = dc;
|
||||
link->dc = dc;
|
||||
link->ctx = ctx;
|
||||
link->link_index = 0;
|
||||
aconn->dc_link = link;
|
||||
|
||||
handle_hpd_rx_irq(aconn);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_schedule_hpd_rx_offload_work - Test offload work is queued
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_schedule_hpd_rx_offload_work(struct kunit *test)
|
||||
{
|
||||
struct hpd_rx_irq_offload_work_queue *offload_wq;
|
||||
struct amdgpu_device *adev;
|
||||
union hpd_irq_data data = { 0 };
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
|
||||
offload_wq = kunit_kzalloc(test, sizeof(*offload_wq), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, offload_wq);
|
||||
offload_wq->wq = create_singlethread_workqueue("dm_irq_test_hpd_rx");
|
||||
KUNIT_ASSERT_NOT_NULL(test, offload_wq->wq);
|
||||
|
||||
schedule_hpd_rx_offload_work(adev, offload_wq, data);
|
||||
flush_workqueue(offload_wq->wq);
|
||||
destroy_workqueue(offload_wq->wq);
|
||||
}
|
||||
|
||||
static void dm_test_destroy_offload_wq(void *data)
|
||||
{
|
||||
struct workqueue_struct *wq = data;
|
||||
|
||||
flush_workqueue(wq);
|
||||
destroy_workqueue(wq);
|
||||
}
|
||||
|
||||
/*
|
||||
* Build an aconnector wired for handle_hpd_rx_irq(): HPD enabled, an MST-root
|
||||
* connector on an MST-branch link so the post-detect block and drm_dp_cec_irq
|
||||
* are skipped, and a flushed offload work queue. Caller sets the link_srv
|
||||
* stubs (dp_handle_hpd_rx_irq and, if needed, dp_should_allow_hpd_rx_irq).
|
||||
*/
|
||||
static struct amdgpu_dm_connector *dm_test_setup_hpd_rx_irq(struct kunit *test,
|
||||
struct link_service **link_srv_out)
|
||||
{
|
||||
struct hpd_rx_irq_offload_work_queue *offload_wq;
|
||||
struct amdgpu_dm_connector *aconn;
|
||||
struct link_service *link_srv;
|
||||
struct amdgpu_device *adev;
|
||||
struct dc_context *ctx;
|
||||
struct dc_link *link;
|
||||
struct dc *dc;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
mutex_init(&adev->dm.dc_lock);
|
||||
|
||||
offload_wq = kunit_kzalloc(test, sizeof(*offload_wq), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, offload_wq);
|
||||
spin_lock_init(&offload_wq->offload_lock);
|
||||
offload_wq->wq = create_singlethread_workqueue("dm_irq_test_hpd_rx");
|
||||
KUNIT_ASSERT_NOT_NULL(test, offload_wq->wq);
|
||||
KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dm_test_destroy_offload_wq,
|
||||
offload_wq->wq), 0);
|
||||
adev->dm.hpd_rx_offload_wq = offload_wq;
|
||||
|
||||
aconn = dm_kunit_alloc_connector(test, adev, NULL);
|
||||
mutex_init(&aconn->hpd_lock);
|
||||
aconn->mst_mgr.mst_state = true;
|
||||
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc);
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link_srv);
|
||||
link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
|
||||
|
||||
dc->ctx = ctx;
|
||||
dc->link_srv = link_srv;
|
||||
ctx->dc = dc;
|
||||
link->dc = dc;
|
||||
link->ctx = ctx;
|
||||
link->link_index = 0;
|
||||
link->type = dc_connection_mst_branch;
|
||||
aconn->dc_link = link;
|
||||
|
||||
*link_srv_out = link_srv;
|
||||
|
||||
return aconn;
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_handle_hpd_rx_irq_no_left_work - Test HPDRX early out (no left work)
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* When dc_link_handle_hpd_rx_irq() reports no left-over work, the handler
|
||||
* jumps to out and returns without scheduling any offload work.
|
||||
*/
|
||||
static void dm_test_handle_hpd_rx_irq_no_left_work(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconn;
|
||||
struct link_service *link_srv;
|
||||
|
||||
aconn = dm_test_setup_hpd_rx_irq(test, &link_srv);
|
||||
link_srv->dp_handle_hpd_rx_irq = dm_test_handle_hpd_rx_no_work;
|
||||
|
||||
handle_hpd_rx_irq(aconn);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_handle_hpd_rx_irq_automated_test - Test HPDRX automated-test path
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* The AUTOMATED_TEST device-service bit must schedule offload work and jump to
|
||||
* out before the allow-hpd-rx-irq checks.
|
||||
*/
|
||||
static void dm_test_handle_hpd_rx_irq_automated_test(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconn;
|
||||
struct link_service *link_srv;
|
||||
|
||||
aconn = dm_test_setup_hpd_rx_irq(test, &link_srv);
|
||||
link_srv->dp_handle_hpd_rx_irq = dm_test_handle_hpd_rx_automated;
|
||||
|
||||
handle_hpd_rx_irq(aconn);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_handle_hpd_rx_irq_msg_rdy - Test HPDRX MST message-ready path
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* With HPD RX IRQs allowed and an UP_REQ_MSG_RDY bit set, the handler must take
|
||||
* the MST message-ready branch, mark is_handling_mst_msg_rdy_event and schedule
|
||||
* offload work.
|
||||
*/
|
||||
static void dm_test_handle_hpd_rx_irq_msg_rdy(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconn;
|
||||
struct link_service *link_srv;
|
||||
|
||||
aconn = dm_test_setup_hpd_rx_irq(test, &link_srv);
|
||||
link_srv->dp_handle_hpd_rx_irq = dm_test_handle_hpd_rx_msg_rdy;
|
||||
link_srv->dp_should_allow_hpd_rx_irq = dm_test_allow_hpd_rx_irq_true;
|
||||
|
||||
handle_hpd_rx_irq(aconn);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, drm_to_adev(aconn->base.dev)
|
||||
->dm.hpd_rx_offload_wq->is_handling_mst_msg_rdy_event);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_handle_hpd_rx_irq_link_loss - Test HPDRX link-loss path
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* With HPD RX IRQs allowed and a reported link loss (no message-ready bits),
|
||||
* the handler must take the link-loss branch, mark is_handling_link_loss and
|
||||
* schedule offload work.
|
||||
*/
|
||||
static void dm_test_handle_hpd_rx_irq_link_loss(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconn;
|
||||
struct link_service *link_srv;
|
||||
|
||||
aconn = dm_test_setup_hpd_rx_irq(test, &link_srv);
|
||||
link_srv->dp_handle_hpd_rx_irq = dm_test_handle_hpd_rx_link_loss;
|
||||
link_srv->dp_should_allow_hpd_rx_irq = dm_test_allow_hpd_rx_irq_true;
|
||||
|
||||
handle_hpd_rx_irq(aconn);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, drm_to_adev(aconn->base.dev)
|
||||
->dm.hpd_rx_offload_wq->is_handling_link_loss);
|
||||
}
|
||||
|
||||
static struct kunit_case amdgpu_dm_irq_tests[] = {
|
||||
/* amdgpu_dm_hpd_to_dal_irq_source */
|
||||
KUNIT_CASE(dm_test_hpd_to_dal_irq_source_hpd1),
|
||||
|
|
@ -2004,6 +2914,26 @@ static struct kunit_case amdgpu_dm_irq_tests[] = {
|
|||
KUNIT_CASE(dm_test_hpd_init_fini_with_connectors),
|
||||
KUNIT_CASE(dm_test_hpd_init_fini_analog_connector),
|
||||
KUNIT_CASE(dm_test_hpd_init_fini_irq_ref),
|
||||
/* dm_handle_hpd_rx_offload_work */
|
||||
KUNIT_CASE(dm_test_hpd_rx_offload_work_no_connector),
|
||||
KUNIT_CASE(dm_test_hpd_rx_offload_work_no_connection),
|
||||
KUNIT_CASE(dm_test_hpd_rx_offload_work_automated_test),
|
||||
KUNIT_CASE(dm_test_hpd_rx_offload_work_link_loss),
|
||||
/* amdgpu_dm_hdmi_hpd_debounce_work */
|
||||
KUNIT_CASE(dm_test_hdmi_hpd_debounce_detect_false),
|
||||
KUNIT_CASE(dm_test_hdmi_hpd_debounce_reallow_idle),
|
||||
/* handle_hpd_irq/handle_hpd_irq_helper */
|
||||
KUNIT_CASE(dm_test_handle_hpd_irq_disabled),
|
||||
KUNIT_CASE(dm_test_handle_hpd_irq_helper_debounce_schedule),
|
||||
KUNIT_CASE(dm_test_handle_hpd_irq_helper_debounce_release_prev),
|
||||
KUNIT_CASE(dm_test_handle_hpd_irq_helper_detect_false),
|
||||
/* handle_hpd_rx_irq/schedule_hpd_rx_offload_work */
|
||||
KUNIT_CASE(dm_test_handle_hpd_rx_irq_disabled),
|
||||
KUNIT_CASE(dm_test_handle_hpd_rx_irq_no_left_work),
|
||||
KUNIT_CASE(dm_test_handle_hpd_rx_irq_automated_test),
|
||||
KUNIT_CASE(dm_test_handle_hpd_rx_irq_msg_rdy),
|
||||
KUNIT_CASE(dm_test_handle_hpd_rx_irq_link_loss),
|
||||
KUNIT_CASE(dm_test_schedule_hpd_rx_offload_work),
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user