root/drivers/firmware/cirrus/test/cs_dsp_test_wmfw.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 <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>

#include "../cs_dsp.h"

/*
 * Test method is:
 *
 * 1) Create a mock regmap in cache-only mode so that all writes will be cached.
 * 2) Create dummy wmfw file.
 * 3) Call cs_dsp_power_up() with the bin file.
 * 4) Readback the cached value of registers that should have been written and
 *    check they have the correct value.
 * 5) All the registers that are expected to have been written are dropped from
 *    the cache. This should leave the cache clean.
 * 6) If the cache is still dirty there have been unexpected writes.
 */

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

struct cs_dsp_test_local {
        struct cs_dsp_mock_xm_header *xm_header;
        struct cs_dsp_mock_wmfw_builder *wmfw_builder;
        int wmfw_version;
};

struct cs_dsp_wmfw_test_param {
        unsigned int num_blocks;
        int mem_type;
};

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

/*
 * wmfw that writes the XM header.
 * cs_dsp always reads this back from unpacked XM.
 */
static void wmfw_write_xm_header_unpacked(struct kunit *test)
{
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        struct firmware *wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
        unsigned int reg_addr;
        u8 *readback;

        /* XM header payload was added to wmfw by test case init function */

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

        /* Read raw so endianness and register width don't matter */
        readback = kunit_kzalloc(test, local->xm_header->blob_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);

        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_XM);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        local->xm_header->blob_size_bytes),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, local->xm_header->blob_data,
                           local->xm_header->blob_size_bytes);

        /* Drop expected writes and the cache should then be clean */
        cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
        KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/* Write one payload of length param->num_blocks */
static void wmfw_write_one_payload(struct kunit *test)
{
        const struct cs_dsp_wmfw_test_param *param = test->param_value;
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        struct firmware *wmfw;
        unsigned int reg_addr;
        u8 *payload_data, *readback;
        unsigned int mem_offset_dsp_words = 0;
        unsigned int payload_size_bytes;

        payload_size_bytes = param->num_blocks *
                             cs_dsp_mock_reg_block_length_bytes(priv, param->mem_type);

        /* payloads must be a multiple of 4 bytes and a whole number of DSP registers */
        do {
                payload_size_bytes += cs_dsp_mock_reg_block_length_bytes(priv, param->mem_type);
        } while (payload_size_bytes % 4);

        payload_data = kunit_kmalloc(test, payload_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, payload_data);
        get_random_bytes(payload_data, payload_size_bytes);

        readback = kunit_kzalloc(test, payload_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);

        /* Tests on XM must be after the XM header */
        if (param->mem_type == WMFW_ADSP2_XM)
                mem_offset_dsp_words += local->xm_header->blob_size_bytes / sizeof(u32);

        /* Add a single payload */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        param->mem_type, mem_offset_dsp_words,
                                        payload_data, payload_size_bytes);

        wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);

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

        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
        reg_addr += cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv) * mem_offset_dsp_words;
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback, payload_size_bytes),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, payload_data, payload_size_bytes);

        /* Drop expected writes and the cache should then be clean */
        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, payload_size_bytes);
        cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
        KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/* Write several smallest possible payloads for the given memory type */
static void wmfw_write_multiple_oneblock_payloads(struct kunit *test)
{
        const struct cs_dsp_wmfw_test_param *param = test->param_value;
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        struct firmware *wmfw;
        unsigned int reg_addr;
        u8 *payload_data, *readback;
        unsigned int mem_offset_dsp_words = 0;
        unsigned int payload_size_bytes, payload_size_dsp_words;
        const unsigned int num_payloads = param->num_blocks;
        int i;

        /* payloads must be a multiple of 4 bytes and a whole number of DSP registers */
        payload_size_dsp_words = 0;
        payload_size_bytes = 0;
        do {
                payload_size_dsp_words += cs_dsp_mock_reg_block_length_dsp_words(priv,
                                                                                 param->mem_type);
                payload_size_bytes += cs_dsp_mock_reg_block_length_bytes(priv, param->mem_type);
        } while (payload_size_bytes % 4);

        payload_data = kunit_kcalloc(test, num_payloads, payload_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, payload_data);

        readback = kunit_kcalloc(test, num_payloads, payload_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);

        get_random_bytes(payload_data, num_payloads * payload_size_bytes);

        /* Tests on XM must be after the XM header */
        if (param->mem_type == WMFW_ADSP2_XM)
                mem_offset_dsp_words += local->xm_header->blob_size_bytes / payload_size_bytes;

        /* Add multiple payloads of one block each */
        for (i = 0; i < num_payloads; ++i) {
                cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                                param->mem_type,
                                                mem_offset_dsp_words + (i * payload_size_dsp_words),
                                                &payload_data[i * payload_size_bytes],
                                                payload_size_bytes);
        }

        wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);

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

        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
        reg_addr += cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv) * mem_offset_dsp_words;
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        num_payloads * payload_size_bytes),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, payload_data, num_payloads * payload_size_bytes);

        /* Drop expected writes and the cache should then be clean */
        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, num_payloads * payload_size_bytes);
        cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
        KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Write several smallest possible payloads of the given memory type
 * in reverse address order
 */
static void wmfw_write_multiple_oneblock_payloads_reverse(struct kunit *test)
{
        const struct cs_dsp_wmfw_test_param *param = test->param_value;
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        struct firmware *wmfw;
        unsigned int reg_addr;
        u8 *payload_data, *readback;
        unsigned int mem_offset_dsp_words = 0;
        unsigned int payload_size_bytes, payload_size_dsp_words;
        const unsigned int num_payloads = param->num_blocks;
        int i;

        /* payloads must be a multiple of 4 bytes and a whole number of DSP registers */
        payload_size_dsp_words = 0;
        payload_size_bytes = 0;
        do {
                payload_size_dsp_words += cs_dsp_mock_reg_block_length_dsp_words(priv,
                                                                                 param->mem_type);
                payload_size_bytes += cs_dsp_mock_reg_block_length_bytes(priv, param->mem_type);
        } while (payload_size_bytes % 4);

        payload_data = kunit_kcalloc(test, num_payloads, payload_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, payload_data);

        readback = kunit_kcalloc(test, num_payloads, payload_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);

        get_random_bytes(payload_data, num_payloads * payload_size_bytes);

        /* Tests on XM must be after the XM header */
        if (param->mem_type == WMFW_ADSP2_XM)
                mem_offset_dsp_words += local->xm_header->blob_size_bytes / payload_size_bytes;

        /* Add multiple payloads of one block each */
        for (i = num_payloads - 1; i >= 0; --i) {
                cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                                param->mem_type,
                                                mem_offset_dsp_words + (i * payload_size_dsp_words),
                                                &payload_data[i * payload_size_bytes],
                                                payload_size_bytes);
        }

        wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);

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

        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
        reg_addr += cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv) * mem_offset_dsp_words;
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        num_payloads * payload_size_bytes),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, payload_data, num_payloads * payload_size_bytes);

        /* Drop expected writes and the cache should then be clean */
        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, num_payloads * payload_size_bytes);
        cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
        KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Write multiple payloads of length param->num_blocks.
 * The payloads are not in address order and collectively do not patch
 * a contiguous block of memory.
 */
static void wmfw_write_multiple_payloads_sparse_unordered(struct kunit *test)
{
        static const unsigned int random_offsets[] = {
                11, 69, 59, 61, 32, 75, 4, 38, 70, 13, 79, 47, 46, 53, 18, 44,
                54, 35, 51, 21, 26, 45, 27, 41, 66, 2, 17, 56, 40, 9, 8, 20,
                29, 19, 63, 42, 12, 16, 43, 3, 5, 55, 52, 22
        };
        const struct cs_dsp_wmfw_test_param *param = test->param_value;
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        struct firmware *wmfw;
        unsigned int reg_addr;
        u8 *payload_data, *readback;
        unsigned int mem_offset_dsp_words = 0;
        unsigned int payload_size_bytes, payload_size_dsp_words;
        const int num_payloads = ARRAY_SIZE(random_offsets);
        int i;

        payload_size_bytes = param->num_blocks *
                             cs_dsp_mock_reg_block_length_bytes(priv, param->mem_type);
        payload_size_dsp_words = param->num_blocks *
                                 cs_dsp_mock_reg_block_length_dsp_words(priv, param->mem_type);

        /* payloads must be a multiple of 4 bytes and a whole number of DSP registers */
        do {
                payload_size_dsp_words += cs_dsp_mock_reg_block_length_dsp_words(priv,
                                                                                 param->mem_type);
                payload_size_bytes += cs_dsp_mock_reg_block_length_bytes(priv, param->mem_type);
        } while (payload_size_bytes % 4);

        payload_data = kunit_kcalloc(test, num_payloads, payload_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, payload_data);
        get_random_bytes(payload_data, payload_size_bytes);

        readback = kunit_kcalloc(test, num_payloads, payload_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);

        /* Tests on XM must be after the XM header */
        if (param->mem_type == WMFW_ADSP2_XM)
                mem_offset_dsp_words += local->xm_header->blob_size_bytes / payload_size_bytes;

        /* Add multiple payloads of one block each at "random" locations */
        for (i = 0; i < num_payloads; ++i) {
                unsigned int offset = random_offsets[i] * payload_size_dsp_words;

                cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                                param->mem_type,
                                                mem_offset_dsp_words + offset,
                                                &payload_data[i * payload_size_bytes],
                                                payload_size_bytes);
        }

        wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);

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

        for (i = 0; i < num_payloads; ++i) {
                unsigned int offset_num_regs = (random_offsets[i] * payload_size_bytes) /
                                                regmap_get_val_bytes(priv->dsp->regmap);
                reg_addr = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
                reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
                reg_addr += cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv) * mem_offset_dsp_words;
                KUNIT_EXPECT_EQ(test,
                                regmap_raw_read(priv->dsp->regmap, reg_addr,
                                                &readback[i * payload_size_bytes],
                                                payload_size_bytes),
                                0);

                cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, payload_size_bytes);
        }

        KUNIT_EXPECT_MEMEQ(test, readback, payload_data, payload_size_bytes);

        /* Drop expected writes and the cache should then be clean */
        cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
        KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/* Write the whole of PM in a single unpacked payload */
static void wmfw_write_all_unpacked_pm(struct kunit *test)
{
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        struct firmware *wmfw;
        unsigned int reg_addr;
        u8 *payload_data, *readback;
        unsigned int payload_size_bytes;

        payload_size_bytes = cs_dsp_mock_size_of_region(priv->dsp, WMFW_ADSP2_PM);
        payload_data = vmalloc(payload_size_bytes);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, payload_data);
        kunit_add_action_or_reset(priv->test, _vfree_wrapper, payload_data);

        readback = vmalloc(payload_size_bytes);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);
        kunit_add_action_or_reset(priv->test, _vfree_wrapper, readback);
        memset(readback, 0, payload_size_bytes);

        /* Add a single PM payload */
        get_random_bytes(payload_data, payload_size_bytes);
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        WMFW_ADSP2_PM, 0,
                                        payload_data, payload_size_bytes);

        wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);

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

        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_PM);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback, payload_size_bytes),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, payload_data, payload_size_bytes);

        /* Drop expected writes and the cache should then be clean */
        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, payload_size_bytes);
        cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
        KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/* Write the whole of PM in a single packed payload */
static void wmfw_write_all_packed_pm(struct kunit *test)
{
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        struct firmware *wmfw;
        unsigned int reg_addr;
        u8 *payload_data, *readback;
        unsigned int payload_size_bytes;

        payload_size_bytes = cs_dsp_mock_size_of_region(priv->dsp, WMFW_HALO_PM_PACKED);
        payload_data = vmalloc(payload_size_bytes);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, payload_data);
        kunit_add_action_or_reset(priv->test, _vfree_wrapper, payload_data);

        readback = vmalloc(payload_size_bytes);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);
        kunit_add_action_or_reset(priv->test, _vfree_wrapper, readback);
        memset(readback, 0, payload_size_bytes);

        /* Add a single PM payload */
        get_random_bytes(payload_data, payload_size_bytes);
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        WMFW_HALO_PM_PACKED, 0,
                                        payload_data, payload_size_bytes);

        wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);

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

        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, WMFW_HALO_PM_PACKED);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback, payload_size_bytes),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, payload_data, payload_size_bytes);

        /* Drop expected writes and the cache should then be clean */
        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, payload_size_bytes);
        cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
        KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Write a series of payloads to various unpacked memory regions.
 * The payloads are of various lengths and offsets, driven by the
 * payload_defs table. The offset and length are both given as a
 * number of minimum-sized register blocks to keep the maths simpler.
 * (Where a minimum-sized register block is the smallest number of
 * registers that contain a whole number of DSP words.)
 */
static void wmfw_write_multiple_unpacked_mem(struct kunit *test)
{
        static const struct {
                int mem_type;
                unsigned int offset_num_blocks;
                unsigned int num_blocks;
        } payload_defs[] = {
                { WMFW_ADSP2_PM, 11, 60 },
                { WMFW_ADSP2_ZM, 69, 8 },
                { WMFW_ADSP2_YM, 32, 74 },
                { WMFW_ADSP2_XM, 70, 38 },
                { WMFW_ADSP2_PM, 84, 48 },
                { WMFW_ADSP2_XM, 46, 18 },
                { WMFW_ADSP2_PM, 0,  8 },
                { WMFW_ADSP2_YM, 0, 30 },
                { WMFW_ADSP2_PM, 160, 50 },
                { WMFW_ADSP2_ZM, 21, 26 },
        };
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        struct firmware *wmfw;
        unsigned int payload_size_bytes, offset_num_dsp_words;
        unsigned int reg_addr, offset_bytes, offset_num_regs;
        void **payload_data;
        void *readback;
        int i, ret;

        payload_data = kunit_kcalloc(test, ARRAY_SIZE(payload_defs), sizeof(*payload_data),
                                     GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, payload_data);

        for (i = 0; i < ARRAY_SIZE(payload_defs); ++i) {
                payload_size_bytes = payload_defs[i].num_blocks *
                                     cs_dsp_mock_reg_block_length_bytes(priv,
                                                                        payload_defs[i].mem_type);

                payload_data[i] = kunit_kmalloc(test, payload_size_bytes, GFP_KERNEL);
                KUNIT_ASSERT_NOT_ERR_OR_NULL(test, payload_data[i]);
                get_random_bytes(payload_data[i], payload_size_bytes);

                offset_num_dsp_words = payload_defs[i].offset_num_blocks *
                                       cs_dsp_mock_reg_block_length_dsp_words(priv,
                                                                         payload_defs[i].mem_type);
                cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                                payload_defs[i].mem_type,
                                                offset_num_dsp_words,
                                                payload_data[i],
                                                payload_size_bytes);
        }

        wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);

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

        for (i = 0; i < ARRAY_SIZE(payload_defs); ++i) {
                payload_size_bytes = payload_defs[i].num_blocks *
                                     cs_dsp_mock_reg_block_length_bytes(priv,
                                                                        payload_defs[i].mem_type);

                readback = kunit_kzalloc(test, payload_size_bytes, GFP_KERNEL);
                KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);

                offset_bytes = payload_defs[i].offset_num_blocks *
                               cs_dsp_mock_reg_block_length_bytes(priv, payload_defs[i].mem_type);
                offset_num_regs = offset_bytes / regmap_get_val_bytes(priv->dsp->regmap);
                reg_addr = cs_dsp_mock_base_addr_for_mem(priv, payload_defs[i].mem_type);
                reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
                ret = regmap_raw_read(priv->dsp->regmap, reg_addr, readback, payload_size_bytes);
                KUNIT_EXPECT_EQ_MSG(test, ret, 0, "%s @%u num:%u\n",
                                    cs_dsp_mem_region_name(payload_defs[i].mem_type),
                                    payload_defs[i].offset_num_blocks, payload_defs[i].num_blocks);
                KUNIT_EXPECT_MEMEQ_MSG(test, readback, payload_data[i], payload_size_bytes,
                                       "%s @%u num:%u\n",
                                       cs_dsp_mem_region_name(payload_defs[i].mem_type),
                                       payload_defs[i].offset_num_blocks,
                                       payload_defs[i].num_blocks);

                kunit_kfree(test, readback);

                cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, payload_size_bytes);
        }

        /* Drop expected writes and the cache should then be clean */
        cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
        KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Write a series of payloads to various packed and unpacked memory regions.
 * The payloads are of various lengths and offsets, driven by the
 * payload_defs table. The offset and length are both given as a
 * number of minimum-sized register blocks to keep the maths simpler.
 * (Where a minimum-sized register block is the smallest number of
 * registers that contain a whole number of DSP words.)
 */
static void wmfw_write_multiple_packed_unpacked_mem(struct kunit *test)
{
        static const struct {
                int mem_type;
                unsigned int offset_num_blocks;
                unsigned int num_blocks;
        } payload_defs[] = {
                { WMFW_HALO_PM_PACKED,  11, 60 },
                { WMFW_ADSP2_YM,        69, 8 },
                { WMFW_HALO_YM_PACKED,  32, 74 },
                { WMFW_HALO_XM_PACKED,  70, 38 },
                { WMFW_HALO_PM_PACKED,  84, 48 },
                { WMFW_HALO_XM_PACKED,  46, 18 },
                { WMFW_HALO_PM_PACKED,  0,  8 },
                { WMFW_HALO_YM_PACKED,  0, 30 },
                { WMFW_HALO_PM_PACKED,  160, 50 },
                { WMFW_ADSP2_XM,        21, 26 },
        };
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        struct firmware *wmfw;
        unsigned int payload_size_bytes, offset_num_dsp_words;
        unsigned int reg_addr, offset_bytes, offset_num_regs;
        void **payload_data;
        void *readback;
        int i, ret;

        payload_data = kunit_kcalloc(test, ARRAY_SIZE(payload_defs), sizeof(*payload_data),
                                     GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, payload_data);

        for (i = 0; i < ARRAY_SIZE(payload_defs); ++i) {
                payload_size_bytes = payload_defs[i].num_blocks *
                                     cs_dsp_mock_reg_block_length_bytes(priv,
                                                                        payload_defs[i].mem_type);

                payload_data[i] = kunit_kmalloc(test, payload_size_bytes, GFP_KERNEL);
                KUNIT_ASSERT_NOT_ERR_OR_NULL(test, payload_data[i]);
                get_random_bytes(payload_data[i], payload_size_bytes);

                offset_num_dsp_words = payload_defs[i].offset_num_blocks *
                                       cs_dsp_mock_reg_block_length_dsp_words(priv,
                                                                         payload_defs[i].mem_type);
                cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                                payload_defs[i].mem_type,
                                                offset_num_dsp_words,
                                                payload_data[i],
                                                payload_size_bytes);
        }

        wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);

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

        for (i = 0; i < ARRAY_SIZE(payload_defs); ++i) {
                payload_size_bytes = payload_defs[i].num_blocks *
                                     cs_dsp_mock_reg_block_length_bytes(priv,
                                                                        payload_defs[i].mem_type);

                readback = kunit_kzalloc(test, payload_size_bytes, GFP_KERNEL);
                KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);

                offset_bytes = payload_defs[i].offset_num_blocks *
                               cs_dsp_mock_reg_block_length_bytes(priv, payload_defs[i].mem_type);
                offset_num_regs = offset_bytes / regmap_get_val_bytes(priv->dsp->regmap);
                reg_addr = cs_dsp_mock_base_addr_for_mem(priv, payload_defs[i].mem_type);
                reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
                ret = regmap_raw_read(priv->dsp->regmap, reg_addr, readback, payload_size_bytes);
                KUNIT_EXPECT_EQ_MSG(test, ret, 0, "%s @%u num:%u\n",
                                    cs_dsp_mem_region_name(payload_defs[i].mem_type),
                                    payload_defs[i].offset_num_blocks,
                                    payload_defs[i].num_blocks);
                KUNIT_EXPECT_MEMEQ_MSG(test, readback, payload_data[i], payload_size_bytes,
                                       "%s @%u num:%u\n",
                                       cs_dsp_mem_region_name(payload_defs[i].mem_type),
                                       payload_defs[i].offset_num_blocks,
                                       payload_defs[i].num_blocks);

                kunit_kfree(test, readback);

                cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, payload_size_bytes);
        }

        /* Drop expected writes and the cache should then be clean */
        cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
        KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Write XM/YM data that is one word longer than a packed block multiple,
 * using one packed payload followed by one unpacked word.
 */
static void wmfw_write_packed_1_unpacked_trailing(struct kunit *test)
{
        const struct cs_dsp_wmfw_test_param *param = test->param_value;
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        int packed_mem_type = param->mem_type;
        int unpacked_mem_type = cs_dsp_mock_packed_to_unpacked_mem_type(param->mem_type);
        unsigned int dsp_words_per_packed_block =
                cs_dsp_mock_reg_block_length_dsp_words(priv, packed_mem_type);
        unsigned int dsp_words_per_unpacked_block =
                cs_dsp_mock_reg_block_length_dsp_words(priv, unpacked_mem_type);
        unsigned int mem_offset_dsp_words = 0;
        struct firmware *wmfw;
        unsigned int reg_addr;
        void *packed_payload_data, *readback;
        u32 unpacked_payload_data[1];
        unsigned int packed_payload_size_bytes, packed_payload_size_dsp_words;
        unsigned int offset_num_regs;

        packed_payload_size_bytes = param->num_blocks *
                                    cs_dsp_mock_reg_block_length_bytes(priv, packed_mem_type);
        packed_payload_size_dsp_words = param->num_blocks * dsp_words_per_packed_block;

        packed_payload_data = kunit_kmalloc(test, packed_payload_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, packed_payload_data);
        get_random_bytes(packed_payload_data, packed_payload_size_bytes);

        get_random_bytes(unpacked_payload_data, sizeof(unpacked_payload_data));

        readback = kunit_kzalloc(test, packed_payload_size_bytes, GFP_KERNEL);

        /* Tests on XM must be after the XM header */
        if (unpacked_mem_type == WMFW_ADSP2_XM) {
                mem_offset_dsp_words += local->xm_header->blob_size_bytes / sizeof(u32);

                /* Round up to multiple of packed block length */
                mem_offset_dsp_words = roundup(mem_offset_dsp_words, dsp_words_per_packed_block);
        }

        /* Add a single packed payload */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        packed_mem_type, mem_offset_dsp_words,
                                        packed_payload_data, packed_payload_size_bytes);
        /*
         * Add payload of one unpacked word to DSP memory right after
         * the packed payload words.
         */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        unpacked_mem_type,
                                        mem_offset_dsp_words + packed_payload_size_dsp_words,
                                        unpacked_payload_data, sizeof(unpacked_payload_data));

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

        /*
         * Check that the packed payload was written correctly and drop
         * it from the regmap cache.
         */
        offset_num_regs = (mem_offset_dsp_words / dsp_words_per_packed_block) *
                          cs_dsp_mock_reg_block_length_registers(priv, packed_mem_type);
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, packed_mem_type);
        reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        packed_payload_size_bytes),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, packed_payload_data, packed_payload_size_bytes);

        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, packed_payload_size_bytes);

        /*
         * Check that the unpacked word was written correctly and drop
         * it from the regmap cache. The unpacked payload is offset within
         * unpacked register space by the number of DSP words that were
         * written in the packed payload.
         */
        offset_num_regs = (mem_offset_dsp_words / dsp_words_per_unpacked_block) *
                          cs_dsp_mock_reg_block_length_registers(priv, unpacked_mem_type);
        offset_num_regs += (packed_payload_size_dsp_words / dsp_words_per_unpacked_block) *
                           cs_dsp_mock_reg_block_length_registers(priv, unpacked_mem_type);
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, unpacked_mem_type);
        reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        sizeof(unpacked_payload_data)),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, unpacked_payload_data, sizeof(unpacked_payload_data));

        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, sizeof(unpacked_payload_data));

        /* Drop expected writes and the cache should then be clean */
        cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
        KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Write XM/YM data that is two words longer than a packed block multiple,
 * using one packed payload followed by one payload of two unpacked words.
 */
static void wmfw_write_packed_2_unpacked_trailing(struct kunit *test)
{
        const struct cs_dsp_wmfw_test_param *param = test->param_value;
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        int packed_mem_type = param->mem_type;
        int unpacked_mem_type = cs_dsp_mock_packed_to_unpacked_mem_type(param->mem_type);
        unsigned int dsp_words_per_packed_block =
                cs_dsp_mock_reg_block_length_dsp_words(priv, packed_mem_type);
        unsigned int dsp_words_per_unpacked_block =
                cs_dsp_mock_reg_block_length_dsp_words(priv, unpacked_mem_type);
        unsigned int mem_offset_dsp_words = 0;
        struct firmware *wmfw;
        unsigned int reg_addr;
        void *packed_payload_data, *readback;
        u32 unpacked_payload_data[2];
        unsigned int packed_payload_size_bytes, packed_payload_size_dsp_words;
        unsigned int offset_num_regs;

        packed_payload_size_bytes = param->num_blocks *
                                    cs_dsp_mock_reg_block_length_bytes(priv, packed_mem_type);
        packed_payload_size_dsp_words = param->num_blocks * dsp_words_per_packed_block;

        packed_payload_data = kunit_kmalloc(test, packed_payload_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, packed_payload_data);
        get_random_bytes(packed_payload_data, packed_payload_size_bytes);

        get_random_bytes(unpacked_payload_data, sizeof(unpacked_payload_data));

        readback = kunit_kzalloc(test, packed_payload_size_bytes, GFP_KERNEL);

        /* Tests on XM must be after the XM header */
        if (unpacked_mem_type == WMFW_ADSP2_XM) {
                mem_offset_dsp_words += local->xm_header->blob_size_bytes / sizeof(u32);

                /* Round up to multiple of packed block length */
                mem_offset_dsp_words = roundup(mem_offset_dsp_words, dsp_words_per_packed_block);
        }

        /* Add a single packed payload */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        packed_mem_type, mem_offset_dsp_words,
                                        packed_payload_data, packed_payload_size_bytes);
        /*
         * Add payload of two unpacked words to DSP memory right after
         * the packed payload words.
         */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        unpacked_mem_type,
                                        mem_offset_dsp_words + packed_payload_size_dsp_words,
                                        unpacked_payload_data, sizeof(unpacked_payload_data));

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

        /*
         * Check that the packed payload was written correctly and drop
         * it from the regmap cache.
         */
        offset_num_regs = (mem_offset_dsp_words / dsp_words_per_packed_block) *
                          cs_dsp_mock_reg_block_length_registers(priv, packed_mem_type);
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, packed_mem_type);
        reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        packed_payload_size_bytes),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, packed_payload_data, packed_payload_size_bytes);

        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, packed_payload_size_bytes);

        /*
         * Check that the unpacked words were written correctly and drop
         * them from the regmap cache. The unpacked payload is offset
         * within unpacked register space by the number of DSP words
         * that were written in the packed payload.
         */
        offset_num_regs = (mem_offset_dsp_words / dsp_words_per_unpacked_block) *
                          cs_dsp_mock_reg_block_length_registers(priv, unpacked_mem_type);
        offset_num_regs += (packed_payload_size_dsp_words / dsp_words_per_unpacked_block) *
                           cs_dsp_mock_reg_block_length_registers(priv, unpacked_mem_type);
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, unpacked_mem_type);
        reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        sizeof(unpacked_payload_data)),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, unpacked_payload_data, sizeof(unpacked_payload_data));

        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, sizeof(unpacked_payload_data));

        /* Drop expected writes and the cache should then be clean */
        cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
        KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Write XM/YM data that is three words longer than a packed block multiple,
 * using one packed payload followed by one payload of three unpacked words.
 */
static void wmfw_write_packed_3_unpacked_trailing(struct kunit *test)
{
        const struct cs_dsp_wmfw_test_param *param = test->param_value;
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        int packed_mem_type = param->mem_type;
        int unpacked_mem_type = cs_dsp_mock_packed_to_unpacked_mem_type(param->mem_type);
        unsigned int dsp_words_per_packed_block =
                cs_dsp_mock_reg_block_length_dsp_words(priv, packed_mem_type);
        unsigned int dsp_words_per_unpacked_block =
                cs_dsp_mock_reg_block_length_dsp_words(priv, unpacked_mem_type);
        unsigned int mem_offset_dsp_words = 0;
        struct firmware *wmfw;
        unsigned int reg_addr;
        void *packed_payload_data, *readback;
        u32 unpacked_payload_data[3];
        unsigned int packed_payload_size_bytes, packed_payload_size_dsp_words;
        unsigned int offset_num_regs;

        packed_payload_size_bytes = param->num_blocks *
                                    cs_dsp_mock_reg_block_length_bytes(priv, packed_mem_type);
        packed_payload_size_dsp_words = param->num_blocks * dsp_words_per_packed_block;

        packed_payload_data = kunit_kmalloc(test, packed_payload_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, packed_payload_data);
        get_random_bytes(packed_payload_data, packed_payload_size_bytes);

        get_random_bytes(unpacked_payload_data, sizeof(unpacked_payload_data));

        readback = kunit_kzalloc(test, packed_payload_size_bytes, GFP_KERNEL);

        /* Tests on XM must be after the XM header */
        if (unpacked_mem_type == WMFW_ADSP2_XM) {
                mem_offset_dsp_words += local->xm_header->blob_size_bytes / sizeof(u32);

                /* Round up to multiple of packed block length */
                mem_offset_dsp_words = roundup(mem_offset_dsp_words, dsp_words_per_packed_block);
        }

        /* Add a single packed payload */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        packed_mem_type, mem_offset_dsp_words,
                                        packed_payload_data, packed_payload_size_bytes);
        /*
         * Add payload of three unpacked words to DSP memory right after
         * the packed payload words.
         */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        unpacked_mem_type,
                                        mem_offset_dsp_words + packed_payload_size_dsp_words,
                                        unpacked_payload_data, sizeof(unpacked_payload_data));

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

        /*
         * Check that the packed payload was written correctly and drop
         * it from the regmap cache.
         */
        offset_num_regs = (mem_offset_dsp_words / dsp_words_per_packed_block) *
                          cs_dsp_mock_reg_block_length_registers(priv, packed_mem_type);
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, packed_mem_type);
        reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        packed_payload_size_bytes),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, packed_payload_data, packed_payload_size_bytes);

        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, packed_payload_size_bytes);

        /*
         * Check that the unpacked words were written correctly and drop
         * them from the regmap cache. The unpacked payload is offset
         * within unpacked register space by the number of DSP words
         * that were written in the packed payload.
         */
        offset_num_regs = (mem_offset_dsp_words / dsp_words_per_unpacked_block) *
                          cs_dsp_mock_reg_block_length_registers(priv, unpacked_mem_type);
        offset_num_regs += (packed_payload_size_dsp_words / dsp_words_per_unpacked_block) *
                           cs_dsp_mock_reg_block_length_registers(priv, unpacked_mem_type);
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, unpacked_mem_type);
        reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        sizeof(unpacked_payload_data)),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, unpacked_payload_data, sizeof(unpacked_payload_data));

        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, sizeof(unpacked_payload_data));

        /* Drop expected writes and the cache should then be clean */
        cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
        KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Write XM/YM data that is two words longer than a packed block multiple,
 * using one packed payload followed by two payloads of one unpacked word each.
 */
static void wmfw_write_packed_2_single_unpacked_trailing(struct kunit *test)
{
        const struct cs_dsp_wmfw_test_param *param = test->param_value;
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        int packed_mem_type = param->mem_type;
        int unpacked_mem_type = cs_dsp_mock_packed_to_unpacked_mem_type(param->mem_type);
        unsigned int dsp_words_per_packed_block =
                cs_dsp_mock_reg_block_length_dsp_words(priv, packed_mem_type);
        unsigned int dsp_words_per_unpacked_block =
                cs_dsp_mock_reg_block_length_dsp_words(priv, unpacked_mem_type);
        unsigned int mem_offset_dsp_words = 0;
        struct firmware *wmfw;
        unsigned int reg_addr;
        void *packed_payload_data, *readback;
        u32 unpacked_payload_data[2];
        unsigned int packed_payload_size_bytes, packed_payload_size_dsp_words;
        unsigned int offset_num_regs;

        packed_payload_size_bytes = param->num_blocks *
                                    cs_dsp_mock_reg_block_length_bytes(priv, packed_mem_type);
        packed_payload_size_dsp_words = param->num_blocks * dsp_words_per_packed_block;

        packed_payload_data = kunit_kmalloc(test, packed_payload_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, packed_payload_data);
        get_random_bytes(packed_payload_data, packed_payload_size_bytes);

        get_random_bytes(unpacked_payload_data, sizeof(unpacked_payload_data));

        readback = kunit_kzalloc(test, packed_payload_size_bytes, GFP_KERNEL);

        /* Tests on XM must be after the XM header */
        if (unpacked_mem_type == WMFW_ADSP2_XM) {
                mem_offset_dsp_words += local->xm_header->blob_size_bytes / sizeof(u32);

                /* Round up to multiple of packed block length */
                mem_offset_dsp_words = roundup(mem_offset_dsp_words, dsp_words_per_packed_block);
        }

        /* Add a single packed payload */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        packed_mem_type, mem_offset_dsp_words,
                                        packed_payload_data, packed_payload_size_bytes);
        /*
         * Add two unpacked words to DSP memory right after the packed
         * payload words. Each unpacked word in its own payload.
         */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        unpacked_mem_type,
                                        mem_offset_dsp_words + packed_payload_size_dsp_words,
                                        &unpacked_payload_data[0],
                                        sizeof(unpacked_payload_data[0]));
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        unpacked_mem_type,
                                        mem_offset_dsp_words + packed_payload_size_dsp_words + 1,
                                        &unpacked_payload_data[1],
                                        sizeof(unpacked_payload_data[1]));

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

        /*
         * Check that the packed payload was written correctly and drop
         * it from the regmap cache.
         */
        offset_num_regs = (mem_offset_dsp_words / dsp_words_per_packed_block) *
                          cs_dsp_mock_reg_block_length_registers(priv, packed_mem_type);
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, packed_mem_type);
        reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        packed_payload_size_bytes),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, packed_payload_data, packed_payload_size_bytes);

        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, packed_payload_size_bytes);

        /*
         * Check that the unpacked words were written correctly and drop
         * them from the regmap cache. The unpacked words are offset
         * within unpacked register space by the number of DSP words
         * that were written in the packed payload.
         */
        offset_num_regs = (mem_offset_dsp_words / dsp_words_per_unpacked_block) *
                          cs_dsp_mock_reg_block_length_registers(priv, unpacked_mem_type);
        offset_num_regs += (packed_payload_size_dsp_words / dsp_words_per_unpacked_block) *
                           cs_dsp_mock_reg_block_length_registers(priv, unpacked_mem_type);
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, unpacked_mem_type);
        reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        sizeof(unpacked_payload_data)),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, unpacked_payload_data, sizeof(unpacked_payload_data));

        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, sizeof(unpacked_payload_data));

        /* Drop expected writes and the cache should then be clean */
        cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
        KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Write XM/YM data that is three words longer than a packed block multiple,
 * using one packed payload followed by three payloads of one unpacked word each.
 */
static void wmfw_write_packed_3_single_unpacked_trailing(struct kunit *test)
{
        const struct cs_dsp_wmfw_test_param *param = test->param_value;
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        int packed_mem_type = param->mem_type;
        int unpacked_mem_type = cs_dsp_mock_packed_to_unpacked_mem_type(param->mem_type);
        unsigned int dsp_words_per_packed_block =
                cs_dsp_mock_reg_block_length_dsp_words(priv, packed_mem_type);
        unsigned int dsp_words_per_unpacked_block =
                cs_dsp_mock_reg_block_length_dsp_words(priv, unpacked_mem_type);
        unsigned int mem_offset_dsp_words = 0;
        struct firmware *wmfw;
        unsigned int reg_addr;
        void *packed_payload_data, *readback;
        u32 unpacked_payload_data[3];
        unsigned int packed_payload_size_bytes, packed_payload_size_dsp_words;
        unsigned int offset_num_regs;

        packed_payload_size_bytes = param->num_blocks *
                                 cs_dsp_mock_reg_block_length_bytes(priv, packed_mem_type);
        packed_payload_size_dsp_words = param->num_blocks * dsp_words_per_packed_block;

        packed_payload_data = kunit_kmalloc(test, packed_payload_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, packed_payload_data);
        get_random_bytes(packed_payload_data, packed_payload_size_bytes);

        get_random_bytes(unpacked_payload_data, sizeof(unpacked_payload_data));

        readback = kunit_kzalloc(test, packed_payload_size_bytes, GFP_KERNEL);

        /* Tests on XM must be after the XM header */
        if (unpacked_mem_type == WMFW_ADSP2_XM) {
                mem_offset_dsp_words += local->xm_header->blob_size_bytes / sizeof(u32);

                /* Round up to multiple of packed block length */
                mem_offset_dsp_words = roundup(mem_offset_dsp_words, dsp_words_per_packed_block);
        }

        /* Add a single packed payload */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        packed_mem_type, mem_offset_dsp_words,
                                        packed_payload_data, packed_payload_size_bytes);
        /*
         * Add three unpacked words to DSP memory right after the packed
         * payload words. Each unpacked word in its own payload.
         */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        unpacked_mem_type,
                                        mem_offset_dsp_words + packed_payload_size_dsp_words,
                                        &unpacked_payload_data[0],
                                        sizeof(unpacked_payload_data[0]));
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        unpacked_mem_type,
                                        mem_offset_dsp_words + packed_payload_size_dsp_words + 1,
                                        &unpacked_payload_data[1],
                                        sizeof(unpacked_payload_data[1]));
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        unpacked_mem_type,
                                        mem_offset_dsp_words + packed_payload_size_dsp_words + 2,
                                        &unpacked_payload_data[2],
                                        sizeof(unpacked_payload_data[2]));

        /* Download the wmfw */
        wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
        KUNIT_EXPECT_EQ(test,
                        cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
                        0);
        /*
         * Check that the packed payload was written correctly and drop
         * it from the regmap cache.
         */
        offset_num_regs = (mem_offset_dsp_words / dsp_words_per_packed_block) *
                          cs_dsp_mock_reg_block_length_registers(priv, packed_mem_type);
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, packed_mem_type);
        reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        packed_payload_size_bytes),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, packed_payload_data, packed_payload_size_bytes);

        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, packed_payload_size_bytes);

        /*
         * Check that the unpacked words were written correctly and drop
         * them from the regmap cache. The unpacked words are offset
         * within unpacked register space by the number of DSP words
         * that were written in the packed payload.
         */
        offset_num_regs = (mem_offset_dsp_words / dsp_words_per_unpacked_block) *
                          cs_dsp_mock_reg_block_length_registers(priv, unpacked_mem_type);
        offset_num_regs += (packed_payload_size_dsp_words / dsp_words_per_unpacked_block) *
                           cs_dsp_mock_reg_block_length_registers(priv, unpacked_mem_type);
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, unpacked_mem_type);
        reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        sizeof(unpacked_payload_data)),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, unpacked_payload_data, sizeof(unpacked_payload_data));

        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, sizeof(unpacked_payload_data));

        /* Drop expected writes and the cache should then be clean */
        cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
        KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Write XM/YM data that is one word longer than a packed block multiple,
 * and does not start on a packed alignment. Use one unpacked word
 * followed by a packed payload.
 */
static void wmfw_write_packed_1_unpacked_leading(struct kunit *test)
{
        const struct cs_dsp_wmfw_test_param *param = test->param_value;
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        int packed_mem_type = param->mem_type;
        int unpacked_mem_type = cs_dsp_mock_packed_to_unpacked_mem_type(param->mem_type);
        unsigned int dsp_words_per_packed_block =
                cs_dsp_mock_reg_block_length_dsp_words(priv, packed_mem_type);
        unsigned int dsp_words_per_unpacked_block =
                cs_dsp_mock_reg_block_length_dsp_words(priv, unpacked_mem_type);
        unsigned int packed_payload_offset_dsp_words = 0;
        struct firmware *wmfw;
        unsigned int reg_addr;
        void *packed_payload_data, *readback;
        u32 unpacked_payload_data[1];
        unsigned int packed_payload_size_bytes;
        unsigned int offset_num_regs;

        packed_payload_size_bytes = param->num_blocks *
                                    cs_dsp_mock_reg_block_length_bytes(priv, packed_mem_type);

        packed_payload_data = kunit_kmalloc(test, packed_payload_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, packed_payload_data);
        get_random_bytes(packed_payload_data, packed_payload_size_bytes);

        get_random_bytes(unpacked_payload_data, sizeof(unpacked_payload_data));

        readback = kunit_kzalloc(test, packed_payload_size_bytes, GFP_KERNEL);

        /* Tests on XM must be after the XM header */
        if (unpacked_mem_type == WMFW_ADSP2_XM)
                packed_payload_offset_dsp_words += local->xm_header->blob_size_bytes /
                                                   sizeof(u32);
        /*
         * Leave space for an unaligned word before the packed block and
         * round the packed block start to multiple of packed block length.
         */
        packed_payload_offset_dsp_words += 1;
        packed_payload_offset_dsp_words = roundup(packed_payload_offset_dsp_words,
                                                  dsp_words_per_packed_block);

        /* Add a single unpacked word right before the first word of packed data */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        unpacked_mem_type,
                                        packed_payload_offset_dsp_words - 1,
                                        unpacked_payload_data, sizeof(unpacked_payload_data));

        /* Add payload of packed data to the DSP memory after the unpacked word. */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        packed_mem_type,
                                        packed_payload_offset_dsp_words,
                                        packed_payload_data, packed_payload_size_bytes);

        /* Download the wmfw */
        wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
        KUNIT_EXPECT_EQ(test,
                        cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
                        0);
        /*
         * Check that the packed payload was written correctly and drop
         * it from the regmap cache.
         */
        offset_num_regs = (packed_payload_offset_dsp_words / dsp_words_per_packed_block) *
                          cs_dsp_mock_reg_block_length_registers(priv, packed_mem_type);
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, packed_mem_type);
        reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        packed_payload_size_bytes),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, packed_payload_data, packed_payload_size_bytes);

        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, packed_payload_size_bytes);

        /*
         * Check that the unpacked word was written correctly and drop
         * it from the regmap cache.
         */
        offset_num_regs = ((packed_payload_offset_dsp_words - 1) / dsp_words_per_unpacked_block) *
                          cs_dsp_mock_reg_block_length_registers(priv, unpacked_mem_type);
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, unpacked_mem_type);
        reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        sizeof(unpacked_payload_data)),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, unpacked_payload_data, sizeof(unpacked_payload_data));

        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, sizeof(unpacked_payload_data));

        /* Drop expected writes and the cache should then be clean */
        cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
        KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Write XM/YM data that is two words longer than a packed block multiple,
 * and does not start on a packed alignment. Use one payload of two unpacked
 * words followed by a packed payload.
 */
static void wmfw_write_packed_2_unpacked_leading(struct kunit *test)
{
        const struct cs_dsp_wmfw_test_param *param = test->param_value;
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        int packed_mem_type = param->mem_type;
        int unpacked_mem_type = cs_dsp_mock_packed_to_unpacked_mem_type(param->mem_type);
        unsigned int dsp_words_per_packed_block =
                cs_dsp_mock_reg_block_length_dsp_words(priv, packed_mem_type);
        unsigned int dsp_words_per_unpacked_block =
                cs_dsp_mock_reg_block_length_dsp_words(priv, unpacked_mem_type);
        unsigned int packed_payload_offset_dsp_words = 0;
        struct firmware *wmfw;
        unsigned int reg_addr;
        void *packed_payload_data, *readback;
        u32 unpacked_payload_data[2];
        unsigned int packed_payload_size_bytes;
        unsigned int offset_num_regs;

        packed_payload_size_bytes = param->num_blocks *
                                    cs_dsp_mock_reg_block_length_bytes(priv, packed_mem_type);

        packed_payload_data = kunit_kmalloc(test, packed_payload_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, packed_payload_data);
        get_random_bytes(packed_payload_data, packed_payload_size_bytes);

        get_random_bytes(unpacked_payload_data, sizeof(unpacked_payload_data));

        readback = kunit_kzalloc(test, packed_payload_size_bytes, GFP_KERNEL);

        /* Tests on XM must be after the XM header */
        if (unpacked_mem_type == WMFW_ADSP2_XM)
                packed_payload_offset_dsp_words += local->xm_header->blob_size_bytes /
                                                   sizeof(u32);
        /*
         * Leave space for two unaligned words before the packed block and
         * round the packed block start to multiple of packed block length.
         */
        packed_payload_offset_dsp_words += 2;
        packed_payload_offset_dsp_words = roundup(packed_payload_offset_dsp_words,
                                                  dsp_words_per_packed_block);

        /*
         * Add two unpacked words as a single payload right before the
         * first word of packed data
         */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        unpacked_mem_type,
                                        packed_payload_offset_dsp_words - 2,
                                        unpacked_payload_data, sizeof(unpacked_payload_data));

        /* Add payload of packed data to the DSP memory after the unpacked words. */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        packed_mem_type,
                                        packed_payload_offset_dsp_words,
                                        packed_payload_data, packed_payload_size_bytes);

        /* Download the wmfw */
        wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
        KUNIT_EXPECT_EQ(test,
                        cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
                        0);
        /*
         * Check that the packed payload was written correctly and drop
         * it from the regmap cache.
         */
        offset_num_regs = (packed_payload_offset_dsp_words / dsp_words_per_packed_block) *
                          cs_dsp_mock_reg_block_length_registers(priv, packed_mem_type);
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, packed_mem_type);
        reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        packed_payload_size_bytes),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, packed_payload_data, packed_payload_size_bytes);

        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, packed_payload_size_bytes);

        /*
         * Check that the unpacked words were written correctly and drop
         * them from the regmap cache.
         */
        offset_num_regs = ((packed_payload_offset_dsp_words - 2) / dsp_words_per_unpacked_block) *
                          cs_dsp_mock_reg_block_length_registers(priv, unpacked_mem_type);
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, unpacked_mem_type);
        reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        sizeof(unpacked_payload_data)),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, unpacked_payload_data, sizeof(unpacked_payload_data));

        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, sizeof(unpacked_payload_data));

        /* Drop expected writes and the cache should then be clean */
        cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
        KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Write XM/YM data that is three words longer than a packed block multiple,
 * and does not start on a packed alignment. Use one payload of three unpacked
 * words followed by a packed payload.
 */
static void wmfw_write_packed_3_unpacked_leading(struct kunit *test)
{
        const struct cs_dsp_wmfw_test_param *param = test->param_value;
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        int packed_mem_type = param->mem_type;
        int unpacked_mem_type = cs_dsp_mock_packed_to_unpacked_mem_type(param->mem_type);
        unsigned int dsp_words_per_packed_block =
                cs_dsp_mock_reg_block_length_dsp_words(priv, packed_mem_type);
        unsigned int dsp_words_per_unpacked_block =
                cs_dsp_mock_reg_block_length_dsp_words(priv, unpacked_mem_type);
        unsigned int packed_payload_offset_dsp_words = 0;
        struct firmware *wmfw;
        unsigned int reg_addr;
        void *packed_payload_data, *readback;
        u32 unpacked_payload_data[3];
        unsigned int packed_payload_size_bytes;
        unsigned int offset_num_regs;

        packed_payload_size_bytes = param->num_blocks *
                                    cs_dsp_mock_reg_block_length_bytes(priv, packed_mem_type);

        packed_payload_data = kunit_kmalloc(test, packed_payload_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, packed_payload_data);
        get_random_bytes(packed_payload_data, packed_payload_size_bytes);

        get_random_bytes(unpacked_payload_data, sizeof(unpacked_payload_data));

        readback = kunit_kzalloc(test, packed_payload_size_bytes, GFP_KERNEL);

        /* Tests on XM must be after the XM header */
        if (unpacked_mem_type == WMFW_ADSP2_XM)
                packed_payload_offset_dsp_words += local->xm_header->blob_size_bytes /
                                                   sizeof(u32);
        /*
         * Leave space for three unaligned words before the packed block and
         * round the packed block start to multiple of packed block length.
         */
        packed_payload_offset_dsp_words += 3;
        packed_payload_offset_dsp_words = roundup(packed_payload_offset_dsp_words,
                                                  dsp_words_per_packed_block);

        /*
         * Add three unpacked words as a single payload right before the
         * first word of packed data
         */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        unpacked_mem_type,
                                        packed_payload_offset_dsp_words - 3,
                                        unpacked_payload_data, sizeof(unpacked_payload_data));

        /* Add payload of packed data to the DSP memory after the unpacked words. */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        packed_mem_type,
                                        packed_payload_offset_dsp_words,
                                        packed_payload_data, packed_payload_size_bytes);

        /* Download the wmfw */
        wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
        KUNIT_EXPECT_EQ(test,
                        cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
                        0);
        /*
         * Check that the packed payload was written correctly and drop
         * it from the regmap cache.
         */
        offset_num_regs = (packed_payload_offset_dsp_words / dsp_words_per_packed_block) *
                          cs_dsp_mock_reg_block_length_registers(priv, packed_mem_type);
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, packed_mem_type);
        reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        packed_payload_size_bytes),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, packed_payload_data, packed_payload_size_bytes);

        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, packed_payload_size_bytes);

        /*
         * Check that the unpacked words were written correctly and drop
         * them from the regmap cache.
         */
        offset_num_regs = ((packed_payload_offset_dsp_words - 3) / dsp_words_per_unpacked_block) *
                          cs_dsp_mock_reg_block_length_registers(priv, unpacked_mem_type);
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, unpacked_mem_type);
        reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        sizeof(unpacked_payload_data)),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, unpacked_payload_data, sizeof(unpacked_payload_data));

        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, sizeof(unpacked_payload_data));

        /* Drop expected writes and the cache should then be clean */
        cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
        KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Write XM/YM data that is two words longer than a packed block multiple,
 * and does not start on a packed alignment. Use two payloads of one unpacked
 * word each, followed by a packed payload.
 */
static void wmfw_write_packed_2_single_unpacked_leading(struct kunit *test)
{
        const struct cs_dsp_wmfw_test_param *param = test->param_value;
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        int packed_mem_type = param->mem_type;
        int unpacked_mem_type = cs_dsp_mock_packed_to_unpacked_mem_type(param->mem_type);
        unsigned int dsp_words_per_packed_block =
                cs_dsp_mock_reg_block_length_dsp_words(priv, packed_mem_type);
        unsigned int dsp_words_per_unpacked_block =
                cs_dsp_mock_reg_block_length_dsp_words(priv, unpacked_mem_type);
        unsigned int packed_payload_offset_dsp_words = 0;
        struct firmware *wmfw;
        unsigned int reg_addr;
        void *packed_payload_data, *readback;
        u32 unpacked_payload_data[2];
        unsigned int packed_payload_size_bytes;
        unsigned int offset_num_regs;

        packed_payload_size_bytes = param->num_blocks *
                                    cs_dsp_mock_reg_block_length_bytes(priv, packed_mem_type);

        packed_payload_data = kunit_kmalloc(test, packed_payload_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, packed_payload_data);
        get_random_bytes(packed_payload_data, packed_payload_size_bytes);

        get_random_bytes(unpacked_payload_data, sizeof(unpacked_payload_data));

        readback = kunit_kzalloc(test, packed_payload_size_bytes, GFP_KERNEL);

        /* Tests on XM must be after the XM header */
        if (unpacked_mem_type == WMFW_ADSP2_XM)
                packed_payload_offset_dsp_words += local->xm_header->blob_size_bytes /
                                                   sizeof(u32);
        /*
         * Leave space for two unaligned words before the packed block and
         * round the packed block start to multiple of packed block length.
         */
        packed_payload_offset_dsp_words += 2;
        packed_payload_offset_dsp_words = roundup(packed_payload_offset_dsp_words,
                                                  dsp_words_per_packed_block);

        /*
         * Add two unpacked words as two payloads each containing a single
         * unpacked word.
         */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        unpacked_mem_type,
                                        packed_payload_offset_dsp_words - 2,
                                        &unpacked_payload_data[0],
                                        sizeof(unpacked_payload_data[0]));
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        unpacked_mem_type,
                                        packed_payload_offset_dsp_words - 1,
                                        &unpacked_payload_data[1],
                                        sizeof(unpacked_payload_data[1]));

        /* Add payload of packed data to the DSP memory after the unpacked words. */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        packed_mem_type,
                                        packed_payload_offset_dsp_words,
                                        packed_payload_data, packed_payload_size_bytes);

        /* Download the wmfw */
        wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
        KUNIT_EXPECT_EQ(test,
                        cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
                        0);
        /*
         * Check that the packed payload was written correctly and drop
         * it from the regmap cache.
         */
        offset_num_regs = (packed_payload_offset_dsp_words / dsp_words_per_packed_block) *
                          cs_dsp_mock_reg_block_length_registers(priv, packed_mem_type);
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, packed_mem_type);
        reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        packed_payload_size_bytes),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, packed_payload_data, packed_payload_size_bytes);

        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, packed_payload_size_bytes);

        /*
         * Check that the unpacked words were written correctly and drop
         * them from the regmap cache.
         */
        offset_num_regs = ((packed_payload_offset_dsp_words - 2) / dsp_words_per_unpacked_block) *
                          cs_dsp_mock_reg_block_length_registers(priv, unpacked_mem_type);
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, unpacked_mem_type);
        reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        sizeof(unpacked_payload_data)),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, unpacked_payload_data, sizeof(unpacked_payload_data));

        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, sizeof(unpacked_payload_data));

        /* Drop expected writes and the cache should then be clean */
        cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
        KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Write XM/YM data that is three words longer than a packed block multiple,
 * and does not start on a packed alignment. Use three payloads of one unpacked
 * word each, followed by a packed payload.
 */
static void wmfw_write_packed_3_single_unpacked_leading(struct kunit *test)
{
        const struct cs_dsp_wmfw_test_param *param = test->param_value;
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        int packed_mem_type = param->mem_type;
        int unpacked_mem_type = cs_dsp_mock_packed_to_unpacked_mem_type(param->mem_type);
        unsigned int dsp_words_per_packed_block =
                cs_dsp_mock_reg_block_length_dsp_words(priv, packed_mem_type);
        unsigned int dsp_words_per_unpacked_block =
                cs_dsp_mock_reg_block_length_dsp_words(priv, unpacked_mem_type);
        unsigned int packed_payload_offset_dsp_words = 0;
        struct firmware *wmfw;
        unsigned int reg_addr;
        void *packed_payload_data, *readback;
        u32 unpacked_payload_data[3];
        unsigned int packed_payload_size_bytes;
        unsigned int offset_num_regs;

        packed_payload_size_bytes = param->num_blocks *
                                    cs_dsp_mock_reg_block_length_bytes(priv, packed_mem_type);

        packed_payload_data = kunit_kmalloc(test, packed_payload_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, packed_payload_data);
        get_random_bytes(packed_payload_data, packed_payload_size_bytes);

        get_random_bytes(unpacked_payload_data, sizeof(unpacked_payload_data));

        readback = kunit_kzalloc(test, packed_payload_size_bytes, GFP_KERNEL);

        /* Tests on XM must be after the XM header */
        if (unpacked_mem_type == WMFW_ADSP2_XM)
                packed_payload_offset_dsp_words += local->xm_header->blob_size_bytes /
                                                   sizeof(u32);
        /*
         * Leave space for two unaligned words before the packed block and
         * round the packed block start to multiple of packed block length.
         */
        packed_payload_offset_dsp_words += 3;
        packed_payload_offset_dsp_words = roundup(packed_payload_offset_dsp_words,
                                                  dsp_words_per_packed_block);

        /*
         * Add three unpacked words as three payloads each containing a single
         * unpacked word.
         */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        unpacked_mem_type,
                                        packed_payload_offset_dsp_words - 3,
                                        &unpacked_payload_data[0],
                                        sizeof(unpacked_payload_data[0]));
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        unpacked_mem_type,
                                        packed_payload_offset_dsp_words - 2,
                                        &unpacked_payload_data[1],
                                        sizeof(unpacked_payload_data[1]));
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        unpacked_mem_type,
                                        packed_payload_offset_dsp_words - 1,
                                        &unpacked_payload_data[2],
                                        sizeof(unpacked_payload_data[2]));

        /* Add payload of packed data to the DSP memory after the unpacked words. */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        packed_mem_type,
                                        packed_payload_offset_dsp_words,
                                        packed_payload_data, packed_payload_size_bytes);

        /* Download the wmfw */
        wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
        KUNIT_EXPECT_EQ(test,
                        cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc"),
                        0);
        /*
         * Check that the packed payload was written correctly and drop
         * it from the regmap cache.
         */
        offset_num_regs = (packed_payload_offset_dsp_words / dsp_words_per_packed_block) *
                          cs_dsp_mock_reg_block_length_registers(priv, packed_mem_type);
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, packed_mem_type);
        reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        packed_payload_size_bytes),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, packed_payload_data, packed_payload_size_bytes);

        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, packed_payload_size_bytes);

        /*
         * Check that the unpacked words were written correctly and drop
         * them from the regmap cache.
         */
        offset_num_regs = ((packed_payload_offset_dsp_words - 3) / dsp_words_per_unpacked_block) *
                          cs_dsp_mock_reg_block_length_registers(priv, unpacked_mem_type);
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, unpacked_mem_type);
        reg_addr += offset_num_regs * regmap_get_reg_stride(priv->dsp->regmap);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback,
                                        sizeof(unpacked_payload_data)),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, unpacked_payload_data, sizeof(unpacked_payload_data));

        cs_dsp_mock_regmap_drop_bytes(priv, reg_addr, sizeof(unpacked_payload_data));

        /* Drop expected writes and the cache should then be clean */
        cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
        KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/* Load a wmfw containing multiple info blocks */
static void wmfw_load_with_info(struct kunit *test)
{
        struct cs_dsp_test *priv = test->priv;
        struct cs_dsp_test_local *local = priv->local;
        struct firmware *wmfw;
        unsigned int reg_addr;
        u8 *payload_data, *readback;
        char *infobuf;
        const unsigned int payload_size_bytes = 48;
        int ret;

        payload_data = kunit_kmalloc(test, payload_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, payload_data);
        get_random_bytes(payload_data, payload_size_bytes);

        readback = kunit_kzalloc(test, payload_size_bytes, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);

        /* Add a couple of info blocks at the start of the wmfw */
        cs_dsp_mock_wmfw_add_info(local->wmfw_builder, "This is a timestamp");
        cs_dsp_mock_wmfw_add_info(local->wmfw_builder, "This is some more info");

        /* Add a single payload */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        WMFW_ADSP2_YM, 0,
                                        payload_data, payload_size_bytes);

        /* Add a bigger info block then another small one*/
        infobuf = kunit_kzalloc(test, 512, GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, infobuf);

        for (; strlcat(infobuf, "Waffle{Blah}\n", 512) < 512;)
                ;

        cs_dsp_mock_wmfw_add_info(local->wmfw_builder, infobuf);
        cs_dsp_mock_wmfw_add_info(local->wmfw_builder, "Another block of info");

        /* Add another payload */
        cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
                                        WMFW_ADSP2_YM, 64,
                                        payload_data, payload_size_bytes);

        wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);

        ret = cs_dsp_power_up(priv->dsp, wmfw, "mock_wmfw", NULL, NULL, "misc");
        KUNIT_EXPECT_EQ_MSG(test, ret, 0, "cs_dsp_power_up failed: %d\n", ret);

        /* Check first payload was written */
        reg_addr = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_YM);
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback, payload_size_bytes),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, payload_data, payload_size_bytes);

        /* Check second payload was written */
        reg_addr += cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv) * 64;
        KUNIT_EXPECT_EQ(test,
                        regmap_raw_read(priv->dsp->regmap, reg_addr, readback, payload_size_bytes),
                        0);
        KUNIT_EXPECT_MEMEQ(test, readback, payload_data, payload_size_bytes);
}

static int cs_dsp_wmfw_test_common_init(struct kunit *test, struct cs_dsp *dsp,
                                        int wmfw_version)
{
        struct cs_dsp_test *priv;
        struct cs_dsp_test_local *local;
        struct device *test_dev;
        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;
        priv->local->wmfw_version = wmfw_version;

        /* 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 that tests can use and extract it to a data payload.
         */
        local->xm_header = cs_dsp_create_mock_xm_header(priv,
                                                        cs_dsp_wmfw_test_mock_algs,
                                                        ARRAY_SIZE(cs_dsp_wmfw_test_mock_algs));
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, local->xm_header);

        local->wmfw_builder = cs_dsp_mock_wmfw_init(priv, priv->local->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,
                                        local->xm_header->blob_data,
                                        local->xm_header->blob_size_bytes);

        /* Init cs_dsp */
        dsp->client_ops = kunit_kzalloc(test, sizeof(*dsp->client_ops), GFP_KERNEL);
        KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dsp->client_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 */
        ret = kunit_add_action_or_reset(priv->test, _cs_dsp_remove_wrapper, dsp);
        if (ret)
                return ret;

        /*
         * The large number of test cases will cause an unusually large amount
         * of dev_info() messages from cs_dsp, so suppress these.
         */
        cs_dsp_suppress_info_messages = true;

        return 0;
}

static void cs_dsp_wmfw_test_exit(struct kunit *test)
{
        cs_dsp_suppress_info_messages = false;
}

static int cs_dsp_wmfw_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_wmfw_test_common_init(test, dsp, 3);
}

static int cs_dsp_wmfw_test_adsp2_32bit_init(struct kunit *test, int wmfw_ver)
{
        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 = 1;
        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_wmfw_test_common_init(test, dsp, wmfw_ver);
}

static int cs_dsp_wmfw_test_adsp2_32bit_wmfw0_init(struct kunit *test)
{
        return cs_dsp_wmfw_test_adsp2_32bit_init(test, 0);
}

static int cs_dsp_wmfw_test_adsp2_32bit_wmfw1_init(struct kunit *test)
{
        return cs_dsp_wmfw_test_adsp2_32bit_init(test, 1);
}

static int cs_dsp_wmfw_test_adsp2_32bit_wmfw2_init(struct kunit *test)
{
        return cs_dsp_wmfw_test_adsp2_32bit_init(test, 2);
}

static int cs_dsp_wmfw_test_adsp2_16bit_init(struct kunit *test, int wmfw_ver)
{
        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_wmfw_test_common_init(test, dsp, wmfw_ver);
}

static int cs_dsp_wmfw_test_adsp2_16bit_wmfw0_init(struct kunit *test)
{
        return cs_dsp_wmfw_test_adsp2_16bit_init(test, 0);
}

static int cs_dsp_wmfw_test_adsp2_16bit_wmfw1_init(struct kunit *test)
{
        return cs_dsp_wmfw_test_adsp2_16bit_init(test, 1);
}

static int cs_dsp_wmfw_test_adsp2_16bit_wmfw2_init(struct kunit *test)
{
        return cs_dsp_wmfw_test_adsp2_16bit_init(test, 2);
}

static void cs_dsp_mem_param_desc(const struct cs_dsp_wmfw_test_param *param, char *desc)
{
        snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s num_blocks:%u",
                 cs_dsp_mem_region_name(param->mem_type),
                 param->num_blocks);
}

static const struct cs_dsp_wmfw_test_param adsp2_all_num_blocks_param_cases[] = {
        { .mem_type = WMFW_ADSP2_PM, .num_blocks = 1 },
        { .mem_type = WMFW_ADSP2_PM, .num_blocks = 2 },
        { .mem_type = WMFW_ADSP2_PM, .num_blocks = 3 },
        { .mem_type = WMFW_ADSP2_PM, .num_blocks = 4 },
        { .mem_type = WMFW_ADSP2_PM, .num_blocks = 5 },
        { .mem_type = WMFW_ADSP2_PM, .num_blocks = 6 },
        { .mem_type = WMFW_ADSP2_PM, .num_blocks = 12 },
        { .mem_type = WMFW_ADSP2_PM, .num_blocks = 13 },
        { .mem_type = WMFW_ADSP2_PM, .num_blocks = 14 },
        { .mem_type = WMFW_ADSP2_PM, .num_blocks = 15 },
        { .mem_type = WMFW_ADSP2_PM, .num_blocks = 16 },

        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 1 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 2 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 3 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 4 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 5 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 6 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 12 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 13 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 14 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 15 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 16 },

        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 1 },
        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 2 },
        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 3 },
        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 4 },
        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 5 },
        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 6 },
        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 12 },
        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 13 },
        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 14 },
        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 15 },
        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 16 },

        { .mem_type = WMFW_ADSP2_ZM, .num_blocks = 1 },
        { .mem_type = WMFW_ADSP2_ZM, .num_blocks = 2 },
        { .mem_type = WMFW_ADSP2_ZM, .num_blocks = 3 },
        { .mem_type = WMFW_ADSP2_ZM, .num_blocks = 4 },
        { .mem_type = WMFW_ADSP2_ZM, .num_blocks = 5 },
        { .mem_type = WMFW_ADSP2_ZM, .num_blocks = 6 },
        { .mem_type = WMFW_ADSP2_ZM, .num_blocks = 12 },
        { .mem_type = WMFW_ADSP2_ZM, .num_blocks = 13 },
        { .mem_type = WMFW_ADSP2_ZM, .num_blocks = 14 },
        { .mem_type = WMFW_ADSP2_ZM, .num_blocks = 15 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 16 },
};

KUNIT_ARRAY_PARAM(adsp2_all_num_blocks,
                  adsp2_all_num_blocks_param_cases,
                  cs_dsp_mem_param_desc);

static const struct cs_dsp_wmfw_test_param halo_all_num_blocks_param_cases[] = {
        { .mem_type = WMFW_HALO_PM_PACKED, .num_blocks = 1 },
        { .mem_type = WMFW_HALO_PM_PACKED, .num_blocks = 2 },
        { .mem_type = WMFW_HALO_PM_PACKED, .num_blocks = 3 },
        { .mem_type = WMFW_HALO_PM_PACKED, .num_blocks = 4 },
        { .mem_type = WMFW_HALO_PM_PACKED, .num_blocks = 5 },
        { .mem_type = WMFW_HALO_PM_PACKED, .num_blocks = 6 },
        { .mem_type = WMFW_HALO_PM_PACKED, .num_blocks = 12 },
        { .mem_type = WMFW_HALO_PM_PACKED, .num_blocks = 13 },
        { .mem_type = WMFW_HALO_PM_PACKED, .num_blocks = 14 },
        { .mem_type = WMFW_HALO_PM_PACKED, .num_blocks = 15 },
        { .mem_type = WMFW_HALO_PM_PACKED, .num_blocks = 16 },

        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 1 },
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 2 },
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 3 },
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 4 },
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 5 },
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 6 },
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 12 },
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 13 },
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 14 },
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 15 },
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 16 },

        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 1 },
        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 2 },
        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 3 },
        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 4 },
        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 5 },
        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 6 },
        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 12 },
        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 13 },
        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 14 },
        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 15 },
        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 16 },

        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 1 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 2 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 3 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 4 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 5 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 6 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 12 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 13 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 14 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 15 },
        { .mem_type = WMFW_ADSP2_XM, .num_blocks = 16 },

        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 1 },
        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 2 },
        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 3 },
        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 4 },
        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 5 },
        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 6 },
        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 12 },
        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 13 },
        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 14 },
        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 15 },
        { .mem_type = WMFW_ADSP2_YM, .num_blocks = 16 },
};

KUNIT_ARRAY_PARAM(halo_all_num_blocks,
                  halo_all_num_blocks_param_cases,
                  cs_dsp_mem_param_desc);

static const struct cs_dsp_wmfw_test_param packed_xy_num_blocks_param_cases[] = {
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 1 },
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 2 },
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 3 },
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 4 },
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 5 },
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 6 },
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 12 },
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 13 },
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 14 },
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 15 },
        { .mem_type = WMFW_HALO_XM_PACKED, .num_blocks = 16 },

        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 1 },
        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 2 },
        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 3 },
        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 4 },
        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 5 },
        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 6 },
        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 12 },
        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 13 },
        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 14 },
        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 15 },
        { .mem_type = WMFW_HALO_YM_PACKED, .num_blocks = 16 },
};

KUNIT_ARRAY_PARAM(packed_xy_num_blocks,
                  packed_xy_num_blocks_param_cases,
                  cs_dsp_mem_param_desc);

static struct kunit_case cs_dsp_wmfw_test_cases_halo[] = {
        KUNIT_CASE(wmfw_write_xm_header_unpacked),

        KUNIT_CASE_PARAM(wmfw_write_one_payload,
                         halo_all_num_blocks_gen_params),
        KUNIT_CASE_PARAM(wmfw_write_multiple_oneblock_payloads,
                         halo_all_num_blocks_gen_params),
        KUNIT_CASE_PARAM(wmfw_write_multiple_oneblock_payloads_reverse,
                         halo_all_num_blocks_gen_params),
        KUNIT_CASE_PARAM(wmfw_write_multiple_payloads_sparse_unordered,
                         halo_all_num_blocks_gen_params),

        KUNIT_CASE(wmfw_write_all_packed_pm),
        KUNIT_CASE(wmfw_write_multiple_packed_unpacked_mem),

        KUNIT_CASE_PARAM(wmfw_write_packed_1_unpacked_trailing,
                         packed_xy_num_blocks_gen_params),
        KUNIT_CASE_PARAM(wmfw_write_packed_2_unpacked_trailing,
                         packed_xy_num_blocks_gen_params),
        KUNIT_CASE_PARAM(wmfw_write_packed_3_unpacked_trailing,
                         packed_xy_num_blocks_gen_params),
        KUNIT_CASE_PARAM(wmfw_write_packed_2_single_unpacked_trailing,
                         packed_xy_num_blocks_gen_params),
        KUNIT_CASE_PARAM(wmfw_write_packed_3_single_unpacked_trailing,
                         packed_xy_num_blocks_gen_params),
        KUNIT_CASE_PARAM(wmfw_write_packed_1_unpacked_leading,
                         packed_xy_num_blocks_gen_params),
        KUNIT_CASE_PARAM(wmfw_write_packed_2_unpacked_leading,
                         packed_xy_num_blocks_gen_params),
        KUNIT_CASE_PARAM(wmfw_write_packed_3_unpacked_leading,
                         packed_xy_num_blocks_gen_params),
        KUNIT_CASE_PARAM(wmfw_write_packed_2_single_unpacked_leading,
                         packed_xy_num_blocks_gen_params),
        KUNIT_CASE_PARAM(wmfw_write_packed_3_single_unpacked_leading,
                         packed_xy_num_blocks_gen_params),

        KUNIT_CASE(wmfw_load_with_info),

        { } /* terminator */
};

static struct kunit_case cs_dsp_wmfw_test_cases_adsp2[] = {
        KUNIT_CASE(wmfw_write_xm_header_unpacked),
        KUNIT_CASE_PARAM(wmfw_write_one_payload,
                         adsp2_all_num_blocks_gen_params),
        KUNIT_CASE_PARAM(wmfw_write_multiple_oneblock_payloads,
                         adsp2_all_num_blocks_gen_params),
        KUNIT_CASE_PARAM(wmfw_write_multiple_oneblock_payloads_reverse,
                         adsp2_all_num_blocks_gen_params),
        KUNIT_CASE_PARAM(wmfw_write_multiple_payloads_sparse_unordered,
                         adsp2_all_num_blocks_gen_params),

        KUNIT_CASE(wmfw_write_all_unpacked_pm),
        KUNIT_CASE(wmfw_write_multiple_unpacked_mem),

        KUNIT_CASE(wmfw_load_with_info),

        { } /* terminator */
};

static struct kunit_suite cs_dsp_wmfw_test_halo = {
        .name = "cs_dsp_wmfwV3_halo",
        .init = cs_dsp_wmfw_test_halo_init,
        .exit = cs_dsp_wmfw_test_exit,
        .test_cases = cs_dsp_wmfw_test_cases_halo,
};

static struct kunit_suite cs_dsp_wmfw_test_adsp2_32bit_wmfw0 = {
        .name = "cs_dsp_wmfwV0_adsp2_32bit",
        .init = cs_dsp_wmfw_test_adsp2_32bit_wmfw0_init,
        .exit = cs_dsp_wmfw_test_exit,
        .test_cases = cs_dsp_wmfw_test_cases_adsp2,
};

static struct kunit_suite cs_dsp_wmfw_test_adsp2_32bit_wmfw1 = {
        .name = "cs_dsp_wmfwV1_adsp2_32bit",
        .init = cs_dsp_wmfw_test_adsp2_32bit_wmfw1_init,
        .exit = cs_dsp_wmfw_test_exit,
        .test_cases = cs_dsp_wmfw_test_cases_adsp2,
};

static struct kunit_suite cs_dsp_wmfw_test_adsp2_32bit_wmfw2 = {
        .name = "cs_dsp_wmfwV2_adsp2_32bit",
        .init = cs_dsp_wmfw_test_adsp2_32bit_wmfw2_init,
        .exit = cs_dsp_wmfw_test_exit,
        .test_cases = cs_dsp_wmfw_test_cases_adsp2,
};

static struct kunit_suite cs_dsp_wmfw_test_adsp2_16bit_wmfw0 = {
        .name = "cs_dsp_wmfwV0_adsp2_16bit",
        .init = cs_dsp_wmfw_test_adsp2_16bit_wmfw0_init,
        .exit = cs_dsp_wmfw_test_exit,
        .test_cases = cs_dsp_wmfw_test_cases_adsp2,
};

static struct kunit_suite cs_dsp_wmfw_test_adsp2_16bit_wmfw1 = {
        .name = "cs_dsp_wmfwV1_adsp2_16bit",
        .init = cs_dsp_wmfw_test_adsp2_16bit_wmfw1_init,
        .exit = cs_dsp_wmfw_test_exit,
        .test_cases = cs_dsp_wmfw_test_cases_adsp2,
};

static struct kunit_suite cs_dsp_wmfw_test_adsp2_16bit_wmfw2 = {
        .name = "cs_dsp_wmfwV2_adsp2_16bit",
        .init = cs_dsp_wmfw_test_adsp2_16bit_wmfw2_init,
        .exit = cs_dsp_wmfw_test_exit,
        .test_cases = cs_dsp_wmfw_test_cases_adsp2,
};

kunit_test_suites(&cs_dsp_wmfw_test_halo,
                  &cs_dsp_wmfw_test_adsp2_32bit_wmfw0,
                  &cs_dsp_wmfw_test_adsp2_32bit_wmfw1,
                  &cs_dsp_wmfw_test_adsp2_32bit_wmfw2,
                  &cs_dsp_wmfw_test_adsp2_16bit_wmfw0,
                  &cs_dsp_wmfw_test_adsp2_16bit_wmfw1,
                  &cs_dsp_wmfw_test_adsp2_16bit_wmfw2);