root/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c
// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
 * KUnit tests for amdgpu_dm_hdcp.c
 *
 * Copyright 2026 Advanced Micro Devices, Inc.
 */

#include <kunit/test.h>
#include <linux/workqueue.h>

#include "amdgpu_dm_hdcp.h"

static void dummy_work_fn(struct work_struct *work) {}

/* Tests for process_output() */

/*
 * Helper: allocate and initialise a minimal hdcp_workqueue sufficient for
 * process_output() testing.  Only the three delayed works accessed by
 * process_output() are initialised; everything else is zeroed.
 */
static struct hdcp_workqueue *alloc_test_workqueue(struct kunit *test)
{
        struct hdcp_workqueue *work;

        work = kunit_kzalloc(test, sizeof(*work), GFP_KERNEL);
        KUNIT_ASSERT_NOT_NULL(test, work);

        INIT_DELAYED_WORK(&work->callback_dwork, dummy_work_fn);
        INIT_DELAYED_WORK(&work->watchdog_timer_dwork, dummy_work_fn);
        INIT_DELAYED_WORK(&work->property_validate_dwork, dummy_work_fn);

        return work;
}

/*
 * process_output() always schedules property_validate_dwork with delay=0,
 * which queues the work item directly (bypassing the timer).  Use
 * work_pending() rather than delayed_work_pending() to detect this.
 */
static void dm_test_process_output_property_validate_always_scheduled(struct kunit *test)
{
        struct hdcp_workqueue *work = alloc_test_workqueue(test);

        /* No flags set: only property_validate_dwork should be enqueued */
        process_output(work);

        KUNIT_EXPECT_TRUE(test, work_pending(&work->property_validate_dwork.work));
        KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->callback_dwork));
        KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->watchdog_timer_dwork));

        cancel_delayed_work_sync(&work->property_validate_dwork);
}

/*
 * output.callback_needed=true must schedule callback_dwork.
 */
static void dm_test_process_output_callback_needed(struct kunit *test)
{
        struct hdcp_workqueue *work = alloc_test_workqueue(test);

        work->output.callback_needed = true;
        work->output.callback_delay = 500;

        process_output(work);

        KUNIT_EXPECT_TRUE(test, delayed_work_pending(&work->callback_dwork));

        cancel_delayed_work_sync(&work->callback_dwork);
        cancel_delayed_work_sync(&work->property_validate_dwork);
}

/*
 * output.callback_stop=true must cancel a previously scheduled callback_dwork.
 */
static void dm_test_process_output_callback_stop(struct kunit *test)
{
        struct hdcp_workqueue *work = alloc_test_workqueue(test);

        /* Pre-schedule callback_dwork with a long delay so it won't fire. */
        schedule_delayed_work(&work->callback_dwork, msecs_to_jiffies(10000));
        KUNIT_ASSERT_TRUE(test, delayed_work_pending(&work->callback_dwork));

        work->output.callback_stop = true;

        process_output(work);

        KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->callback_dwork));

        cancel_delayed_work_sync(&work->property_validate_dwork);
}

/*
 * output.watchdog_timer_needed=true must schedule watchdog_timer_dwork.
 */
static void dm_test_process_output_watchdog_needed(struct kunit *test)
{
        struct hdcp_workqueue *work = alloc_test_workqueue(test);

        work->output.watchdog_timer_needed = true;
        work->output.watchdog_timer_delay = 1000;

        process_output(work);

        KUNIT_EXPECT_TRUE(test, delayed_work_pending(&work->watchdog_timer_dwork));

        cancel_delayed_work_sync(&work->watchdog_timer_dwork);
        cancel_delayed_work_sync(&work->property_validate_dwork);
}

/*
 * output.watchdog_timer_stop=true must cancel a previously scheduled
 * watchdog_timer_dwork.
 */
static void dm_test_process_output_watchdog_stop(struct kunit *test)
{
        struct hdcp_workqueue *work = alloc_test_workqueue(test);

        /* Pre-schedule watchdog_timer_dwork with a long delay. */
        schedule_delayed_work(&work->watchdog_timer_dwork, msecs_to_jiffies(10000));
        KUNIT_ASSERT_TRUE(test, delayed_work_pending(&work->watchdog_timer_dwork));

        work->output.watchdog_timer_stop = true;

        process_output(work);

        KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->watchdog_timer_dwork));

        cancel_delayed_work_sync(&work->property_validate_dwork);
}

/*
 * Both callback_needed and watchdog_timer_needed set: both dworks are
 * scheduled independently.
 */
static void dm_test_process_output_callback_and_watchdog_needed(struct kunit *test)
{
        struct hdcp_workqueue *work = alloc_test_workqueue(test);

        work->output.callback_needed = true;
        work->output.callback_delay = 200;
        work->output.watchdog_timer_needed = true;
        work->output.watchdog_timer_delay = 800;

        process_output(work);

        KUNIT_EXPECT_TRUE(test, delayed_work_pending(&work->callback_dwork));
        KUNIT_EXPECT_TRUE(test, delayed_work_pending(&work->watchdog_timer_dwork));

        cancel_delayed_work_sync(&work->callback_dwork);
        cancel_delayed_work_sync(&work->watchdog_timer_dwork);
        cancel_delayed_work_sync(&work->property_validate_dwork);
}
/* End of tests for process_output() */

static struct kunit_case dm_hdcp_test_cases[] = {
        KUNIT_CASE(dm_test_process_output_property_validate_always_scheduled),
        KUNIT_CASE(dm_test_process_output_callback_needed),
        KUNIT_CASE(dm_test_process_output_callback_stop),
        KUNIT_CASE(dm_test_process_output_watchdog_needed),
        KUNIT_CASE(dm_test_process_output_watchdog_stop),
        KUNIT_CASE(dm_test_process_output_callback_and_watchdog_needed),
        {}
};

static struct kunit_suite dm_hdcp_test_suite = {
        .name = "amdgpu_dm_hdcp",
        .test_cases = dm_hdcp_test_cases,
};

kunit_test_suite(dm_hdcp_test_suite);

MODULE_LICENSE("Dual MIT/GPL");
MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_hdcp");
MODULE_AUTHOR("AMD");