root/drivers/firmware/cirrus/test/cs_dsp_test_callbacks.c
// SPDX-License-Identifier: GPL-2.0-only
//
// KUnit tests for cs_dsp.
//
// Copyright (C) 2024 Cirrus Logic, Inc. and
//                    Cirrus Logic International Semiconductor Ltd.
//

#include <kunit/device.h>
#include <kunit/resource.h>
#include <kunit/test.h>
#include <kunit/test-bug.h>
#include <linux/build_bug.h>
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/firmware/cirrus/cs_dsp_test_utils.h>
#include <linux/firmware/cirrus/wmfw.h>
#include <linux/random.h>
#include <linux/regmap.h>
#include <linux/string.h>
#include <linux/vmalloc.h>

#define ADSP2_LOCK_REGION_CTRL               0x7A
#define ADSP2_WDT_TIMEOUT_STS_MASK           0x2000

KUNIT_DEFINE_ACTION_WRAPPER(_put_device_wrapper, put_device, struct device *)
KUNIT_DEFINE_ACTION_WRAPPER(_cs_dsp_remove_wrapper, cs_dsp_remove, struct cs_dsp *)

struct cs_dsp_test_local {
        struct cs_dsp_mock_wmfw_builder *wmfw_builder;

        int num_control_add;
        int num_control_remove;
        int num_pre_run;
        int num_post_run;
        int num_pre_stop;
        int num_post_stop;
        int num_watchdog_expired;

        struct cs_dsp_coeff_ctl *passed_ctl[16];
        struct cs_dsp *passed_dsp;
};

struct cs_dsp_callbacks_test_param {
        const struct cs_dsp_client_ops *ops;
        const char *case_name;
};

static const struct cs_dsp_mock_alg_def cs_dsp_callbacks_test_mock_algs[] = {
        {
                .id = 0xfafa,
                .ver = 0x100000,
                .xm_size_words = 164,
                .ym_size_words = 164,
                .zm_size_words = 164,
        },
};

static const struct cs_dsp_mock_coeff_def mock_coeff_template = {
        .shortname = "Dummy Coeff",
        .type = WMFW_CTL_TYPE_BYTES,
        .mem_type = WMFW_ADSP2_YM,
        .flags = WMFW_CTL_FLAG_VOLATILE,
        .length_bytes = 4,
};

static int cs_dsp_test_control_add_callback(struct cs_dsp_coeff_ctl *ctl)
{
        struct kunit *test = kunit_get_current_test();
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;

        local->passed_ctl[local->num_control_add] = ctl;
        local->num_control_add++;

        return 0;
}

static void cs_dsp_test_control_remove_callback(struct cs_dsp_coeff_ctl *ctl)
{
        struct kunit *test = kunit_get_current_test();
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;

        local->passed_ctl[local->num_control_remove] = ctl;
        local->num_control_remove++;
}

static int cs_dsp_test_pre_run_callback(struct cs_dsp *dsp)
{
        struct kunit *test = kunit_get_current_test();
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;

        local->passed_dsp = dsp;
        local->num_pre_run++;

        return 0;
}

static int cs_dsp_test_post_run_callback(struct cs_dsp *dsp)
{
        struct kunit *test = kunit_get_current_test();
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;

        local->passed_dsp = dsp;
        local->num_post_run++;

        return 0;
}

static void cs_dsp_test_pre_stop_callback(struct cs_dsp *dsp)
{
        struct kunit *test = kunit_get_current_test();
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;

        local->passed_dsp = dsp;
        local->num_pre_stop++;
}

static void cs_dsp_test_post_stop_callback(struct cs_dsp *dsp)
{
        struct kunit *test = kunit_get_current_test();
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;

        local->passed_dsp = dsp;
        local->num_post_stop++;
}

static void cs_dsp_test_watchdog_expired_callback(struct cs_dsp *dsp)
{
        struct kunit *test = kunit_get_current_test();
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;

        local->passed_dsp = dsp;
        local->num_watchdog_expired++;
}

static const struct cs_dsp_client_ops cs_dsp_callback_test_client_ops = {
        .control_add = cs_dsp_test_control_add_callback,
        .control_remove = cs_dsp_test_control_remove_callback,
        .pre_run = cs_dsp_test_pre_run_callback,
        .post_run = cs_dsp_test_post_run_callback,
        .pre_stop = cs_dsp_test_pre_stop_callback,
        .post_stop = cs_dsp_test_post_stop_callback,
        .watchdog_expired = cs_dsp_test_watchdog_expired_callback,
};

static const struct cs_dsp_client_ops cs_dsp_callback_test_empty_client_ops = {
        /* No entries */
};

static void cs_dsp_test_run_stop_callbacks(struct kunit *test)
{
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        struct firmware *wmfw;

        wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);

        KUNIT_EXPECT_EQ(test,
                        cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
                        0);

        KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
        KUNIT_EXPECT_EQ(test, local->num_pre_run, 1);
        KUNIT_EXPECT_EQ(test, local->num_post_run, 1);
        KUNIT_EXPECT_EQ(test, local->num_pre_stop, 0);
        KUNIT_EXPECT_EQ(test, local->num_post_stop, 0);
        KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
        local->passed_dsp = NULL;

        cs_dsp_stop(priv->dsp);
        KUNIT_EXPECT_EQ(test, local->num_pre_run, 1);
        KUNIT_EXPECT_EQ(test, local->num_post_run, 1);
        KUNIT_EXPECT_EQ(test, local->num_pre_stop, 1);
        KUNIT_EXPECT_EQ(test, local->num_post_stop, 1);
        KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
        local->passed_dsp = NULL;

        KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
        KUNIT_EXPECT_EQ(test, local->num_pre_run, 2);
        KUNIT_EXPECT_EQ(test, local->num_post_run, 2);
        KUNIT_EXPECT_EQ(test, local->num_pre_stop, 1);
        KUNIT_EXPECT_EQ(test, local->num_post_stop, 1);
        KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
        local->passed_dsp = NULL;

        cs_dsp_stop(priv->dsp);
        KUNIT_EXPECT_EQ(test, local->num_pre_run, 2);
        KUNIT_EXPECT_EQ(test, local->num_post_run, 2);
        KUNIT_EXPECT_EQ(test, local->num_pre_stop, 2);
        KUNIT_EXPECT_EQ(test, local->num_post_stop, 2);
        KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
        local->passed_dsp = NULL;
}

static void cs_dsp_test_ctl_v1_callbacks(struct kunit *test)
{
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        struct cs_dsp_mock_coeff_def def = mock_coeff_template;
        struct cs_dsp_coeff_ctl *ctl;
        struct firmware *wmfw;
        int i;

        /* Add a control for each memory */
        cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
                                              cs_dsp_callbacks_test_mock_algs[0].id,
                                              "dummyalg", NULL);
        def.shortname = "zm";
        def.mem_type = WMFW_ADSP2_ZM;
        cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);

        def.shortname = "ym";
        def.mem_type = WMFW_ADSP2_YM;
        cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);

        def.shortname = "xm";
        def.mem_type = WMFW_ADSP2_XM;
        cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);

        cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

        wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
        KUNIT_EXPECT_EQ(test,
                        cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
                        0);

        /* There should have been an add callback for each control */
        KUNIT_EXPECT_EQ(test, list_count_nodes(&priv->dsp->ctl_list), 3);
        KUNIT_EXPECT_EQ(test, local->num_control_add, 3);
        KUNIT_EXPECT_EQ(test, local->num_control_remove, 0);

        i = 0;
        list_for_each_entry_reverse(ctl, &priv->dsp->ctl_list, list)
                KUNIT_EXPECT_PTR_EQ(test, local->passed_ctl[i++], ctl);

        /*
         * Call cs_dsp_remove() and there should be a remove callback
         * for each control
         */
        memset(local->passed_ctl, 0, sizeof(local->passed_ctl));
        cs_dsp_remove(priv->dsp);

        /* Prevent double cleanup */
        kunit_remove_action(priv->test, _cs_dsp_remove_wrapper, priv->dsp);

        KUNIT_EXPECT_EQ(test, local->num_control_add, 3);
        KUNIT_EXPECT_EQ(test, local->num_control_remove, 3);

        i = 0;
        list_for_each_entry_reverse(ctl, &priv->dsp->ctl_list, list)
                KUNIT_EXPECT_PTR_EQ(test, local->passed_ctl[i++], ctl);
}

static void cs_dsp_test_ctl_v2_callbacks(struct kunit *test)
{
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        struct cs_dsp_mock_coeff_def def = mock_coeff_template;
        struct cs_dsp_coeff_ctl *ctl;
        struct firmware *wmfw;
        char name[2] = { };
        int i;

        /* Add some controls */
        def.shortname = name;
        cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
                                              cs_dsp_callbacks_test_mock_algs[0].id,
                                              "dummyalg", NULL);
        for (i = 0; i < ARRAY_SIZE(local->passed_ctl); ++i) {
                name[0] = 'A' + i;
                def.offset_dsp_words = i;
                cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
        }
        cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

        wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
        KUNIT_EXPECT_EQ(test,
                        cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
                        0);

        /* There should have been an add callback for each control */
        KUNIT_EXPECT_EQ(test, list_count_nodes(&priv->dsp->ctl_list),
                        ARRAY_SIZE(local->passed_ctl));
        KUNIT_EXPECT_EQ(test, local->num_control_add, ARRAY_SIZE(local->passed_ctl));
        KUNIT_EXPECT_EQ(test, local->num_control_remove, 0);

        i = 0;
        list_for_each_entry_reverse(ctl, &priv->dsp->ctl_list, list)
                KUNIT_EXPECT_PTR_EQ(test, local->passed_ctl[i++], ctl);

        /*
         * Call cs_dsp_remove() and there should be a remove callback
         * for each control
         */
        memset(local->passed_ctl, 0, sizeof(local->passed_ctl));
        cs_dsp_remove(priv->dsp);

        /* Prevent double cleanup */
        kunit_remove_action(priv->test, _cs_dsp_remove_wrapper, priv->dsp);

        KUNIT_EXPECT_EQ(test, local->num_control_add, ARRAY_SIZE(local->passed_ctl));
        KUNIT_EXPECT_EQ(test, local->num_control_remove, ARRAY_SIZE(local->passed_ctl));

        i = 0;
        list_for_each_entry_reverse(ctl, &priv->dsp->ctl_list, list)
                KUNIT_EXPECT_PTR_EQ(test, local->passed_ctl[i++], ctl);
}

static void cs_dsp_test_no_callbacks(struct kunit *test)
{
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        struct cs_dsp_mock_coeff_def def = mock_coeff_template;
        struct firmware *wmfw;

        /* Add a controls */
        def.shortname = "A";
        cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
                                              cs_dsp_callbacks_test_mock_algs[0].id,
                                              "dummyalg", NULL);
        cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
        cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

        wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);

        /* Run a sequence of ops that would invoke callbacks */
        KUNIT_EXPECT_EQ(test,
                        cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
                        0);
        KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
        cs_dsp_stop(priv->dsp);
        cs_dsp_remove(priv->dsp);

        /* Prevent double cleanup */
        kunit_remove_action(priv->test, _cs_dsp_remove_wrapper, priv->dsp);

        /* Something went very wrong if any of our callbacks were called */
        KUNIT_EXPECT_EQ(test, local->num_control_add, 0);
        KUNIT_EXPECT_EQ(test, local->num_control_remove, 0);
        KUNIT_EXPECT_EQ(test, local->num_pre_run, 0);
        KUNIT_EXPECT_EQ(test, local->num_post_run, 0);
        KUNIT_EXPECT_EQ(test, local->num_pre_stop, 0);
        KUNIT_EXPECT_EQ(test, local->num_post_stop, 0);
}

static void cs_dsp_test_adsp2v2_watchdog_callback(struct kunit *test)
{
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        struct firmware *wmfw;

        wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);

        KUNIT_EXPECT_EQ(test,
                        cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
                        0);

        KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);

        /* Set the watchdog timeout bit */
        regmap_write(priv->dsp->regmap, priv->dsp->base + ADSP2_LOCK_REGION_CTRL,
                     ADSP2_WDT_TIMEOUT_STS_MASK);

        /* Notify an interrupt and the watchdog callback should be called */
        cs_dsp_adsp2_bus_error(priv->dsp);
        KUNIT_EXPECT_EQ(test, local->num_watchdog_expired, 1);
        KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
}

static void cs_dsp_test_adsp2v2_watchdog_no_callbacks(struct kunit *test)
{
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        struct firmware *wmfw;

        wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
        KUNIT_EXPECT_EQ(test,
                        cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
                        0);
        KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);

        /* Set the watchdog timeout bit */
        regmap_write(priv->dsp->regmap, priv->dsp->base + ADSP2_LOCK_REGION_CTRL,
                     ADSP2_WDT_TIMEOUT_STS_MASK);

        /* Notify an interrupt, which will look for a watchdog callback */
        cs_dsp_adsp2_bus_error(priv->dsp);
        KUNIT_EXPECT_EQ(test, local->num_watchdog_expired, 0);
}

static void cs_dsp_test_halo_watchdog_callback(struct kunit *test)
{
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        struct firmware *wmfw;

        wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);

        KUNIT_EXPECT_EQ(test,
                        cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
                        0);

        KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);

        /* Notify an interrupt and the watchdog callback should be called */
        cs_dsp_halo_wdt_expire(priv->dsp);
        KUNIT_EXPECT_EQ(test, local->num_watchdog_expired, 1);
        KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
}

static void cs_dsp_test_halo_watchdog_no_callbacks(struct kunit *test)
{
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        struct firmware *wmfw;

        wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
        KUNIT_EXPECT_EQ(test,
                        cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
                        0);
        KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);

        /* Notify an interrupt, which will look for a watchdog callback */
        cs_dsp_halo_wdt_expire(priv->dsp);
        KUNIT_EXPECT_EQ(test, local->num_watchdog_expired, 0);
}

static int cs_dsp_callbacks_test_common_init(struct kunit *test, struct cs_dsp *dsp,
                                             int wmfw_version)
{
        const struct cs_dsp_callbacks_test_param *param = test->param_value;
        struct cs_dsp_test *priv;
        struct cs_dsp_test_local *local;
        struct device *test_dev;
        struct cs_dsp_mock_xm_header *xm_header;
        int ret;

        priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;

        local = kunit_kzalloc(test, sizeof(struct cs_dsp_test_local), GFP_KERNEL);
        if (!local)
                return -ENOMEM;

        priv->test = test;
        priv->dsp = dsp;
        test->priv = priv;
        priv->local = local;

        /* Create dummy struct device */
        test_dev = kunit_device_register(test, "cs_dsp_test_drv");
        if (IS_ERR(test_dev))
                return PTR_ERR(test_dev);

        dsp->dev = get_device(test_dev);
        if (!dsp->dev)
                return -ENODEV;

        ret = kunit_add_action_or_reset(test, _put_device_wrapper, dsp->dev);
        if (ret)
                return ret;

        dev_set_drvdata(dsp->dev, priv);

        /* Allocate regmap */
        ret = cs_dsp_mock_regmap_init(priv);
        if (ret)
                return ret;

        /*
         * There must always be a XM header with at least 1 algorithm,
         * so create a dummy one and pre-populate XM so the wmfw doesn't
         * have to contain an XM blob.
         */
        xm_header = cs_dsp_create_mock_xm_header(priv,
                                                 cs_dsp_callbacks_test_mock_algs,
                                                 ARRAY_SIZE(cs_dsp_callbacks_test_mock_algs));
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xm_header);
        cs_dsp_mock_xm_header_write_to_regmap(xm_header);

        local->wmfw_builder = cs_dsp_mock_wmfw_init(priv, wmfw_version);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, local->wmfw_builder);

        /* Add dummy XM header payload to wmfw */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        WMFW_ADSP2_XM, 0,
                                        xm_header->blob_data,
                                        xm_header->blob_size_bytes);

        /* Init cs_dsp */
        dsp->client_ops = param->ops;

        switch (dsp->type) {
        case WMFW_ADSP2:
                ret = cs_dsp_adsp2_init(dsp);
                break;
        case WMFW_HALO:
                ret = cs_dsp_halo_init(dsp);
                break;
        default:
                KUNIT_FAIL(test, "Untested DSP type %d\n", dsp->type);
                return -EINVAL;
        }

        if (ret)
                return ret;

        /* Automatically call cs_dsp_remove() when test case ends */
        return kunit_add_action_or_reset(priv->test, _cs_dsp_remove_wrapper, dsp);
}

static int cs_dsp_callbacks_test_halo_init(struct kunit *test)
{
        struct cs_dsp *dsp;

        /* Fill in cs_dsp and initialize */
        dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
        if (!dsp)
                return -ENOMEM;

        dsp->num = 1;
        dsp->type = WMFW_HALO;
        dsp->mem = cs_dsp_mock_halo_dsp1_regions;
        dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_halo_dsp1_region_sizes);
        dsp->base = cs_dsp_mock_halo_core_base;
        dsp->base_sysinfo = cs_dsp_mock_halo_sysinfo_base;

        return cs_dsp_callbacks_test_common_init(test, dsp, 3);
}

static int cs_dsp_callbacks_test_adsp2_32bit_init(struct kunit *test, int rev)
{
        struct cs_dsp *dsp;

        /* Fill in cs_dsp and initialize */
        dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
        if (!dsp)
                return -ENOMEM;

        dsp->num = 1;
        dsp->type = WMFW_ADSP2;
        dsp->rev = rev;
        dsp->mem = cs_dsp_mock_adsp2_32bit_dsp1_regions;
        dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_32bit_dsp1_region_sizes);
        dsp->base = cs_dsp_mock_adsp2_32bit_sysbase;

        return cs_dsp_callbacks_test_common_init(test, dsp, 2);
}

static int cs_dsp_callbacks_test_adsp2v2_32bit_init(struct kunit *test)
{
        return cs_dsp_callbacks_test_adsp2_32bit_init(test, 2);
}

static int cs_dsp_callbacks_test_adsp2v1_32bit_init(struct kunit *test)
{
        return cs_dsp_callbacks_test_adsp2_32bit_init(test, 1);
}

static int cs_dsp_callbacks_test_adsp2_16bit_init(struct kunit *test)
{
        struct cs_dsp *dsp;

        /* Fill in cs_dsp and initialize */
        dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
        if (!dsp)
                return -ENOMEM;

        dsp->num = 1;
        dsp->type = WMFW_ADSP2;
        dsp->rev = 0;
        dsp->mem = cs_dsp_mock_adsp2_16bit_dsp1_regions;
        dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_16bit_dsp1_region_sizes);
        dsp->base = cs_dsp_mock_adsp2_16bit_sysbase;

        return cs_dsp_callbacks_test_common_init(test, dsp, 1);
}

static void cs_dsp_callbacks_param_desc(const struct cs_dsp_callbacks_test_param *param,
                                        char *desc)
{
        snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s", param->case_name);
}

/* Parameterize on different client callback ops tables */
static const struct cs_dsp_callbacks_test_param cs_dsp_callbacks_ops_cases[] = {
        { .ops =  &cs_dsp_callback_test_client_ops, .case_name = "all ops" },
};

KUNIT_ARRAY_PARAM(cs_dsp_callbacks_ops,
                  cs_dsp_callbacks_ops_cases,
                  cs_dsp_callbacks_param_desc);

static const struct cs_dsp_callbacks_test_param cs_dsp_no_callbacks_cases[] = {
        { .ops =  &cs_dsp_callback_test_empty_client_ops, .case_name = "empty ops" },
        { .ops =  NULL, .case_name = "NULL ops" },
};

KUNIT_ARRAY_PARAM(cs_dsp_no_callbacks,
                  cs_dsp_no_callbacks_cases,
                  cs_dsp_callbacks_param_desc);

static struct kunit_case cs_dsp_callbacks_adsp2_wmfwv1_test_cases[] = {
        KUNIT_CASE_PARAM(cs_dsp_test_run_stop_callbacks, cs_dsp_callbacks_ops_gen_params),
        KUNIT_CASE_PARAM(cs_dsp_test_ctl_v1_callbacks, cs_dsp_callbacks_ops_gen_params),
        KUNIT_CASE_PARAM(cs_dsp_test_no_callbacks, cs_dsp_no_callbacks_gen_params),

        { } /* terminator */
};

static struct kunit_case cs_dsp_callbacks_adsp2_wmfwv2_test_cases[] = {
        KUNIT_CASE_PARAM(cs_dsp_test_run_stop_callbacks, cs_dsp_callbacks_ops_gen_params),
        KUNIT_CASE_PARAM(cs_dsp_test_ctl_v2_callbacks, cs_dsp_callbacks_ops_gen_params),
        KUNIT_CASE_PARAM(cs_dsp_test_no_callbacks, cs_dsp_no_callbacks_gen_params),

        { } /* terminator */
};

static struct kunit_case cs_dsp_callbacks_halo_test_cases[] = {
        KUNIT_CASE_PARAM(cs_dsp_test_run_stop_callbacks, cs_dsp_callbacks_ops_gen_params),
        KUNIT_CASE_PARAM(cs_dsp_test_ctl_v2_callbacks, cs_dsp_callbacks_ops_gen_params),
        KUNIT_CASE_PARAM(cs_dsp_test_no_callbacks, cs_dsp_no_callbacks_gen_params),

        { } /* terminator */
};

static struct kunit_case cs_dsp_watchdog_adsp2v2_test_cases[] = {
        KUNIT_CASE_PARAM(cs_dsp_test_adsp2v2_watchdog_callback, cs_dsp_callbacks_ops_gen_params),
        KUNIT_CASE_PARAM(cs_dsp_test_adsp2v2_watchdog_no_callbacks, cs_dsp_no_callbacks_gen_params),

        { } /* terminator */
};

static struct kunit_case cs_dsp_watchdog_halo_test_cases[] = {
        KUNIT_CASE_PARAM(cs_dsp_test_halo_watchdog_callback, cs_dsp_callbacks_ops_gen_params),
        KUNIT_CASE_PARAM(cs_dsp_test_halo_watchdog_no_callbacks, cs_dsp_no_callbacks_gen_params),

        { } /* terminator */
};

static struct kunit_suite cs_dsp_callbacks_test_halo = {
        .name = "cs_dsp_callbacks_halo",
        .init = cs_dsp_callbacks_test_halo_init,
        .test_cases = cs_dsp_callbacks_halo_test_cases,
};

static struct kunit_suite cs_dsp_callbacks_test_adsp2v2_32bit = {
        .name = "cs_dsp_callbacks_adsp2v2_32bit_wmfwv2",
        .init = cs_dsp_callbacks_test_adsp2v2_32bit_init,
        .test_cases = cs_dsp_callbacks_adsp2_wmfwv2_test_cases,
};

static struct kunit_suite cs_dsp_callbacks_test_adsp2v1_32bit = {
        .name = "cs_dsp_callbacks_adsp2v1_32bit_wmfwv2",
        .init = cs_dsp_callbacks_test_adsp2v1_32bit_init,
        .test_cases = cs_dsp_callbacks_adsp2_wmfwv2_test_cases,
};

static struct kunit_suite cs_dsp_callbacks_test_adsp2_16bit = {
        .name = "cs_dsp_callbacks_adsp2_16bit_wmfwv1",
        .init = cs_dsp_callbacks_test_adsp2_16bit_init,
        .test_cases = cs_dsp_callbacks_adsp2_wmfwv1_test_cases,
};

static struct kunit_suite cs_dsp_watchdog_test_adsp2v2_32bit = {
        .name = "cs_dsp_watchdog_adsp2v2_32bit",
        .init = cs_dsp_callbacks_test_adsp2v2_32bit_init,
        .test_cases = cs_dsp_watchdog_adsp2v2_test_cases,
};

static struct kunit_suite cs_dsp_watchdog_test_halo_32bit = {
        .name = "cs_dsp_watchdog_halo",
        .init = cs_dsp_callbacks_test_halo_init,
        .test_cases = cs_dsp_watchdog_halo_test_cases,
};

kunit_test_suites(&cs_dsp_callbacks_test_halo,
                  &cs_dsp_callbacks_test_adsp2v2_32bit,
                  &cs_dsp_callbacks_test_adsp2v1_32bit,
                  &cs_dsp_callbacks_test_adsp2_16bit,
                  &cs_dsp_watchdog_test_adsp2v2_32bit,
                  &cs_dsp_watchdog_test_halo_32bit);