mirror of
https://github.com/torvalds/linux.git
synced 2026-09-19 17:18:02 +02:00
drm/amd/display: Add vblank handling tests for crtc
Add KUnit coverage for the CRTC vblank paths: - amdgpu_dm_crtc_handle_vblank: no-event completion and the AMDGPU_FLIP_SUBMITTED guard that keeps a pending event pending. - amdgpu_dm_crtc_vblank_control_worker: enable increments, disable decrements, and disable clamps the active vblank IRQ count at zero. - amdgpu_dm_crtc_disable_vblank: disable path returns cleanly when the IRQ subsystem is not installed. Expose amdgpu_dm_crtc_vblank_control_worker for KUnit via STATIC_IFN_KUNIT/EXPORT_IF_KUNIT and declare it in the header. Assisted-by: Copilot:Claude-Opus-4.8 Reviewed-by: Alex Hung <alex.hung@amd.com> Signed-off-by: Bhawanpreet Lakha <bhawanpreet.lakha@amd.com> Signed-off-by: Roman Li <roman.li@amd.com> Tested-by: Dan Wheeler <daniel.wheeler@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
parent
d1db1a99fc
commit
bed804fe0b
|
|
@ -59,6 +59,7 @@ void amdgpu_dm_crtc_handle_vblank(struct amdgpu_crtc *acrtc)
|
|||
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_crtc_handle_vblank);
|
||||
|
||||
bool amdgpu_dm_crtc_modeset_required(struct drm_crtc_state *crtc_state,
|
||||
struct dc_stream_state *new_stream,
|
||||
|
|
@ -216,7 +217,7 @@ struct idle_workqueue *idle_create_workqueue(struct amdgpu_device *adev)
|
|||
}
|
||||
EXPORT_IF_KUNIT(idle_create_workqueue);
|
||||
|
||||
static void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work)
|
||||
STATIC_IFN_KUNIT void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work)
|
||||
{
|
||||
struct vblank_control_work *vblank_work =
|
||||
container_of(work, struct vblank_control_work, work);
|
||||
|
|
@ -241,6 +242,7 @@ static void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work)
|
|||
|
||||
kfree(vblank_work);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_crtc_vblank_control_worker);
|
||||
|
||||
static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable)
|
||||
{
|
||||
|
|
@ -400,6 +402,7 @@ void amdgpu_dm_crtc_disable_vblank(struct drm_crtc *crtc)
|
|||
{
|
||||
amdgpu_dm_crtc_set_vblank(crtc, false);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_crtc_disable_vblank);
|
||||
|
||||
STATIC_IFN_KUNIT void amdgpu_dm_crtc_destroy_state(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *state)
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ struct drm_crtc_state *amdgpu_dm_crtc_duplicate_state(struct drm_crtc *crtc);
|
|||
void amdgpu_dm_crtc_reset_state(struct drm_crtc *crtc);
|
||||
void amdgpu_dm_crtc_update_crtc_active_planes(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *new_crtc_state);
|
||||
void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work);
|
||||
#endif
|
||||
|
||||
bool amdgpu_dm_crtc_vrr_active(const struct dm_crtc_state *dm_state);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/drm_kunit_helpers.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
|
||||
#include "dc.h"
|
||||
#include "amdgpu.h"
|
||||
|
|
@ -621,6 +622,209 @@ static void dm_test_crtc_destroy_state_no_stream(struct kunit *test)
|
|||
amdgpu_dm_crtc_destroy_state(NULL, &dm_state->base);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_crtc_handle_vblank_no_event - Test vblank handling with no pending event
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* With no flip event pending, handling a vblank must complete without sending a
|
||||
* vblank event and must leave acrtc->event untouched (NULL).
|
||||
*/
|
||||
static void dm_test_crtc_handle_vblank_no_event(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
struct amdgpu_crtc *acrtc;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
|
||||
|
||||
/* Initialise vblank so drm_crtc_handle_vblank() runs cleanly. */
|
||||
KUNIT_ASSERT_EQ(test, drm_vblank_init(&adev->ddev, 1), 0);
|
||||
|
||||
acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc);
|
||||
acrtc->base.dev = &adev->ddev;
|
||||
acrtc->event = NULL;
|
||||
|
||||
amdgpu_dm_crtc_handle_vblank(acrtc);
|
||||
|
||||
KUNIT_EXPECT_NULL(test, acrtc->event);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_crtc_handle_vblank_skips_when_flip_submitted - Test event kept on submit
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* A pending event whose flip is still AMDGPU_FLIP_SUBMITTED must not be signalled
|
||||
* on vblank; acrtc->event must remain set for later completion.
|
||||
*/
|
||||
static void dm_test_crtc_handle_vblank_skips_when_flip_submitted(struct kunit *test)
|
||||
{
|
||||
struct drm_pending_vblank_event *event;
|
||||
struct amdgpu_device *adev;
|
||||
struct amdgpu_crtc *acrtc;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
|
||||
|
||||
KUNIT_ASSERT_EQ(test, drm_vblank_init(&adev->ddev, 1), 0);
|
||||
|
||||
acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc);
|
||||
event = kunit_kzalloc(test, sizeof(*event), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, event);
|
||||
|
||||
acrtc->base.dev = &adev->ddev;
|
||||
acrtc->event = event;
|
||||
acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED;
|
||||
|
||||
amdgpu_dm_crtc_handle_vblank(acrtc);
|
||||
|
||||
/* Flip still in-flight: event must be preserved, not signalled. */
|
||||
KUNIT_EXPECT_PTR_EQ(test, acrtc->event, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_vblank_control_worker_setup - Build a vblank_control_work for the worker
|
||||
* @test: The KUnit test context
|
||||
* @enable: Value for vblank_work->enable
|
||||
* @count: Initial dm->active_vblank_irq_count
|
||||
*
|
||||
* Returns a work item wired to a freshly allocated adev/crtc/stream. The CRTC is
|
||||
* left without an atomic state so amdgpu_dm_ism_commit_event() short-circuits and
|
||||
* only the vblank IRQ accounting in the worker runs.
|
||||
*/
|
||||
static struct vblank_control_work *
|
||||
dm_test_vblank_control_worker_setup(struct kunit *test, bool enable,
|
||||
uint32_t count)
|
||||
{
|
||||
struct dc_stream_state *stream;
|
||||
struct vblank_control_work *work;
|
||||
struct amdgpu_device *adev;
|
||||
struct amdgpu_crtc *acrtc;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
|
||||
|
||||
mutex_init(&adev->dm.dc_lock);
|
||||
adev->dm.dc = dm_kunit_alloc_dc_with_ctx(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev->dm.dc);
|
||||
adev->dm.active_vblank_irq_count = count;
|
||||
|
||||
acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc);
|
||||
acrtc->base.dev = &adev->ddev;
|
||||
acrtc->base.state = NULL;
|
||||
|
||||
stream = dm_kunit_alloc_stream(test, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
|
||||
/* Worker releases the stream; keep an extra ref so kunit owns the free. */
|
||||
kref_get(&stream->refcount);
|
||||
|
||||
/* Worker kfree()s the work item, so it must be a plain allocation. */
|
||||
work = kzalloc_obj(*work, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, work);
|
||||
work->dm = &adev->dm;
|
||||
work->acrtc = acrtc;
|
||||
work->stream = stream;
|
||||
work->enable = enable;
|
||||
|
||||
return work;
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_vblank_control_worker_enable_increments - Test enable bumps IRQ count
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* Running the worker with enable set must increment the active vblank IRQ count.
|
||||
*/
|
||||
static void dm_test_vblank_control_worker_enable_increments(struct kunit *test)
|
||||
{
|
||||
struct vblank_control_work *work;
|
||||
struct amdgpu_display_manager *dm;
|
||||
|
||||
work = dm_test_vblank_control_worker_setup(test, true, 0);
|
||||
dm = work->dm;
|
||||
|
||||
amdgpu_dm_crtc_vblank_control_worker(&work->work);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm->active_vblank_irq_count, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_vblank_control_worker_disable_decrements - Test disable drops IRQ count
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* Running the worker with enable clear must decrement a non-zero active vblank
|
||||
* IRQ count.
|
||||
*/
|
||||
static void dm_test_vblank_control_worker_disable_decrements(struct kunit *test)
|
||||
{
|
||||
struct vblank_control_work *work;
|
||||
struct amdgpu_display_manager *dm;
|
||||
|
||||
work = dm_test_vblank_control_worker_setup(test, false, 2);
|
||||
dm = work->dm;
|
||||
|
||||
amdgpu_dm_crtc_vblank_control_worker(&work->work);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm->active_vblank_irq_count, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_vblank_control_worker_disable_clamps_zero - Test disable clamps at zero
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* Disabling when the active vblank IRQ count is already zero must not underflow.
|
||||
*/
|
||||
static void dm_test_vblank_control_worker_disable_clamps_zero(struct kunit *test)
|
||||
{
|
||||
struct vblank_control_work *work;
|
||||
struct amdgpu_display_manager *dm;
|
||||
|
||||
work = dm_test_vblank_control_worker_setup(test, false, 0);
|
||||
dm = work->dm;
|
||||
|
||||
amdgpu_dm_crtc_vblank_control_worker(&work->work);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm->active_vblank_irq_count, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_crtc_disable_vblank_no_irq_installed - Test disable with IRQ uninstalled
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* Disabling vblank walks amdgpu_dm_crtc_set_vblank()'s disable path. With the
|
||||
* IRQ subsystem not installed, amdgpu_irq_put() returns early so the routine
|
||||
* completes without touching the vblank workqueue or the active IRQ count.
|
||||
*/
|
||||
static void dm_test_crtc_disable_vblank_no_irq_installed(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
struct amdgpu_crtc *acrtc;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
|
||||
|
||||
adev->dm.dc = dm_kunit_alloc_dc_with_ctx(test);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev->dm.dc);
|
||||
/* DCE_VERSION_6_0 has no VRR, so the vupdate-irq branch is skipped. */
|
||||
adev->dm.dc->ctx->dce_version = DCE_VERSION_6_0;
|
||||
adev->dm.active_vblank_irq_count = 0;
|
||||
|
||||
/* No CRTCs registered and IRQs not installed -> irq_put returns early. */
|
||||
adev->mode_info.num_crtc = 0;
|
||||
adev->irq.installed = false;
|
||||
|
||||
acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc);
|
||||
acrtc->base.dev = &adev->ddev;
|
||||
acrtc->crtc_id = 0;
|
||||
|
||||
amdgpu_dm_crtc_disable_vblank(&acrtc->base);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, adev->dm.active_vblank_irq_count, 0);
|
||||
}
|
||||
|
||||
static struct kunit_case amdgpu_dm_crtc_tests[] = {
|
||||
/* amdgpu_dm_crtc_modeset_required */
|
||||
KUNIT_CASE(dm_test_crtc_modeset_required_active_mode_changed),
|
||||
|
|
@ -665,6 +869,15 @@ static struct kunit_case amdgpu_dm_crtc_tests[] = {
|
|||
KUNIT_CASE(dm_test_crtc_reset_state_allocates_state),
|
||||
/* amdgpu_dm_crtc_destroy_state */
|
||||
KUNIT_CASE(dm_test_crtc_destroy_state_no_stream),
|
||||
/* amdgpu_dm_crtc_handle_vblank */
|
||||
KUNIT_CASE(dm_test_crtc_handle_vblank_no_event),
|
||||
KUNIT_CASE(dm_test_crtc_handle_vblank_skips_when_flip_submitted),
|
||||
/* amdgpu_dm_crtc_vblank_control_worker */
|
||||
KUNIT_CASE(dm_test_vblank_control_worker_enable_increments),
|
||||
KUNIT_CASE(dm_test_vblank_control_worker_disable_decrements),
|
||||
KUNIT_CASE(dm_test_vblank_control_worker_disable_clamps_zero),
|
||||
/* amdgpu_dm_crtc_disable_vblank */
|
||||
KUNIT_CASE(dm_test_crtc_disable_vblank_no_irq_installed),
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user