mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-07 12:44:32 -04:00
firmware: cirrus: Add KUnit tests for cs_dsp
Merge series from Richard Fitzgerald <rf@opensource.cirrus.com>: This series adds KUnit tests for the cs_dsp module. Most of the functionality in cs_dsp is for downloading firmware to DSP memory and interacting with "control" words defined in that memory. This doesn't require any emulation of running firmware, because it is only reading and writing registers. So the testing can be done using a dummy regmap. The way this is used to perform testing is described in more detail in the commit message for each test. ADSP1 is not tested because this was only used by the WM2200 codec, a long-obsolete part that was discontinued in 2015.
This commit is contained in:
@@ -5501,8 +5501,8 @@ L: patches@opensource.cirrus.com
|
||||
S: Supported
|
||||
W: https://github.com/CirrusLogic/linux-drivers/wiki
|
||||
T: git https://github.com/CirrusLogic/linux-drivers.git
|
||||
F: drivers/firmware/cirrus/*
|
||||
F: include/linux/firmware/cirrus/*
|
||||
F: drivers/firmware/cirrus/
|
||||
F: include/linux/firmware/cirrus/
|
||||
|
||||
CIRRUS LOGIC EP93XX ETHERNET DRIVER
|
||||
M: Hartley Sweeten <hsweeten@visionengravers.com>
|
||||
|
||||
@@ -3,3 +3,23 @@
|
||||
config FW_CS_DSP
|
||||
tristate
|
||||
default n
|
||||
|
||||
config FW_CS_DSP_KUNIT_TEST_UTILS
|
||||
tristate
|
||||
depends on KUNIT
|
||||
select REGMAP
|
||||
select FW_CS_DSP
|
||||
|
||||
config FW_CS_DSP_KUNIT_TEST
|
||||
tristate "KUnit tests for Cirrus Logic cs_dsp" if !KUNIT_ALL_TESTS
|
||||
depends on KUNIT
|
||||
default KUNIT_ALL_TESTS
|
||||
select REGMAP
|
||||
select FW_CS_DSP
|
||||
select FW_CS_DSP_KUNIT_TEST_UTILS
|
||||
help
|
||||
This builds KUnit tests for cs_dsp.
|
||||
For more information on KUnit and unit tests in general,
|
||||
please refer to the KUnit documentation in
|
||||
Documentation/dev-tools/kunit/.
|
||||
If in doubt, say "N".
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
obj-$(CONFIG_FW_CS_DSP) += cs_dsp.o
|
||||
|
||||
obj-y += test/
|
||||
|
||||
23
drivers/firmware/cirrus/test/Makefile
Normal file
23
drivers/firmware/cirrus/test/Makefile
Normal file
@@ -0,0 +1,23 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
|
||||
cs_dsp_test_utils-objs := \
|
||||
cs_dsp_mock_mem_maps.o \
|
||||
cs_dsp_mock_bin.o \
|
||||
cs_dsp_mock_regmap.o \
|
||||
cs_dsp_mock_utils.o \
|
||||
cs_dsp_mock_wmfw.o
|
||||
|
||||
cs_dsp_test-objs := \
|
||||
cs_dsp_test_bin.o \
|
||||
cs_dsp_test_bin_error.o \
|
||||
cs_dsp_test_callbacks.o \
|
||||
cs_dsp_test_control_parse.o \
|
||||
cs_dsp_test_control_cache.o \
|
||||
cs_dsp_test_control_rw.o \
|
||||
cs_dsp_test_wmfw.o \
|
||||
cs_dsp_test_wmfw_error.o \
|
||||
cs_dsp_tests.o
|
||||
|
||||
obj-$(CONFIG_FW_CS_DSP_KUNIT_TEST_UTILS) += cs_dsp_test_utils.o
|
||||
obj-$(CONFIG_FW_CS_DSP_KUNIT_TEST) += cs_dsp_test.o
|
||||
199
drivers/firmware/cirrus/test/cs_dsp_mock_bin.c
Normal file
199
drivers/firmware/cirrus/test/cs_dsp_mock_bin.c
Normal file
@@ -0,0 +1,199 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// bin file builder for cs_dsp KUnit tests.
|
||||
//
|
||||
// Copyright (C) 2024 Cirrus Logic, Inc. and
|
||||
// Cirrus Logic International Semiconductor Ltd.
|
||||
|
||||
#include <kunit/resource.h>
|
||||
#include <kunit/test.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/firmware.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
/* Buffer large enough for bin file content */
|
||||
#define CS_DSP_MOCK_BIN_BUF_SIZE 32768
|
||||
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(vfree_action_wrapper, vfree, void *)
|
||||
|
||||
struct cs_dsp_mock_bin_builder {
|
||||
struct cs_dsp_test *test_priv;
|
||||
void *buf;
|
||||
void *write_p;
|
||||
size_t bytes_used;
|
||||
};
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_bin_get_firmware() - Get struct firmware wrapper for data.
|
||||
*
|
||||
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
|
||||
*
|
||||
* Return: Pointer to a struct firmware wrapper for the data.
|
||||
*/
|
||||
struct firmware *cs_dsp_mock_bin_get_firmware(struct cs_dsp_mock_bin_builder *builder)
|
||||
{
|
||||
struct firmware *fw;
|
||||
|
||||
fw = kunit_kzalloc(builder->test_priv->test, sizeof(*fw), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(builder->test_priv->test, fw);
|
||||
|
||||
fw->data = builder->buf;
|
||||
fw->size = builder->bytes_used;
|
||||
|
||||
return fw;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_get_firmware, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_bin_add_raw_block() - Add a data block to the bin file.
|
||||
*
|
||||
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
|
||||
* @alg_ig: Algorithm ID.
|
||||
* @alg_ver: Algorithm version.
|
||||
* @type: Type of the block.
|
||||
* @offset: Offset.
|
||||
* @payload_data: Pointer to buffer containing the payload data.
|
||||
* @payload_len_bytes: Length of payload data in bytes.
|
||||
*/
|
||||
void cs_dsp_mock_bin_add_raw_block(struct cs_dsp_mock_bin_builder *builder,
|
||||
unsigned int alg_id, unsigned int alg_ver,
|
||||
int type, unsigned int offset,
|
||||
const void *payload_data, size_t payload_len_bytes)
|
||||
{
|
||||
struct wmfw_coeff_item *item;
|
||||
size_t bytes_needed = struct_size_t(struct wmfw_coeff_item, data, payload_len_bytes);
|
||||
|
||||
KUNIT_ASSERT_TRUE(builder->test_priv->test,
|
||||
(builder->write_p + bytes_needed) <
|
||||
(builder->buf + CS_DSP_MOCK_BIN_BUF_SIZE));
|
||||
|
||||
item = builder->write_p;
|
||||
|
||||
item->offset = cpu_to_le16(offset);
|
||||
item->type = cpu_to_le16(type);
|
||||
item->id = cpu_to_le32(alg_id);
|
||||
item->ver = cpu_to_le32(alg_ver << 8);
|
||||
item->len = cpu_to_le32(payload_len_bytes);
|
||||
|
||||
if (payload_len_bytes)
|
||||
memcpy(item->data, payload_data, payload_len_bytes);
|
||||
|
||||
builder->write_p += bytes_needed;
|
||||
builder->bytes_used += bytes_needed;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_raw_block, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
static void cs_dsp_mock_bin_add_name_or_info(struct cs_dsp_mock_bin_builder *builder,
|
||||
const char *info, int type)
|
||||
{
|
||||
size_t info_len = strlen(info);
|
||||
char *tmp = NULL;
|
||||
|
||||
if (info_len % 4) {
|
||||
/* Create a padded string with length a multiple of 4 */
|
||||
info_len = round_up(info_len, 4);
|
||||
tmp = kunit_kzalloc(builder->test_priv->test, info_len, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(builder->test_priv->test, tmp);
|
||||
memcpy(tmp, info, info_len);
|
||||
info = tmp;
|
||||
}
|
||||
|
||||
cs_dsp_mock_bin_add_raw_block(builder, 0, 0, WMFW_INFO_TEXT, 0, info, info_len);
|
||||
kunit_kfree(builder->test_priv->test, tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_bin_add_info() - Add an info block to the bin file.
|
||||
*
|
||||
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
|
||||
* @info: Pointer to info string to be copied into the file.
|
||||
*
|
||||
* The string will be padded to a length that is a multiple of 4 bytes.
|
||||
*/
|
||||
void cs_dsp_mock_bin_add_info(struct cs_dsp_mock_bin_builder *builder,
|
||||
const char *info)
|
||||
{
|
||||
cs_dsp_mock_bin_add_name_or_info(builder, info, WMFW_INFO_TEXT);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_info, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_bin_add_name() - Add a name block to the bin file.
|
||||
*
|
||||
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
|
||||
* @name: Pointer to name string to be copied into the file.
|
||||
*/
|
||||
void cs_dsp_mock_bin_add_name(struct cs_dsp_mock_bin_builder *builder,
|
||||
const char *name)
|
||||
{
|
||||
cs_dsp_mock_bin_add_name_or_info(builder, name, WMFW_NAME_TEXT);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_name, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_bin_add_patch() - Add a patch data block to the bin file.
|
||||
*
|
||||
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
|
||||
* @alg_ig: Algorithm ID for the patch.
|
||||
* @alg_ver: Algorithm version for the patch.
|
||||
* @mem_region: Memory region for the patch.
|
||||
* @reg_addr_offset: Offset to start of data in register addresses.
|
||||
* @payload_data: Pointer to buffer containing the payload data.
|
||||
* @payload_len_bytes: Length of payload data in bytes.
|
||||
*/
|
||||
void cs_dsp_mock_bin_add_patch(struct cs_dsp_mock_bin_builder *builder,
|
||||
unsigned int alg_id, unsigned int alg_ver,
|
||||
int mem_region, unsigned int reg_addr_offset,
|
||||
const void *payload_data, size_t payload_len_bytes)
|
||||
{
|
||||
/* Payload length must be a multiple of 4 */
|
||||
KUNIT_ASSERT_EQ(builder->test_priv->test, payload_len_bytes % 4, 0);
|
||||
|
||||
cs_dsp_mock_bin_add_raw_block(builder, alg_id, alg_ver,
|
||||
mem_region, reg_addr_offset,
|
||||
payload_data, payload_len_bytes);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_patch, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_bin_init() - Initialize a struct cs_dsp_mock_bin_builder.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
* @format_version: Required bin format version.
|
||||
* @fw_version: Firmware version to put in bin file.
|
||||
*
|
||||
* Return: Pointer to created struct cs_dsp_mock_bin_builder.
|
||||
*/
|
||||
struct cs_dsp_mock_bin_builder *cs_dsp_mock_bin_init(struct cs_dsp_test *priv,
|
||||
int format_version,
|
||||
unsigned int fw_version)
|
||||
{
|
||||
struct cs_dsp_mock_bin_builder *builder;
|
||||
struct wmfw_coeff_hdr *hdr;
|
||||
|
||||
builder = kunit_kzalloc(priv->test, sizeof(*builder), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(priv->test, builder);
|
||||
builder->test_priv = priv;
|
||||
|
||||
builder->buf = vmalloc(CS_DSP_MOCK_BIN_BUF_SIZE);
|
||||
KUNIT_ASSERT_NOT_NULL(priv->test, builder->buf);
|
||||
kunit_add_action_or_reset(priv->test, vfree_action_wrapper, builder->buf);
|
||||
|
||||
/* Create header */
|
||||
hdr = builder->buf;
|
||||
memcpy(hdr->magic, "WMDR", sizeof(hdr->magic));
|
||||
hdr->len = cpu_to_le32(offsetof(struct wmfw_coeff_hdr, data));
|
||||
hdr->ver = cpu_to_le32(fw_version | (format_version << 24));
|
||||
hdr->core_ver = cpu_to_le32(((u32)priv->dsp->type << 24) | priv->dsp->rev);
|
||||
|
||||
builder->write_p = hdr->data;
|
||||
builder->bytes_used = offsetof(struct wmfw_coeff_hdr, data);
|
||||
|
||||
return builder;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_init, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
751
drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c
Normal file
751
drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c
Normal file
@@ -0,0 +1,751 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// Mock DSP memory maps for cs_dsp KUnit tests.
|
||||
//
|
||||
// Copyright (C) 2024 Cirrus Logic, Inc. and
|
||||
// Cirrus Logic International Semiconductor Ltd.
|
||||
|
||||
#include <kunit/test.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/math.h>
|
||||
|
||||
const struct cs_dsp_region cs_dsp_mock_halo_dsp1_regions[] = {
|
||||
{ .type = WMFW_HALO_PM_PACKED, .base = 0x3800000 },
|
||||
{ .type = WMFW_HALO_XM_PACKED, .base = 0x2000000 },
|
||||
{ .type = WMFW_HALO_YM_PACKED, .base = 0x2C00000 },
|
||||
{ .type = WMFW_ADSP2_XM, .base = 0x2800000 },
|
||||
{ .type = WMFW_ADSP2_YM, .base = 0x3400000 },
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_halo_dsp1_regions, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/* List of sizes in bytes, for each entry above */
|
||||
const unsigned int cs_dsp_mock_halo_dsp1_region_sizes[] = {
|
||||
0x5000, /* PM_PACKED */
|
||||
0x6000, /* XM_PACKED */
|
||||
0x47F4, /* YM_PACKED */
|
||||
0x8000, /* XM_UNPACKED_24 */
|
||||
0x5FF8, /* YM_UNPACKED_24 */
|
||||
|
||||
0 /* terminator */
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_halo_dsp1_region_sizes, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
const struct cs_dsp_region cs_dsp_mock_adsp2_32bit_dsp1_regions[] = {
|
||||
{ .type = WMFW_ADSP2_PM, .base = 0x080000 },
|
||||
{ .type = WMFW_ADSP2_XM, .base = 0x0a0000 },
|
||||
{ .type = WMFW_ADSP2_YM, .base = 0x0c0000 },
|
||||
{ .type = WMFW_ADSP2_ZM, .base = 0x0e0000 },
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_32bit_dsp1_regions, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/* List of sizes in bytes, for each entry above */
|
||||
const unsigned int cs_dsp_mock_adsp2_32bit_dsp1_region_sizes[] = {
|
||||
0x9000, /* PM */
|
||||
0xa000, /* ZM */
|
||||
0x2000, /* XM */
|
||||
0x2000, /* YM */
|
||||
|
||||
0 /* terminator */
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_32bit_dsp1_region_sizes, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
const struct cs_dsp_region cs_dsp_mock_adsp2_16bit_dsp1_regions[] = {
|
||||
{ .type = WMFW_ADSP2_PM, .base = 0x100000 },
|
||||
{ .type = WMFW_ADSP2_ZM, .base = 0x180000 },
|
||||
{ .type = WMFW_ADSP2_XM, .base = 0x190000 },
|
||||
{ .type = WMFW_ADSP2_YM, .base = 0x1a8000 },
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_16bit_dsp1_regions, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/* List of sizes in bytes, for each entry above */
|
||||
const unsigned int cs_dsp_mock_adsp2_16bit_dsp1_region_sizes[] = {
|
||||
0x6000, /* PM */
|
||||
0x800, /* ZM */
|
||||
0x800, /* XM */
|
||||
0x800, /* YM */
|
||||
|
||||
0 /* terminator */
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_16bit_dsp1_region_sizes, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
int cs_dsp_mock_count_regions(const unsigned int *region_sizes)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; region_sizes[i]; ++i)
|
||||
;
|
||||
|
||||
return i;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_count_regions, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_size_of_region() - Return size of given memory region.
|
||||
*
|
||||
* @dsp: Pointer to struct cs_dsp.
|
||||
* @mem_type: Memory region type.
|
||||
*
|
||||
* Return: Size of region in bytes.
|
||||
*/
|
||||
unsigned int cs_dsp_mock_size_of_region(const struct cs_dsp *dsp, int mem_type)
|
||||
{
|
||||
const unsigned int *sizes;
|
||||
int i;
|
||||
|
||||
if (dsp->mem == cs_dsp_mock_halo_dsp1_regions)
|
||||
sizes = cs_dsp_mock_halo_dsp1_region_sizes;
|
||||
else if (dsp->mem == cs_dsp_mock_adsp2_32bit_dsp1_regions)
|
||||
sizes = cs_dsp_mock_adsp2_32bit_dsp1_region_sizes;
|
||||
else if (dsp->mem == cs_dsp_mock_adsp2_16bit_dsp1_regions)
|
||||
sizes = cs_dsp_mock_adsp2_16bit_dsp1_region_sizes;
|
||||
else
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < dsp->num_mems; ++i) {
|
||||
if (dsp->mem[i].type == mem_type)
|
||||
return sizes[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_size_of_region, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_base_addr_for_mem() - Base register address for memory region.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
* @mem_type: Memory region type.
|
||||
*
|
||||
* Return: Base register address of region.
|
||||
*/
|
||||
unsigned int cs_dsp_mock_base_addr_for_mem(struct cs_dsp_test *priv, int mem_type)
|
||||
{
|
||||
int num_mems = priv->dsp->num_mems;
|
||||
const struct cs_dsp_region *region = priv->dsp->mem;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_mems; ++i) {
|
||||
if (region[i].type == mem_type)
|
||||
return region[i].base;
|
||||
}
|
||||
|
||||
KUNIT_FAIL(priv->test, "Unexpected region %d\n", mem_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_base_addr_for_mem, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_reg_addr_inc_per_unpacked_word() - Unpacked register address increment per DSP word.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
*
|
||||
* Return: Amount by which register address increments to move to the next
|
||||
* DSP word in unpacked XM/YM/ZM.
|
||||
*/
|
||||
unsigned int cs_dsp_mock_reg_addr_inc_per_unpacked_word(struct cs_dsp_test *priv)
|
||||
{
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
return 2; /* two 16-bit register indexes per XM/YM/ZM word */
|
||||
case WMFW_HALO:
|
||||
return 4; /* one byte-addressed 32-bit register per XM/YM/ZM word */
|
||||
default:
|
||||
KUNIT_FAIL(priv->test, "Unexpected DSP type\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_reg_addr_inc_per_unpacked_word, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_reg_block_length_bytes() - Number of bytes in an access block.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
* @mem_type: Memory region type.
|
||||
*
|
||||
* Return: Total number of bytes in a group of registers forming the
|
||||
* smallest bus access size (including any padding bits). For unpacked
|
||||
* memory this is the number of registers containing one DSP word.
|
||||
* For packed memory this is the number of registers in one packed
|
||||
* access block.
|
||||
*/
|
||||
unsigned int cs_dsp_mock_reg_block_length_bytes(struct cs_dsp_test *priv, int mem_type)
|
||||
{
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
switch (mem_type) {
|
||||
case WMFW_ADSP2_PM:
|
||||
return 3 * regmap_get_val_bytes(priv->dsp->regmap);
|
||||
case WMFW_ADSP2_XM:
|
||||
case WMFW_ADSP2_YM:
|
||||
case WMFW_ADSP2_ZM:
|
||||
return sizeof(u32);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WMFW_HALO:
|
||||
switch (mem_type) {
|
||||
case WMFW_ADSP2_XM:
|
||||
case WMFW_ADSP2_YM:
|
||||
return sizeof(u32);
|
||||
case WMFW_HALO_PM_PACKED:
|
||||
return 5 * sizeof(u32);
|
||||
case WMFW_HALO_XM_PACKED:
|
||||
case WMFW_HALO_YM_PACKED:
|
||||
return 3 * sizeof(u32);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
KUNIT_FAIL(priv->test, "Unexpected DSP type\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
KUNIT_FAIL(priv->test, "Unexpected mem type\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_reg_block_length_bytes, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_reg_block_length_registers() - Number of registers in an access block.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
* @mem_type: Memory region type.
|
||||
*
|
||||
* Return: Total number of register forming the smallest bus access size.
|
||||
* For unpacked memory this is the number of registers containing one
|
||||
* DSP word. For packed memory this is the number of registers in one
|
||||
* packed access block.
|
||||
*/
|
||||
unsigned int cs_dsp_mock_reg_block_length_registers(struct cs_dsp_test *priv, int mem_type)
|
||||
{
|
||||
return cs_dsp_mock_reg_block_length_bytes(priv, mem_type) /
|
||||
regmap_get_val_bytes(priv->dsp->regmap);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_reg_block_length_registers, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_reg_block_length_dsp_words() - Number of dsp_words in an access block.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
* @mem_type: Memory region type.
|
||||
*
|
||||
* Return: Total number of DSP words in a group of registers forming the
|
||||
* smallest bus access size.
|
||||
*/
|
||||
unsigned int cs_dsp_mock_reg_block_length_dsp_words(struct cs_dsp_test *priv, int mem_type)
|
||||
{
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
switch (mem_type) {
|
||||
case WMFW_ADSP2_PM:
|
||||
return regmap_get_val_bytes(priv->dsp->regmap) / 2;
|
||||
case WMFW_ADSP2_XM:
|
||||
case WMFW_ADSP2_YM:
|
||||
case WMFW_ADSP2_ZM:
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WMFW_HALO:
|
||||
switch (mem_type) {
|
||||
case WMFW_ADSP2_XM:
|
||||
case WMFW_ADSP2_YM:
|
||||
return 1;
|
||||
case WMFW_HALO_PM_PACKED:
|
||||
case WMFW_HALO_XM_PACKED:
|
||||
case WMFW_HALO_YM_PACKED:
|
||||
return 4;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
KUNIT_FAIL(priv->test, "Unexpected DSP type\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
KUNIT_FAIL(priv->test, "Unexpected mem type\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_reg_block_length_dsp_words, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_has_zm() - DSP has ZM
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
*
|
||||
* Return: True if DSP has ZM.
|
||||
*/
|
||||
bool cs_dsp_mock_has_zm(struct cs_dsp_test *priv)
|
||||
{
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_has_zm, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_packed_to_unpacked_mem_type() - Unpacked region that is
|
||||
* the same memory as a packed region.
|
||||
*
|
||||
* @packed_mem_type: Type of packed memory region.
|
||||
*
|
||||
* Return: unpacked type that is the same memory as packed_mem_type.
|
||||
*/
|
||||
int cs_dsp_mock_packed_to_unpacked_mem_type(int packed_mem_type)
|
||||
{
|
||||
switch (packed_mem_type) {
|
||||
case WMFW_HALO_XM_PACKED:
|
||||
return WMFW_ADSP2_XM;
|
||||
case WMFW_HALO_YM_PACKED:
|
||||
return WMFW_ADSP2_YM;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_packed_to_unpacked_mem_type, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_num_dsp_words_to_num_packed_regs() - Number of DSP words
|
||||
* to number of packed registers.
|
||||
*
|
||||
* @num_dsp_words: Number of DSP words.
|
||||
*
|
||||
* Convert number of DSP words to number of packed registers rounded
|
||||
* down to the nearest register.
|
||||
*
|
||||
* Return: Number of packed registers.
|
||||
*/
|
||||
unsigned int cs_dsp_mock_num_dsp_words_to_num_packed_regs(unsigned int num_dsp_words)
|
||||
{
|
||||
/* There are 3 registers for every 4 packed words */
|
||||
return (num_dsp_words * 3) / 4;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_num_dsp_words_to_num_packed_regs, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
static const struct wmfw_halo_id_hdr cs_dsp_mock_halo_xm_hdr = {
|
||||
.fw = {
|
||||
.core_id = cpu_to_be32(WMFW_HALO << 16),
|
||||
.block_rev = cpu_to_be32(3 << 16),
|
||||
.vendor_id = cpu_to_be32(0x2),
|
||||
.id = cpu_to_be32(0xabcdef),
|
||||
.ver = cpu_to_be32(0x090101),
|
||||
},
|
||||
|
||||
/*
|
||||
* Leave enough space for this header and 40 algorithm descriptors.
|
||||
* base and size are counted in DSP words.
|
||||
*/
|
||||
.xm_base = cpu_to_be32(((sizeof(struct wmfw_halo_id_hdr) +
|
||||
(40 * sizeof(struct wmfw_halo_alg_hdr)))
|
||||
/ 4) * 3),
|
||||
.xm_size = cpu_to_be32(0x20),
|
||||
|
||||
/* Allocate a dummy word of YM */
|
||||
.ym_base = cpu_to_be32(0),
|
||||
.ym_size = cpu_to_be32(1),
|
||||
|
||||
.n_algs = 0,
|
||||
};
|
||||
|
||||
static const struct wmfw_adsp2_id_hdr cs_dsp_mock_adsp2_xm_hdr = {
|
||||
.fw = {
|
||||
.core_id = cpu_to_be32(WMFW_ADSP2 << 16),
|
||||
.core_rev = cpu_to_be32(2 << 16),
|
||||
.id = cpu_to_be32(0xabcdef),
|
||||
.ver = cpu_to_be32(0x090101),
|
||||
},
|
||||
|
||||
/*
|
||||
* Leave enough space for this header and 40 algorithm descriptors.
|
||||
* base and size are counted in DSP words.
|
||||
*/
|
||||
.xm = cpu_to_be32(((sizeof(struct wmfw_adsp2_id_hdr) +
|
||||
(40 * sizeof(struct wmfw_adsp2_alg_hdr)))
|
||||
/ 4) * 3),
|
||||
|
||||
.ym = cpu_to_be32(0),
|
||||
.zm = cpu_to_be32(0),
|
||||
|
||||
.n_algs = 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_xm_header_get_alg_base_in_words() - Algorithm base offset in DSP words.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
* @alg_id: Algorithm ID.
|
||||
* @mem_type: Memory region type.
|
||||
*
|
||||
* Lookup an algorithm in the XM header and return the base offset in
|
||||
* DSP words of the algorithm data in the requested memory region.
|
||||
*
|
||||
* Return: Offset in DSP words.
|
||||
*/
|
||||
unsigned int cs_dsp_mock_xm_header_get_alg_base_in_words(struct cs_dsp_test *priv,
|
||||
unsigned int alg_id,
|
||||
int mem_type)
|
||||
{
|
||||
unsigned int xm = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_XM);
|
||||
union {
|
||||
struct wmfw_adsp2_alg_hdr adsp2;
|
||||
struct wmfw_halo_alg_hdr halo;
|
||||
} alg;
|
||||
unsigned int alg_hdr_addr;
|
||||
unsigned int val, xm_base = 0, ym_base = 0, zm_base = 0;
|
||||
int ret;
|
||||
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
alg_hdr_addr = xm + (sizeof(struct wmfw_adsp2_id_hdr) / 2);
|
||||
for (;; alg_hdr_addr += sizeof(alg.adsp2) / 2) {
|
||||
ret = regmap_read(priv->dsp->regmap, alg_hdr_addr, &val);
|
||||
KUNIT_ASSERT_GE(priv->test, ret, 0);
|
||||
KUNIT_ASSERT_NE(priv->test, val, 0xbedead);
|
||||
ret = regmap_raw_read(priv->dsp->regmap, alg_hdr_addr,
|
||||
&alg.adsp2, sizeof(alg.adsp2));
|
||||
KUNIT_ASSERT_GE(priv->test, ret, 0);
|
||||
if (be32_to_cpu(alg.adsp2.alg.id) == alg_id) {
|
||||
xm_base = be32_to_cpu(alg.adsp2.xm);
|
||||
ym_base = be32_to_cpu(alg.adsp2.ym);
|
||||
zm_base = be32_to_cpu(alg.adsp2.zm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WMFW_HALO:
|
||||
alg_hdr_addr = xm + sizeof(struct wmfw_halo_id_hdr);
|
||||
for (;; alg_hdr_addr += sizeof(alg.halo)) {
|
||||
ret = regmap_read(priv->dsp->regmap, alg_hdr_addr, &val);
|
||||
KUNIT_ASSERT_GE(priv->test, ret, 0);
|
||||
KUNIT_ASSERT_NE(priv->test, val, 0xbedead);
|
||||
ret = regmap_raw_read(priv->dsp->regmap, alg_hdr_addr,
|
||||
&alg.halo, sizeof(alg.halo));
|
||||
KUNIT_ASSERT_GE(priv->test, ret, 0);
|
||||
if (be32_to_cpu(alg.halo.alg.id) == alg_id) {
|
||||
xm_base = be32_to_cpu(alg.halo.xm_base);
|
||||
ym_base = be32_to_cpu(alg.halo.ym_base);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
KUNIT_FAIL(priv->test, "Unexpected DSP type %d\n", priv->dsp->type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (mem_type) {
|
||||
case WMFW_ADSP2_XM:
|
||||
case WMFW_HALO_XM_PACKED:
|
||||
return xm_base;
|
||||
case WMFW_ADSP2_YM:
|
||||
case WMFW_HALO_YM_PACKED:
|
||||
return ym_base;
|
||||
case WMFW_ADSP2_ZM:
|
||||
return zm_base;
|
||||
default:
|
||||
KUNIT_FAIL(priv->test, "Bad mem_type\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_get_alg_base_in_words, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_xm_header_get_fw_version_from_regmap() - Firmware version.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
*
|
||||
* Return: Firmware version word value.
|
||||
*/
|
||||
unsigned int cs_dsp_mock_xm_header_get_fw_version_from_regmap(struct cs_dsp_test *priv)
|
||||
{
|
||||
unsigned int xm = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_XM);
|
||||
union {
|
||||
struct wmfw_id_hdr adsp2;
|
||||
struct wmfw_v3_id_hdr halo;
|
||||
} hdr;
|
||||
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
regmap_raw_read(priv->dsp->regmap, xm, &hdr.adsp2, sizeof(hdr.adsp2));
|
||||
return be32_to_cpu(hdr.adsp2.ver);
|
||||
case WMFW_HALO:
|
||||
regmap_raw_read(priv->dsp->regmap, xm, &hdr.halo, sizeof(hdr.halo));
|
||||
return be32_to_cpu(hdr.halo.ver);
|
||||
default:
|
||||
KUNIT_FAIL(priv->test, NULL);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_get_fw_version_from_regmap,
|
||||
"FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_xm_header_get_fw_version() - Firmware version.
|
||||
*
|
||||
* @header: Pointer to struct cs_dsp_mock_xm_header.
|
||||
*
|
||||
* Return: Firmware version word value.
|
||||
*/
|
||||
unsigned int cs_dsp_mock_xm_header_get_fw_version(struct cs_dsp_mock_xm_header *header)
|
||||
{
|
||||
const struct wmfw_id_hdr *adsp2_hdr;
|
||||
const struct wmfw_v3_id_hdr *halo_hdr;
|
||||
|
||||
switch (header->test_priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
adsp2_hdr = header->blob_data;
|
||||
return be32_to_cpu(adsp2_hdr->ver);
|
||||
case WMFW_HALO:
|
||||
halo_hdr = header->blob_data;
|
||||
return be32_to_cpu(halo_hdr->ver);
|
||||
default:
|
||||
KUNIT_FAIL(header->test_priv->test, NULL);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_get_fw_version, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_xm_header_drop_from_regmap_cache() - Drop XM header from regmap cache.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
*/
|
||||
void cs_dsp_mock_xm_header_drop_from_regmap_cache(struct cs_dsp_test *priv)
|
||||
{
|
||||
unsigned int xm = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_XM);
|
||||
unsigned int bytes;
|
||||
u32 num_algs;
|
||||
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
/*
|
||||
* Could be one 32-bit register or two 16-bit registers.
|
||||
* A raw read will read the requested number of bytes.
|
||||
*/
|
||||
regmap_raw_read(priv->dsp->regmap,
|
||||
xm + (offsetof(struct wmfw_adsp2_id_hdr, n_algs) / 2),
|
||||
&num_algs, sizeof(num_algs));
|
||||
num_algs = be32_to_cpu(num_algs);
|
||||
bytes = sizeof(struct wmfw_adsp2_id_hdr) +
|
||||
(num_algs * sizeof(struct wmfw_adsp2_alg_hdr)) +
|
||||
4 /* terminator word */;
|
||||
|
||||
regcache_drop_region(priv->dsp->regmap, xm, xm + (bytes / 2) - 1);
|
||||
break;
|
||||
case WMFW_HALO:
|
||||
regmap_read(priv->dsp->regmap,
|
||||
xm + offsetof(struct wmfw_halo_id_hdr, n_algs),
|
||||
&num_algs);
|
||||
bytes = sizeof(struct wmfw_halo_id_hdr) +
|
||||
(num_algs * sizeof(struct wmfw_halo_alg_hdr)) +
|
||||
4 /* terminator word */;
|
||||
|
||||
regcache_drop_region(priv->dsp->regmap, xm, xm + bytes - 4);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_drop_from_regmap_cache, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
static void cs_dsp_mock_xm_header_add_adsp2_algs(struct cs_dsp_mock_xm_header *builder,
|
||||
const struct cs_dsp_mock_alg_def *algs,
|
||||
size_t num_algs)
|
||||
{
|
||||
struct wmfw_adsp2_id_hdr *hdr = builder->blob_data;
|
||||
unsigned int next_free_xm_word, next_free_ym_word, next_free_zm_word;
|
||||
|
||||
next_free_xm_word = be32_to_cpu(hdr->xm);
|
||||
next_free_ym_word = be32_to_cpu(hdr->ym);
|
||||
next_free_zm_word = be32_to_cpu(hdr->zm);
|
||||
|
||||
/* Set num_algs in XM header. */
|
||||
hdr->n_algs = cpu_to_be32(num_algs);
|
||||
|
||||
/* Create algorithm descriptor list */
|
||||
struct wmfw_adsp2_alg_hdr *alg_info =
|
||||
(struct wmfw_adsp2_alg_hdr *)(&hdr[1]);
|
||||
|
||||
for (; num_algs > 0; num_algs--, algs++, alg_info++) {
|
||||
unsigned int alg_xm_last, alg_ym_last, alg_zm_last;
|
||||
|
||||
alg_info->alg.id = cpu_to_be32(algs->id);
|
||||
alg_info->alg.ver = cpu_to_be32(algs->ver);
|
||||
alg_info->xm = cpu_to_be32(algs->xm_base_words);
|
||||
alg_info->ym = cpu_to_be32(algs->ym_base_words);
|
||||
alg_info->zm = cpu_to_be32(algs->zm_base_words);
|
||||
|
||||
/* Check if we need to auto-allocate base addresses */
|
||||
if (!alg_info->xm && algs->xm_size_words)
|
||||
alg_info->xm = cpu_to_be32(next_free_xm_word);
|
||||
|
||||
if (!alg_info->ym && algs->ym_size_words)
|
||||
alg_info->ym = cpu_to_be32(next_free_ym_word);
|
||||
|
||||
if (!alg_info->zm && algs->zm_size_words)
|
||||
alg_info->zm = cpu_to_be32(next_free_zm_word);
|
||||
|
||||
alg_xm_last = be32_to_cpu(alg_info->xm) + algs->xm_size_words - 1;
|
||||
if (alg_xm_last > next_free_xm_word)
|
||||
next_free_xm_word = alg_xm_last;
|
||||
|
||||
alg_ym_last = be32_to_cpu(alg_info->ym) + algs->ym_size_words - 1;
|
||||
if (alg_ym_last > next_free_ym_word)
|
||||
next_free_ym_word = alg_ym_last;
|
||||
|
||||
alg_zm_last = be32_to_cpu(alg_info->zm) + algs->zm_size_words - 1;
|
||||
if (alg_zm_last > next_free_zm_word)
|
||||
next_free_zm_word = alg_zm_last;
|
||||
}
|
||||
|
||||
/* Write list terminator */
|
||||
*(__be32 *)(alg_info) = cpu_to_be32(0xbedead);
|
||||
}
|
||||
|
||||
static void cs_dsp_mock_xm_header_add_halo_algs(struct cs_dsp_mock_xm_header *builder,
|
||||
const struct cs_dsp_mock_alg_def *algs,
|
||||
size_t num_algs)
|
||||
{
|
||||
struct wmfw_halo_id_hdr *hdr = builder->blob_data;
|
||||
unsigned int next_free_xm_word, next_free_ym_word;
|
||||
|
||||
/* Assume we're starting with bare header */
|
||||
next_free_xm_word = be32_to_cpu(hdr->xm_base) + be32_to_cpu(hdr->xm_size) - 1;
|
||||
next_free_ym_word = be32_to_cpu(hdr->ym_base) + be32_to_cpu(hdr->ym_size) - 1;
|
||||
|
||||
/* Set num_algs in XM header */
|
||||
hdr->n_algs = cpu_to_be32(num_algs);
|
||||
|
||||
/* Create algorithm descriptor list */
|
||||
struct wmfw_halo_alg_hdr *alg_info =
|
||||
(struct wmfw_halo_alg_hdr *)(&hdr[1]);
|
||||
|
||||
for (; num_algs > 0; num_algs--, algs++, alg_info++) {
|
||||
unsigned int alg_xm_last, alg_ym_last;
|
||||
|
||||
alg_info->alg.id = cpu_to_be32(algs->id);
|
||||
alg_info->alg.ver = cpu_to_be32(algs->ver);
|
||||
alg_info->xm_base = cpu_to_be32(algs->xm_base_words);
|
||||
alg_info->xm_size = cpu_to_be32(algs->xm_size_words);
|
||||
alg_info->ym_base = cpu_to_be32(algs->ym_base_words);
|
||||
alg_info->ym_size = cpu_to_be32(algs->ym_size_words);
|
||||
|
||||
/* Check if we need to auto-allocate base addresses */
|
||||
if (!alg_info->xm_base && alg_info->xm_size)
|
||||
alg_info->xm_base = cpu_to_be32(next_free_xm_word);
|
||||
|
||||
if (!alg_info->ym_base && alg_info->ym_size)
|
||||
alg_info->ym_base = cpu_to_be32(next_free_ym_word);
|
||||
|
||||
alg_xm_last = be32_to_cpu(alg_info->xm_base) + be32_to_cpu(alg_info->xm_size) - 1;
|
||||
if (alg_xm_last > next_free_xm_word)
|
||||
next_free_xm_word = alg_xm_last;
|
||||
|
||||
alg_ym_last = be32_to_cpu(alg_info->ym_base) + be32_to_cpu(alg_info->ym_size) - 1;
|
||||
if (alg_ym_last > next_free_ym_word)
|
||||
next_free_ym_word = alg_ym_last;
|
||||
}
|
||||
|
||||
/* Write list terminator */
|
||||
*(__be32 *)(alg_info) = cpu_to_be32(0xbedead);
|
||||
}
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_xm_header_write_to_regmap() - Write XM header to regmap.
|
||||
*
|
||||
* @header: Pointer to struct cs_dsp_mock_xm_header.
|
||||
*
|
||||
* The data in header is written to the XM addresses in the regmap.
|
||||
*
|
||||
* Return: 0 on success, else negative error code.
|
||||
*/
|
||||
int cs_dsp_mock_xm_header_write_to_regmap(struct cs_dsp_mock_xm_header *header)
|
||||
{
|
||||
struct cs_dsp_test *priv = header->test_priv;
|
||||
unsigned int reg_addr = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_XM);
|
||||
|
||||
/*
|
||||
* One 32-bit word corresponds to one 32-bit unpacked XM word so the
|
||||
* blob can be written directly to the regmap.
|
||||
*/
|
||||
return regmap_raw_write(priv->dsp->regmap, reg_addr,
|
||||
header->blob_data, header->blob_size_bytes);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_write_to_regmap, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_create_mock_xm_header() - Create a dummy XM header.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
* @algs: Pointer to array of struct cs_dsp_mock_alg_def listing the
|
||||
* dummy algorithm entries to include in the XM header.
|
||||
* @num_algs: Number of entries in the algs array.
|
||||
*
|
||||
* Return: Pointer to created struct cs_dsp_mock_xm_header.
|
||||
*/
|
||||
struct cs_dsp_mock_xm_header *cs_dsp_create_mock_xm_header(struct cs_dsp_test *priv,
|
||||
const struct cs_dsp_mock_alg_def *algs,
|
||||
size_t num_algs)
|
||||
{
|
||||
struct cs_dsp_mock_xm_header *builder;
|
||||
size_t total_bytes_required;
|
||||
const void *header;
|
||||
size_t header_size_bytes;
|
||||
|
||||
builder = kunit_kzalloc(priv->test, sizeof(*builder), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(priv->test, builder);
|
||||
builder->test_priv = priv;
|
||||
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
header = &cs_dsp_mock_adsp2_xm_hdr;
|
||||
header_size_bytes = sizeof(cs_dsp_mock_adsp2_xm_hdr);
|
||||
total_bytes_required = header_size_bytes +
|
||||
(num_algs * sizeof(struct wmfw_adsp2_alg_hdr))
|
||||
+ 4; /* terminator word */
|
||||
break;
|
||||
case WMFW_HALO:
|
||||
header = &cs_dsp_mock_halo_xm_hdr,
|
||||
header_size_bytes = sizeof(cs_dsp_mock_halo_xm_hdr);
|
||||
total_bytes_required = header_size_bytes +
|
||||
(num_algs * sizeof(struct wmfw_halo_alg_hdr))
|
||||
+ 4; /* terminator word */
|
||||
break;
|
||||
default:
|
||||
KUNIT_FAIL(priv->test, "%s unexpected DSP type %d\n",
|
||||
__func__, priv->dsp->type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
builder->blob_data = kunit_kzalloc(priv->test, total_bytes_required, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(priv->test, builder->blob_data);
|
||||
builder->blob_size_bytes = total_bytes_required;
|
||||
|
||||
memcpy(builder->blob_data, header, header_size_bytes);
|
||||
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
cs_dsp_mock_xm_header_add_adsp2_algs(builder, algs, num_algs);
|
||||
break;
|
||||
case WMFW_HALO:
|
||||
cs_dsp_mock_xm_header_add_halo_algs(builder, algs, num_algs);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_create_mock_xm_header, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
367
drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c
Normal file
367
drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c
Normal file
@@ -0,0 +1,367 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// Mock regmap for cs_dsp KUnit tests.
|
||||
//
|
||||
// Copyright (C) 2024 Cirrus Logic, Inc. and
|
||||
// Cirrus Logic International Semiconductor Ltd.
|
||||
|
||||
#include <kunit/test.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/regmap.h>
|
||||
|
||||
static int cs_dsp_mock_regmap_read(void *context, const void *reg_buf,
|
||||
const size_t reg_size, void *val_buf,
|
||||
size_t val_size)
|
||||
{
|
||||
struct cs_dsp_test *priv = context;
|
||||
|
||||
/* Should never get here because the regmap is cache-only */
|
||||
KUNIT_FAIL(priv->test, "Unexpected bus read @%#x", *(u32 *)reg_buf);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int cs_dsp_mock_regmap_gather_write(void *context,
|
||||
const void *reg_buf, size_t reg_size,
|
||||
const void *val_buf, size_t val_size)
|
||||
{
|
||||
struct cs_dsp_test *priv = context;
|
||||
|
||||
priv->saw_bus_write = true;
|
||||
|
||||
/* Should never get here because the regmap is cache-only */
|
||||
KUNIT_FAIL(priv->test, "Unexpected bus gather_write @%#x", *(u32 *)reg_buf);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int cs_dsp_mock_regmap_write(void *context, const void *val_buf, size_t val_size)
|
||||
{
|
||||
struct cs_dsp_test *priv = context;
|
||||
|
||||
priv->saw_bus_write = true;
|
||||
|
||||
/* Should never get here because the regmap is cache-only */
|
||||
KUNIT_FAIL(priv->test, "Unexpected bus write @%#x", *(u32 *)val_buf);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static const struct regmap_bus cs_dsp_mock_regmap_bus = {
|
||||
.read = cs_dsp_mock_regmap_read,
|
||||
.write = cs_dsp_mock_regmap_write,
|
||||
.gather_write = cs_dsp_mock_regmap_gather_write,
|
||||
.reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
|
||||
.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
|
||||
};
|
||||
|
||||
static const struct reg_default adsp2_32bit_register_defaults[] = {
|
||||
{ 0xffe00, 0x0000 }, /* CONTROL */
|
||||
{ 0xffe02, 0x0000 }, /* CLOCKING */
|
||||
{ 0xffe04, 0x0001 }, /* STATUS1: RAM_RDY=1 */
|
||||
{ 0xffe30, 0x0000 }, /* WDMW_CONFIG_1 */
|
||||
{ 0xffe32, 0x0000 }, /* WDMA_CONFIG_2 */
|
||||
{ 0xffe34, 0x0000 }, /* RDMA_CONFIG_1 */
|
||||
{ 0xffe40, 0x0000 }, /* SCRATCH_0_1 */
|
||||
{ 0xffe42, 0x0000 }, /* SCRATCH_2_3 */
|
||||
};
|
||||
|
||||
static const struct regmap_range adsp2_32bit_registers[] = {
|
||||
regmap_reg_range(0x80000, 0x88ffe), /* PM */
|
||||
regmap_reg_range(0xa0000, 0xa9ffe), /* XM */
|
||||
regmap_reg_range(0xc0000, 0xc1ffe), /* YM */
|
||||
regmap_reg_range(0xe0000, 0xe1ffe), /* ZM */
|
||||
regmap_reg_range(0xffe00, 0xffe7c), /* CORE CTRL */
|
||||
};
|
||||
|
||||
const unsigned int cs_dsp_mock_adsp2_32bit_sysbase = 0xffe00;
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_32bit_sysbase, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
static const struct regmap_access_table adsp2_32bit_rw = {
|
||||
.yes_ranges = adsp2_32bit_registers,
|
||||
.n_yes_ranges = ARRAY_SIZE(adsp2_32bit_registers),
|
||||
};
|
||||
|
||||
static const struct regmap_config cs_dsp_mock_regmap_adsp2_32bit = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 2,
|
||||
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
.val_format_endian = REGMAP_ENDIAN_BIG,
|
||||
.wr_table = &adsp2_32bit_rw,
|
||||
.rd_table = &adsp2_32bit_rw,
|
||||
.max_register = 0xffe7c,
|
||||
.reg_defaults = adsp2_32bit_register_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(adsp2_32bit_register_defaults),
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
};
|
||||
|
||||
static const struct reg_default adsp2_16bit_register_defaults[] = {
|
||||
{ 0x1100, 0x0000 }, /* CONTROL */
|
||||
{ 0x1101, 0x0000 }, /* CLOCKING */
|
||||
{ 0x1104, 0x0001 }, /* STATUS1: RAM_RDY=1 */
|
||||
{ 0x1130, 0x0000 }, /* WDMW_CONFIG_1 */
|
||||
{ 0x1131, 0x0000 }, /* WDMA_CONFIG_2 */
|
||||
{ 0x1134, 0x0000 }, /* RDMA_CONFIG_1 */
|
||||
{ 0x1140, 0x0000 }, /* SCRATCH_0 */
|
||||
{ 0x1141, 0x0000 }, /* SCRATCH_1 */
|
||||
{ 0x1142, 0x0000 }, /* SCRATCH_2 */
|
||||
{ 0x1143, 0x0000 }, /* SCRATCH_3 */
|
||||
};
|
||||
|
||||
static const struct regmap_range adsp2_16bit_registers[] = {
|
||||
regmap_reg_range(0x001100, 0x001143), /* CORE CTRL */
|
||||
regmap_reg_range(0x100000, 0x105fff), /* PM */
|
||||
regmap_reg_range(0x180000, 0x1807ff), /* ZM */
|
||||
regmap_reg_range(0x190000, 0x1947ff), /* XM */
|
||||
regmap_reg_range(0x1a8000, 0x1a97ff), /* YM */
|
||||
};
|
||||
|
||||
const unsigned int cs_dsp_mock_adsp2_16bit_sysbase = 0x001100;
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_16bit_sysbase, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
static const struct regmap_access_table adsp2_16bit_rw = {
|
||||
.yes_ranges = adsp2_16bit_registers,
|
||||
.n_yes_ranges = ARRAY_SIZE(adsp2_16bit_registers),
|
||||
};
|
||||
|
||||
static const struct regmap_config cs_dsp_mock_regmap_adsp2_16bit = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 16,
|
||||
.reg_stride = 1,
|
||||
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
.val_format_endian = REGMAP_ENDIAN_BIG,
|
||||
.wr_table = &adsp2_16bit_rw,
|
||||
.rd_table = &adsp2_16bit_rw,
|
||||
.max_register = 0x1a97ff,
|
||||
.reg_defaults = adsp2_16bit_register_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(adsp2_16bit_register_defaults),
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
};
|
||||
|
||||
static const struct reg_default halo_register_defaults[] = {
|
||||
/* CORE */
|
||||
{ 0x2b80010, 0 }, /* HALO_CORE_SOFT_RESET */
|
||||
{ 0x2b805c0, 0 }, /* HALO_SCRATCH1 */
|
||||
{ 0x2b805c8, 0 }, /* HALO_SCRATCH2 */
|
||||
{ 0x2b805d0, 0 }, /* HALO_SCRATCH3 */
|
||||
{ 0x2b805c8, 0 }, /* HALO_SCRATCH4 */
|
||||
{ 0x2bc1000, 0 }, /* HALO_CCM_CORE_CONTROL */
|
||||
{ 0x2bc7000, 0 }, /* HALO_WDT_CONTROL */
|
||||
|
||||
/* SYSINFO */
|
||||
{ 0x25e2040, 0 }, /* HALO_AHBM_WINDOW_DEBUG_0 */
|
||||
{ 0x25e2044, 0 }, /* HALO_AHBM_WINDOW_DEBUG_1 */
|
||||
};
|
||||
|
||||
static const struct regmap_range halo_readable_registers[] = {
|
||||
regmap_reg_range(0x2000000, 0x2005fff), /* XM_PACKED */
|
||||
regmap_reg_range(0x25e0000, 0x25e004f), /* SYSINFO */
|
||||
regmap_reg_range(0x25e2000, 0x25e2047), /* SYSINFO */
|
||||
regmap_reg_range(0x2800000, 0x2807fff), /* XM */
|
||||
regmap_reg_range(0x2b80000, 0x2bc700b), /* CORE CTRL */
|
||||
regmap_reg_range(0x2c00000, 0x2c047f3), /* YM_PACKED */
|
||||
regmap_reg_range(0x3400000, 0x3405ff7), /* YM */
|
||||
regmap_reg_range(0x3800000, 0x3804fff), /* PM_PACKED */
|
||||
};
|
||||
|
||||
static const struct regmap_range halo_writeable_registers[] = {
|
||||
regmap_reg_range(0x2000000, 0x2005fff), /* XM_PACKED */
|
||||
regmap_reg_range(0x2800000, 0x2807fff), /* XM */
|
||||
regmap_reg_range(0x2b80000, 0x2bc700b), /* CORE CTRL */
|
||||
regmap_reg_range(0x2c00000, 0x2c047f3), /* YM_PACKED */
|
||||
regmap_reg_range(0x3400000, 0x3405ff7), /* YM */
|
||||
regmap_reg_range(0x3800000, 0x3804fff), /* PM_PACKED */
|
||||
};
|
||||
|
||||
const unsigned int cs_dsp_mock_halo_core_base = 0x2b80000;
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_halo_core_base, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
const unsigned int cs_dsp_mock_halo_sysinfo_base = 0x25e0000;
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_halo_sysinfo_base, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
static const struct regmap_access_table halo_readable = {
|
||||
.yes_ranges = halo_readable_registers,
|
||||
.n_yes_ranges = ARRAY_SIZE(halo_readable_registers),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table halo_writeable = {
|
||||
.yes_ranges = halo_writeable_registers,
|
||||
.n_yes_ranges = ARRAY_SIZE(halo_writeable_registers),
|
||||
};
|
||||
|
||||
static const struct regmap_config cs_dsp_mock_regmap_halo = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
.val_format_endian = REGMAP_ENDIAN_BIG,
|
||||
.wr_table = &halo_writeable,
|
||||
.rd_table = &halo_readable,
|
||||
.max_register = 0x3804ffc,
|
||||
.reg_defaults = halo_register_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(halo_register_defaults),
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
};
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_regmap_drop_range() - drop a range of registers from the cache.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test object.
|
||||
* @first_reg: Address of first register to drop.
|
||||
* @last_reg: Address of last register to drop.
|
||||
*/
|
||||
void cs_dsp_mock_regmap_drop_range(struct cs_dsp_test *priv,
|
||||
unsigned int first_reg, unsigned int last_reg)
|
||||
{
|
||||
regcache_drop_region(priv->dsp->regmap, first_reg, last_reg);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_drop_range, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_regmap_drop_regs() - drop a number of registers from the cache.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test object.
|
||||
* @first_reg: Address of first register to drop.
|
||||
* @num_regs: Number of registers to drop.
|
||||
*/
|
||||
void cs_dsp_mock_regmap_drop_regs(struct cs_dsp_test *priv,
|
||||
unsigned int first_reg, size_t num_regs)
|
||||
{
|
||||
int stride = regmap_get_reg_stride(priv->dsp->regmap);
|
||||
unsigned int last = first_reg + (stride * (num_regs - 1));
|
||||
|
||||
cs_dsp_mock_regmap_drop_range(priv, first_reg, last);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_drop_regs, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_regmap_drop_bytes() - drop a number of bytes from the cache.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test object.
|
||||
* @first_reg: Address of first register to drop.
|
||||
* @num_bytes: Number of bytes to drop from the cache. Will be rounded
|
||||
* down to a whole number of registers. Trailing bytes that
|
||||
* are not a multiple of the register size will not be dropped.
|
||||
* (This is intended to help detect math errors in test code.)
|
||||
*/
|
||||
void cs_dsp_mock_regmap_drop_bytes(struct cs_dsp_test *priv,
|
||||
unsigned int first_reg, size_t num_bytes)
|
||||
{
|
||||
size_t num_regs = num_bytes / regmap_get_val_bytes(priv->dsp->regmap);
|
||||
|
||||
cs_dsp_mock_regmap_drop_regs(priv, first_reg, num_regs);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_drop_bytes, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_regmap_drop_system_regs() - Drop DSP system registers from the cache.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test object.
|
||||
*
|
||||
* Drops all DSP system registers from the regmap cache.
|
||||
*/
|
||||
void cs_dsp_mock_regmap_drop_system_regs(struct cs_dsp_test *priv)
|
||||
{
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
if (priv->dsp->base) {
|
||||
regcache_drop_region(priv->dsp->regmap,
|
||||
priv->dsp->base,
|
||||
priv->dsp->base + 0x7c);
|
||||
}
|
||||
return;
|
||||
case WMFW_HALO:
|
||||
if (priv->dsp->base) {
|
||||
regcache_drop_region(priv->dsp->regmap,
|
||||
priv->dsp->base,
|
||||
priv->dsp->base + 0x47000);
|
||||
}
|
||||
|
||||
/* sysinfo registers are read-only so don't drop them */
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_drop_system_regs, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_regmap_is_dirty() - Test for dirty registers in the cache.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test object.
|
||||
* @drop_system_regs: If true the DSP system regs will be dropped from
|
||||
* the cache before checking for dirty.
|
||||
*
|
||||
* All registers that are expected to be written must have been dropped
|
||||
* from the cache (DSP system registers can be dropped by passing
|
||||
* drop_system_regs == true). If any unexpected registers were written
|
||||
* there will still be dirty entries in the cache and a cache sync will
|
||||
* cause a write.
|
||||
*
|
||||
* Returns: true if there were dirty entries, false if not.
|
||||
*/
|
||||
bool cs_dsp_mock_regmap_is_dirty(struct cs_dsp_test *priv, bool drop_system_regs)
|
||||
{
|
||||
if (drop_system_regs)
|
||||
cs_dsp_mock_regmap_drop_system_regs(priv);
|
||||
|
||||
priv->saw_bus_write = false;
|
||||
regcache_cache_only(priv->dsp->regmap, false);
|
||||
regcache_sync(priv->dsp->regmap);
|
||||
regcache_cache_only(priv->dsp->regmap, true);
|
||||
|
||||
return priv->saw_bus_write;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_is_dirty, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_regmap_init() - Initialize a mock regmap.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test object. This must have a
|
||||
* valid pointer to a struct cs_dsp in which the type and
|
||||
* rev fields are set to the type of DSP to be simulated.
|
||||
*
|
||||
* On success the priv->dsp->regmap will point to the created
|
||||
* regmap instance.
|
||||
*
|
||||
* Return: zero on success, else negative error code.
|
||||
*/
|
||||
int cs_dsp_mock_regmap_init(struct cs_dsp_test *priv)
|
||||
{
|
||||
const struct regmap_config *config;
|
||||
int ret;
|
||||
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_HALO:
|
||||
config = &cs_dsp_mock_regmap_halo;
|
||||
break;
|
||||
case WMFW_ADSP2:
|
||||
if (priv->dsp->rev == 0)
|
||||
config = &cs_dsp_mock_regmap_adsp2_16bit;
|
||||
else
|
||||
config = &cs_dsp_mock_regmap_adsp2_32bit;
|
||||
break;
|
||||
default:
|
||||
config = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
priv->dsp->regmap = devm_regmap_init(priv->dsp->dev,
|
||||
&cs_dsp_mock_regmap_bus,
|
||||
priv,
|
||||
config);
|
||||
if (IS_ERR(priv->dsp->regmap)) {
|
||||
ret = PTR_ERR(priv->dsp->regmap);
|
||||
kunit_err(priv->test, "Failed to allocate register map: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Put regmap in cache-only so it accumulates the writes done by cs_dsp */
|
||||
regcache_cache_only(priv->dsp->regmap, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_init, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
13
drivers/firmware/cirrus/test/cs_dsp_mock_utils.c
Normal file
13
drivers/firmware/cirrus/test/cs_dsp_mock_utils.c
Normal file
@@ -0,0 +1,13 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// Utility module for cs_dsp KUnit testing.
|
||||
//
|
||||
// Copyright (C) 2024 Cirrus Logic, Inc. and
|
||||
// Cirrus Logic International Semiconductor Ltd.
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
MODULE_DESCRIPTION("Utilities for Cirrus Logic DSP driver testing");
|
||||
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS("FW_CS_DSP");
|
||||
473
drivers/firmware/cirrus/test/cs_dsp_mock_wmfw.c
Normal file
473
drivers/firmware/cirrus/test/cs_dsp_mock_wmfw.c
Normal file
@@ -0,0 +1,473 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// wmfw file builder for cs_dsp KUnit tests.
|
||||
//
|
||||
// Copyright (C) 2024 Cirrus Logic, Inc. and
|
||||
// Cirrus Logic International Semiconductor Ltd.
|
||||
|
||||
#include <kunit/resource.h>
|
||||
#include <kunit/test.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/firmware.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
/* Buffer large enough for bin file content */
|
||||
#define CS_DSP_MOCK_WMFW_BUF_SIZE 131072
|
||||
|
||||
struct cs_dsp_mock_wmfw_builder {
|
||||
struct cs_dsp_test *test_priv;
|
||||
int format_version;
|
||||
void *buf;
|
||||
size_t buf_size_bytes;
|
||||
void *write_p;
|
||||
size_t bytes_used;
|
||||
|
||||
void *alg_data_header;
|
||||
unsigned int num_coeffs;
|
||||
};
|
||||
|
||||
struct wmfw_adsp2_halo_header {
|
||||
struct wmfw_header header;
|
||||
struct wmfw_adsp2_sizes sizes;
|
||||
struct wmfw_footer footer;
|
||||
} __packed;
|
||||
|
||||
struct wmfw_long_string {
|
||||
__le16 len;
|
||||
u8 data[] __nonstring __counted_by(len);
|
||||
} __packed;
|
||||
|
||||
struct wmfw_short_string {
|
||||
u8 len;
|
||||
u8 data[] __nonstring __counted_by(len);
|
||||
} __packed;
|
||||
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(vfree_action_wrapper, vfree, void *)
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_wmfw_format_version() - Return format version.
|
||||
*
|
||||
* @builder: Pointer to struct cs_dsp_mock_wmfw_builder.
|
||||
*
|
||||
* Return: Format version.
|
||||
*/
|
||||
int cs_dsp_mock_wmfw_format_version(struct cs_dsp_mock_wmfw_builder *builder)
|
||||
{
|
||||
return builder->format_version;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_format_version, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_wmfw_get_firmware() - Get struct firmware wrapper for data.
|
||||
*
|
||||
* @builder: Pointer to struct cs_dsp_mock_wmfw_builder.
|
||||
*
|
||||
* Return: Pointer to a struct firmware wrapper for the data.
|
||||
*/
|
||||
struct firmware *cs_dsp_mock_wmfw_get_firmware(struct cs_dsp_mock_wmfw_builder *builder)
|
||||
{
|
||||
struct firmware *fw;
|
||||
|
||||
if (!builder)
|
||||
return NULL;
|
||||
|
||||
fw = kunit_kzalloc(builder->test_priv->test, sizeof(*fw), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(builder->test_priv->test, fw);
|
||||
|
||||
fw->data = builder->buf;
|
||||
fw->size = builder->bytes_used;
|
||||
|
||||
return fw;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_get_firmware, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_wmfw_add_raw_block() - Add a block to the wmfw file.
|
||||
*
|
||||
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
|
||||
* @block_type: Block type.
|
||||
* @offset: Offset.
|
||||
* @payload_data: Pointer to buffer containing the payload data,
|
||||
* or NULL if no data.
|
||||
* @payload_len_bytes: Length of payload data in bytes, or zero.
|
||||
*/
|
||||
void cs_dsp_mock_wmfw_add_raw_block(struct cs_dsp_mock_wmfw_builder *builder,
|
||||
int block_type, unsigned int offset,
|
||||
const void *payload_data, size_t payload_len_bytes)
|
||||
{
|
||||
struct wmfw_region *header = builder->write_p;
|
||||
unsigned int bytes_needed = struct_size_t(struct wmfw_region, data, payload_len_bytes);
|
||||
|
||||
KUNIT_ASSERT_TRUE(builder->test_priv->test,
|
||||
(builder->write_p + bytes_needed) <
|
||||
(builder->buf + CS_DSP_MOCK_WMFW_BUF_SIZE));
|
||||
|
||||
header->offset = cpu_to_le32(offset | (block_type << 24));
|
||||
header->len = cpu_to_le32(payload_len_bytes);
|
||||
if (payload_len_bytes > 0)
|
||||
memcpy(header->data, payload_data, payload_len_bytes);
|
||||
|
||||
builder->write_p += bytes_needed;
|
||||
builder->bytes_used += bytes_needed;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_add_raw_block, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_wmfw_add_info() - Add an info block to the wmfw file.
|
||||
*
|
||||
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
|
||||
* @info: Pointer to info string to be copied into the file.
|
||||
*
|
||||
* The string will be padded to a length that is a multiple of 4 bytes.
|
||||
*/
|
||||
void cs_dsp_mock_wmfw_add_info(struct cs_dsp_mock_wmfw_builder *builder,
|
||||
const char *info)
|
||||
{
|
||||
size_t info_len = strlen(info);
|
||||
char *tmp = NULL;
|
||||
|
||||
if (info_len % 4) {
|
||||
/* Create a padded string with length a multiple of 4 */
|
||||
info_len = round_up(info_len, 4);
|
||||
tmp = kunit_kzalloc(builder->test_priv->test, info_len, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(builder->test_priv->test, tmp);
|
||||
memcpy(tmp, info, info_len);
|
||||
info = tmp;
|
||||
}
|
||||
|
||||
cs_dsp_mock_wmfw_add_raw_block(builder, WMFW_INFO_TEXT, 0, info, info_len);
|
||||
kunit_kfree(builder->test_priv->test, tmp);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_add_info, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_wmfw_add_data_block() - Add a data block to the wmfw file.
|
||||
*
|
||||
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
|
||||
* @mem_region: Memory region for the block.
|
||||
* @mem_offset_dsp_words: Offset to start of destination in DSP words.
|
||||
* @payload_data: Pointer to buffer containing the payload data.
|
||||
* @payload_len_bytes: Length of payload data in bytes.
|
||||
*/
|
||||
void cs_dsp_mock_wmfw_add_data_block(struct cs_dsp_mock_wmfw_builder *builder,
|
||||
int mem_region, unsigned int mem_offset_dsp_words,
|
||||
const void *payload_data, size_t payload_len_bytes)
|
||||
{
|
||||
/* Blob payload length must be a multiple of 4 */
|
||||
KUNIT_ASSERT_EQ(builder->test_priv->test, payload_len_bytes % 4, 0);
|
||||
|
||||
cs_dsp_mock_wmfw_add_raw_block(builder, mem_region, mem_offset_dsp_words,
|
||||
payload_data, payload_len_bytes);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_add_data_block, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
void cs_dsp_mock_wmfw_start_alg_info_block(struct cs_dsp_mock_wmfw_builder *builder,
|
||||
unsigned int alg_id,
|
||||
const char *name,
|
||||
const char *description)
|
||||
{
|
||||
struct wmfw_region *rgn = builder->write_p;
|
||||
struct wmfw_adsp_alg_data *v1;
|
||||
struct wmfw_short_string *shortstring;
|
||||
struct wmfw_long_string *longstring;
|
||||
size_t bytes_needed, name_len, description_len;
|
||||
int offset;
|
||||
|
||||
/* Bytes needed for region header */
|
||||
bytes_needed = offsetof(struct wmfw_region, data);
|
||||
|
||||
builder->alg_data_header = builder->write_p;
|
||||
builder->num_coeffs = 0;
|
||||
|
||||
switch (builder->format_version) {
|
||||
case 0:
|
||||
KUNIT_FAIL(builder->test_priv->test, "wmfwV0 does not have alg blocks\n");
|
||||
return;
|
||||
case 1:
|
||||
bytes_needed += offsetof(struct wmfw_adsp_alg_data, data);
|
||||
KUNIT_ASSERT_TRUE(builder->test_priv->test,
|
||||
(builder->write_p + bytes_needed) <
|
||||
(builder->buf + CS_DSP_MOCK_WMFW_BUF_SIZE));
|
||||
|
||||
memset(builder->write_p, 0, bytes_needed);
|
||||
|
||||
/* Create region header */
|
||||
rgn->offset = cpu_to_le32(WMFW_ALGORITHM_DATA << 24);
|
||||
|
||||
/* Create algorithm entry */
|
||||
v1 = (struct wmfw_adsp_alg_data *)&rgn->data[0];
|
||||
v1->id = cpu_to_le32(alg_id);
|
||||
if (name)
|
||||
strscpy(v1->name, name, sizeof(v1->name));
|
||||
|
||||
if (description)
|
||||
strscpy(v1->descr, description, sizeof(v1->descr));
|
||||
break;
|
||||
default:
|
||||
name_len = 0;
|
||||
description_len = 0;
|
||||
|
||||
if (name)
|
||||
name_len = strlen(name);
|
||||
|
||||
if (description)
|
||||
description_len = strlen(description);
|
||||
|
||||
bytes_needed += sizeof(__le32); /* alg id */
|
||||
bytes_needed += round_up(name_len + sizeof(u8), sizeof(__le32));
|
||||
bytes_needed += round_up(description_len + sizeof(__le16), sizeof(__le32));
|
||||
bytes_needed += sizeof(__le32); /* coeff count */
|
||||
|
||||
KUNIT_ASSERT_TRUE(builder->test_priv->test,
|
||||
(builder->write_p + bytes_needed) <
|
||||
(builder->buf + CS_DSP_MOCK_WMFW_BUF_SIZE));
|
||||
|
||||
memset(builder->write_p, 0, bytes_needed);
|
||||
|
||||
/* Create region header */
|
||||
rgn->offset = cpu_to_le32(WMFW_ALGORITHM_DATA << 24);
|
||||
|
||||
/* Create algorithm entry */
|
||||
*(__force __le32 *)&rgn->data[0] = cpu_to_le32(alg_id);
|
||||
|
||||
shortstring = (struct wmfw_short_string *)&rgn->data[4];
|
||||
shortstring->len = name_len;
|
||||
|
||||
if (name_len)
|
||||
memcpy(shortstring->data, name, name_len);
|
||||
|
||||
/* Round up to next __le32 */
|
||||
offset = round_up(4 + struct_size_t(struct wmfw_short_string, data, name_len),
|
||||
sizeof(__le32));
|
||||
|
||||
longstring = (struct wmfw_long_string *)&rgn->data[offset];
|
||||
longstring->len = cpu_to_le16(description_len);
|
||||
|
||||
if (description_len)
|
||||
memcpy(longstring->data, description, description_len);
|
||||
break;
|
||||
}
|
||||
|
||||
builder->write_p += bytes_needed;
|
||||
builder->bytes_used += bytes_needed;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_start_alg_info_block, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
void cs_dsp_mock_wmfw_add_coeff_desc(struct cs_dsp_mock_wmfw_builder *builder,
|
||||
const struct cs_dsp_mock_coeff_def *def)
|
||||
{
|
||||
struct wmfw_adsp_coeff_data *v1;
|
||||
struct wmfw_short_string *shortstring;
|
||||
struct wmfw_long_string *longstring;
|
||||
size_t bytes_needed, shortname_len, fullname_len, description_len;
|
||||
__le32 *ple32;
|
||||
|
||||
KUNIT_ASSERT_NOT_NULL(builder->test_priv->test, builder->alg_data_header);
|
||||
|
||||
switch (builder->format_version) {
|
||||
case 0:
|
||||
return;
|
||||
case 1:
|
||||
bytes_needed = offsetof(struct wmfw_adsp_coeff_data, data);
|
||||
KUNIT_ASSERT_TRUE(builder->test_priv->test,
|
||||
(builder->write_p + bytes_needed) <
|
||||
(builder->buf + CS_DSP_MOCK_WMFW_BUF_SIZE));
|
||||
|
||||
v1 = (struct wmfw_adsp_coeff_data *)builder->write_p;
|
||||
memset(v1, 0, sizeof(*v1));
|
||||
v1->hdr.offset = cpu_to_le16(def->offset_dsp_words);
|
||||
v1->hdr.type = cpu_to_le16(def->mem_type);
|
||||
v1->hdr.size = cpu_to_le32(bytes_needed - sizeof(v1->hdr));
|
||||
v1->ctl_type = cpu_to_le16(def->type);
|
||||
v1->flags = cpu_to_le16(def->flags);
|
||||
v1->len = cpu_to_le32(def->length_bytes);
|
||||
|
||||
if (def->fullname)
|
||||
strscpy(v1->name, def->fullname, sizeof(v1->name));
|
||||
|
||||
if (def->description)
|
||||
strscpy(v1->descr, def->description, sizeof(v1->descr));
|
||||
break;
|
||||
default:
|
||||
fullname_len = 0;
|
||||
description_len = 0;
|
||||
shortname_len = strlen(def->shortname);
|
||||
|
||||
if (def->fullname)
|
||||
fullname_len = strlen(def->fullname);
|
||||
|
||||
if (def->description)
|
||||
description_len = strlen(def->description);
|
||||
|
||||
bytes_needed = sizeof(__le32) * 2; /* type, offset and size */
|
||||
bytes_needed += round_up(shortname_len + sizeof(u8), sizeof(__le32));
|
||||
bytes_needed += round_up(fullname_len + sizeof(u8), sizeof(__le32));
|
||||
bytes_needed += round_up(description_len + sizeof(__le16), sizeof(__le32));
|
||||
bytes_needed += sizeof(__le32) * 2; /* flags, type and length */
|
||||
KUNIT_ASSERT_TRUE(builder->test_priv->test,
|
||||
(builder->write_p + bytes_needed) <
|
||||
(builder->buf + CS_DSP_MOCK_WMFW_BUF_SIZE));
|
||||
|
||||
ple32 = (__force __le32 *)builder->write_p;
|
||||
*ple32++ = cpu_to_le32(def->offset_dsp_words | (def->mem_type << 16));
|
||||
*ple32++ = cpu_to_le32(bytes_needed - sizeof(__le32) - sizeof(__le32));
|
||||
|
||||
shortstring = (__force struct wmfw_short_string *)ple32;
|
||||
shortstring->len = shortname_len;
|
||||
memcpy(shortstring->data, def->shortname, shortname_len);
|
||||
|
||||
/* Round up to next __le32 multiple */
|
||||
ple32 += round_up(struct_size_t(struct wmfw_short_string, data, shortname_len),
|
||||
sizeof(*ple32)) / sizeof(*ple32);
|
||||
|
||||
shortstring = (__force struct wmfw_short_string *)ple32;
|
||||
shortstring->len = fullname_len;
|
||||
memcpy(shortstring->data, def->fullname, fullname_len);
|
||||
|
||||
/* Round up to next __le32 multiple */
|
||||
ple32 += round_up(struct_size_t(struct wmfw_short_string, data, fullname_len),
|
||||
sizeof(*ple32)) / sizeof(*ple32);
|
||||
|
||||
longstring = (__force struct wmfw_long_string *)ple32;
|
||||
longstring->len = description_len;
|
||||
memcpy(longstring->data, def->description, description_len);
|
||||
|
||||
/* Round up to next __le32 multiple */
|
||||
ple32 += round_up(struct_size_t(struct wmfw_long_string, data, description_len),
|
||||
sizeof(*ple32)) / sizeof(*ple32);
|
||||
|
||||
*ple32++ = cpu_to_le32(def->type | (def->flags << 16));
|
||||
*ple32 = cpu_to_le32(def->length_bytes);
|
||||
break;
|
||||
}
|
||||
|
||||
builder->write_p += bytes_needed;
|
||||
builder->bytes_used += bytes_needed;
|
||||
builder->num_coeffs++;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_add_coeff_desc, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
void cs_dsp_mock_wmfw_end_alg_info_block(struct cs_dsp_mock_wmfw_builder *builder)
|
||||
{
|
||||
struct wmfw_region *rgn = builder->alg_data_header;
|
||||
struct wmfw_adsp_alg_data *v1;
|
||||
const struct wmfw_short_string *shortstring;
|
||||
const struct wmfw_long_string *longstring;
|
||||
size_t offset;
|
||||
|
||||
KUNIT_ASSERT_NOT_NULL(builder->test_priv->test, rgn);
|
||||
|
||||
/* Fill in data size */
|
||||
rgn->len = cpu_to_le32((u8 *)builder->write_p - (u8 *)rgn->data);
|
||||
|
||||
/* Fill in coefficient count */
|
||||
switch (builder->format_version) {
|
||||
case 0:
|
||||
return;
|
||||
case 1:
|
||||
v1 = (struct wmfw_adsp_alg_data *)&rgn->data[0];
|
||||
v1->ncoeff = cpu_to_le32(builder->num_coeffs);
|
||||
break;
|
||||
default:
|
||||
offset = 4; /* skip alg id */
|
||||
|
||||
/* Get name length and round up to __le32 multiple */
|
||||
shortstring = (const struct wmfw_short_string *)&rgn->data[offset];
|
||||
offset += round_up(struct_size_t(struct wmfw_short_string, data, shortstring->len),
|
||||
sizeof(__le32));
|
||||
|
||||
/* Get description length and round up to __le32 multiple */
|
||||
longstring = (const struct wmfw_long_string *)&rgn->data[offset];
|
||||
offset += round_up(struct_size_t(struct wmfw_long_string, data,
|
||||
le16_to_cpu(longstring->len)),
|
||||
sizeof(__le32));
|
||||
|
||||
*(__force __le32 *)&rgn->data[offset] = cpu_to_le32(builder->num_coeffs);
|
||||
break;
|
||||
}
|
||||
|
||||
builder->alg_data_header = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_end_alg_info_block, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
static void cs_dsp_init_adsp2_halo_wmfw(struct cs_dsp_mock_wmfw_builder *builder)
|
||||
{
|
||||
struct wmfw_adsp2_halo_header *hdr = builder->buf;
|
||||
const struct cs_dsp *dsp = builder->test_priv->dsp;
|
||||
|
||||
memcpy(hdr->header.magic, "WMFW", sizeof(hdr->header.magic));
|
||||
hdr->header.len = cpu_to_le32(sizeof(*hdr));
|
||||
hdr->header.ver = builder->format_version;
|
||||
hdr->header.core = dsp->type;
|
||||
hdr->header.rev = cpu_to_le16(dsp->rev);
|
||||
|
||||
hdr->sizes.pm = cpu_to_le32(cs_dsp_mock_size_of_region(dsp, WMFW_ADSP2_PM));
|
||||
hdr->sizes.xm = cpu_to_le32(cs_dsp_mock_size_of_region(dsp, WMFW_ADSP2_XM));
|
||||
hdr->sizes.ym = cpu_to_le32(cs_dsp_mock_size_of_region(dsp, WMFW_ADSP2_YM));
|
||||
|
||||
switch (dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
hdr->sizes.zm = cpu_to_le32(cs_dsp_mock_size_of_region(dsp, WMFW_ADSP2_ZM));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
builder->write_p = &hdr[1];
|
||||
builder->bytes_used += sizeof(*hdr);
|
||||
}
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_wmfw_init() - Initialize a struct cs_dsp_mock_wmfw_builder.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
* @format_version: Required wmfw format version.
|
||||
*
|
||||
* Return: Pointer to created struct cs_dsp_mock_wmfw_builder.
|
||||
*/
|
||||
struct cs_dsp_mock_wmfw_builder *cs_dsp_mock_wmfw_init(struct cs_dsp_test *priv,
|
||||
int format_version)
|
||||
{
|
||||
struct cs_dsp_mock_wmfw_builder *builder;
|
||||
|
||||
/* If format version isn't given use the default for the target core */
|
||||
if (format_version < 0) {
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
format_version = 2;
|
||||
break;
|
||||
default:
|
||||
format_version = 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
builder = kunit_kzalloc(priv->test, sizeof(*builder), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(priv->test, builder);
|
||||
|
||||
builder->test_priv = priv;
|
||||
builder->format_version = format_version;
|
||||
|
||||
builder->buf = vmalloc(CS_DSP_MOCK_WMFW_BUF_SIZE);
|
||||
KUNIT_ASSERT_NOT_NULL(priv->test, builder->buf);
|
||||
kunit_add_action_or_reset(priv->test, vfree_action_wrapper, builder->buf);
|
||||
|
||||
builder->buf_size_bytes = CS_DSP_MOCK_WMFW_BUF_SIZE;
|
||||
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
case WMFW_HALO:
|
||||
cs_dsp_init_adsp2_halo_wmfw(builder);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_init, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
2557
drivers/firmware/cirrus/test/cs_dsp_test_bin.c
Normal file
2557
drivers/firmware/cirrus/test/cs_dsp_test_bin.c
Normal file
File diff suppressed because it is too large
Load Diff
600
drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c
Normal file
600
drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c
Normal file
@@ -0,0 +1,600 @@
|
||||
// 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>
|
||||
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(_put_device_wrapper, put_device, struct device *);
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(_cs_dsp_remove_wrapper, cs_dsp_remove, struct cs_dsp *);
|
||||
|
||||
struct cs_dsp_test_local {
|
||||
struct cs_dsp_mock_bin_builder *bin_builder;
|
||||
struct cs_dsp_mock_xm_header *xm_header;
|
||||
struct cs_dsp_mock_wmfw_builder *wmfw_builder;
|
||||
struct firmware *wmfw;
|
||||
int wmfw_version;
|
||||
};
|
||||
|
||||
struct cs_dsp_bin_test_param {
|
||||
int block_type;
|
||||
};
|
||||
|
||||
static const struct cs_dsp_mock_alg_def cs_dsp_bin_err_test_mock_algs[] = {
|
||||
{
|
||||
.id = 0xfafa,
|
||||
.ver = 0x100000,
|
||||
.xm_size_words = 164,
|
||||
.ym_size_words = 164,
|
||||
.zm_size_words = 164,
|
||||
},
|
||||
};
|
||||
|
||||
/* Load a bin containing unknown blocks. They should be skipped. */
|
||||
static void bin_load_with_unknown_blocks(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *bin;
|
||||
unsigned int reg_addr;
|
||||
u8 *payload_data, *readback;
|
||||
u8 random_data[8];
|
||||
const unsigned int payload_size_bytes = 64;
|
||||
|
||||
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 some unknown blocks at the start of the bin */
|
||||
get_random_bytes(random_data, sizeof(random_data));
|
||||
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
|
||||
cs_dsp_bin_err_test_mock_algs[0].id,
|
||||
cs_dsp_bin_err_test_mock_algs[0].ver,
|
||||
0xf5, 0,
|
||||
random_data, sizeof(random_data));
|
||||
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
|
||||
cs_dsp_bin_err_test_mock_algs[0].id,
|
||||
cs_dsp_bin_err_test_mock_algs[0].ver,
|
||||
0xf500, 0,
|
||||
random_data, sizeof(random_data));
|
||||
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
|
||||
cs_dsp_bin_err_test_mock_algs[0].id,
|
||||
cs_dsp_bin_err_test_mock_algs[0].ver,
|
||||
0xc300, 0,
|
||||
random_data, sizeof(random_data));
|
||||
|
||||
/* Add a single payload to be written to DSP memory */
|
||||
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
|
||||
cs_dsp_bin_err_test_mock_algs[0].id,
|
||||
cs_dsp_bin_err_test_mock_algs[0].ver,
|
||||
WMFW_ADSP2_YM, 0,
|
||||
payload_data, payload_size_bytes);
|
||||
|
||||
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
/* Check that the payload was written to memory */
|
||||
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);
|
||||
}
|
||||
|
||||
/* Load a bin that doesn't have a valid magic marker. */
|
||||
static void bin_err_wrong_magic(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *bin;
|
||||
|
||||
/* Sanity-check that the wmfw loads ok without the bin */
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
cs_dsp_power_down(priv->dsp);
|
||||
|
||||
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
|
||||
|
||||
memcpy((void *)bin->data, "WMFW", 4);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
memcpy((void *)bin->data, "xMDR", 4);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
memcpy((void *)bin->data, "WxDR", 4);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
memcpy((void *)bin->data, "WMxR", 4);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
memcpy((void *)bin->data, "WMDx", 4);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
memset((void *)bin->data, 0, 4);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
}
|
||||
|
||||
/* Load a bin that is too short for a valid header. */
|
||||
static void bin_err_too_short_for_header(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *bin;
|
||||
|
||||
/* Sanity-check that the wmfw loads ok without the bin */
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
cs_dsp_power_down(priv->dsp);
|
||||
|
||||
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
|
||||
do {
|
||||
bin->size--;
|
||||
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
} while (bin->size > 0);
|
||||
}
|
||||
|
||||
/* Header length field isn't a valid header length. */
|
||||
static void bin_err_bad_header_length(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *bin;
|
||||
struct wmfw_coeff_hdr *header;
|
||||
unsigned int real_len, len;
|
||||
|
||||
/* Sanity-check that the wmfw loads ok without the bin */
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
cs_dsp_power_down(priv->dsp);
|
||||
|
||||
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
|
||||
header = (struct wmfw_coeff_hdr *)bin->data;
|
||||
real_len = le32_to_cpu(header->len);
|
||||
|
||||
for (len = 0; len < real_len; len++) {
|
||||
header->len = cpu_to_le32(len);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
}
|
||||
|
||||
for (len = real_len + 1; len < real_len + 7; len++) {
|
||||
header->len = cpu_to_le32(len);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
}
|
||||
|
||||
header->len = cpu_to_le32(0xffffffff);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
header->len = cpu_to_le32(0x80000000);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
header->len = cpu_to_le32(0x7fffffff);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
}
|
||||
|
||||
/* Wrong core type in header. */
|
||||
static void bin_err_bad_core_type(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *bin;
|
||||
struct wmfw_coeff_hdr *header;
|
||||
|
||||
/* Sanity-check that the wmfw loads ok without the bin */
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
cs_dsp_power_down(priv->dsp);
|
||||
|
||||
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
|
||||
header = (struct wmfw_coeff_hdr *)bin->data;
|
||||
|
||||
header->core_ver = cpu_to_le32(0);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
header->core_ver = cpu_to_le32(1);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
header->core_ver = cpu_to_le32(priv->dsp->type + 1);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
header->core_ver = cpu_to_le32(0xff);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
}
|
||||
|
||||
/* File too short to contain a full block header */
|
||||
static void bin_too_short_for_block_header(struct kunit *test)
|
||||
{
|
||||
const struct cs_dsp_bin_test_param *param = test->param_value;
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *bin;
|
||||
unsigned int header_length;
|
||||
|
||||
/* Sanity-check that the wmfw loads ok without the bin */
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
cs_dsp_power_down(priv->dsp);
|
||||
|
||||
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
|
||||
header_length = bin->size;
|
||||
kunit_kfree(test, bin);
|
||||
|
||||
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
|
||||
cs_dsp_bin_err_test_mock_algs[0].id,
|
||||
cs_dsp_bin_err_test_mock_algs[0].ver,
|
||||
param->block_type, 0,
|
||||
NULL, 0);
|
||||
|
||||
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
|
||||
KUNIT_ASSERT_GT(test, bin->size, header_length);
|
||||
|
||||
for (bin->size--; bin->size > header_length; bin->size--) {
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
/* File too short to contain the block payload */
|
||||
static void bin_too_short_for_block_payload(struct kunit *test)
|
||||
{
|
||||
const struct cs_dsp_bin_test_param *param = test->param_value;
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *bin;
|
||||
static const u8 payload[256] = { };
|
||||
int i;
|
||||
|
||||
/* Sanity-check that the wmfw loads ok without the bin */
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
cs_dsp_power_down(priv->dsp);
|
||||
|
||||
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
|
||||
cs_dsp_bin_err_test_mock_algs[0].id,
|
||||
cs_dsp_bin_err_test_mock_algs[0].ver,
|
||||
param->block_type, 0,
|
||||
payload, sizeof(payload));
|
||||
|
||||
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
|
||||
for (i = 0; i < sizeof(payload); i++) {
|
||||
bin->size--;
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Block payload length is a garbage value */
|
||||
static void bin_block_payload_len_garbage(struct kunit *test)
|
||||
{
|
||||
const struct cs_dsp_bin_test_param *param = test->param_value;
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *bin;
|
||||
struct wmfw_coeff_hdr *header;
|
||||
struct wmfw_coeff_item *block;
|
||||
u32 payload = 0;
|
||||
|
||||
/* Sanity-check that the wmfw loads ok without the bin */
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
cs_dsp_power_down(priv->dsp);
|
||||
|
||||
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
|
||||
cs_dsp_bin_err_test_mock_algs[0].id,
|
||||
cs_dsp_bin_err_test_mock_algs[0].ver,
|
||||
param->block_type, 0,
|
||||
&payload, sizeof(payload));
|
||||
|
||||
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
|
||||
header = (struct wmfw_coeff_hdr *)bin->data;
|
||||
block = (struct wmfw_coeff_item *)&bin->data[le32_to_cpu(header->len)];
|
||||
|
||||
/* Sanity check that we're looking at the correct part of the bin */
|
||||
KUNIT_ASSERT_EQ(test, le16_to_cpu(block->type), param->block_type);
|
||||
KUNIT_ASSERT_EQ(test, le32_to_cpu(block->len), sizeof(payload));
|
||||
|
||||
block->len = cpu_to_le32(0x8000);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
block->len = cpu_to_le32(0xffff);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
block->len = cpu_to_le32(0x7fffffff);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
block->len = cpu_to_le32(0x80000000);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
block->len = cpu_to_le32(0xffffffff);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
}
|
||||
|
||||
static void cs_dsp_bin_err_test_exit(struct kunit *test)
|
||||
{
|
||||
/*
|
||||
* Testing error conditions can produce a lot of log output
|
||||
* from cs_dsp error messages, so rate limit the test cases.
|
||||
*/
|
||||
usleep_range(200, 500);
|
||||
}
|
||||
|
||||
static int cs_dsp_bin_err_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_bin_err_test_mock_algs,
|
||||
ARRAY_SIZE(cs_dsp_bin_err_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);
|
||||
|
||||
local->wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
|
||||
|
||||
local->bin_builder =
|
||||
cs_dsp_mock_bin_init(priv, 1,
|
||||
cs_dsp_mock_xm_header_get_fw_version_from_regmap(priv));
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, local->bin_builder);
|
||||
|
||||
/* 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 */
|
||||
return kunit_add_action_or_reset(priv->test, _cs_dsp_remove_wrapper, dsp);
|
||||
}
|
||||
|
||||
static int cs_dsp_bin_err_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_bin_err_test_common_init(test, dsp, 3);
|
||||
}
|
||||
|
||||
static int cs_dsp_bin_err_test_adsp2_32bit_init(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp *dsp;
|
||||
|
||||
/* Fill in cs_dsp and initialize */
|
||||
dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
|
||||
if (!dsp)
|
||||
return -ENOMEM;
|
||||
|
||||
dsp->num = 1;
|
||||
dsp->type = WMFW_ADSP2;
|
||||
dsp->rev = 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_bin_err_test_common_init(test, dsp, 2);
|
||||
}
|
||||
|
||||
static int cs_dsp_bin_err_test_adsp2_16bit_init(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp *dsp;
|
||||
|
||||
/* Fill in cs_dsp and initialize */
|
||||
dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
|
||||
if (!dsp)
|
||||
return -ENOMEM;
|
||||
|
||||
dsp->num = 1;
|
||||
dsp->type = WMFW_ADSP2;
|
||||
dsp->rev = 0;
|
||||
dsp->mem = cs_dsp_mock_adsp2_16bit_dsp1_regions;
|
||||
dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_16bit_dsp1_region_sizes);
|
||||
dsp->base = cs_dsp_mock_adsp2_16bit_sysbase;
|
||||
|
||||
return cs_dsp_bin_err_test_common_init(test, dsp, 1);
|
||||
}
|
||||
|
||||
static struct kunit_case cs_dsp_bin_err_test_cases_halo[] = {
|
||||
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
static void cs_dsp_bin_err_block_types_desc(const struct cs_dsp_bin_test_param *param,
|
||||
char *desc)
|
||||
{
|
||||
snprintf(desc, KUNIT_PARAM_DESC_SIZE, "block_type:%#x", param->block_type);
|
||||
}
|
||||
|
||||
/* Some block types to test against, including illegal types */
|
||||
static const struct cs_dsp_bin_test_param bin_test_block_types_cases[] = {
|
||||
{ .block_type = WMFW_INFO_TEXT << 8 },
|
||||
{ .block_type = WMFW_METADATA << 8 },
|
||||
{ .block_type = WMFW_ADSP2_PM },
|
||||
{ .block_type = WMFW_ADSP2_XM },
|
||||
{ .block_type = 0x33 },
|
||||
{ .block_type = 0xf500 },
|
||||
{ .block_type = 0xc000 },
|
||||
};
|
||||
|
||||
KUNIT_ARRAY_PARAM(bin_test_block_types,
|
||||
bin_test_block_types_cases,
|
||||
cs_dsp_bin_err_block_types_desc);
|
||||
|
||||
static struct kunit_case cs_dsp_bin_err_test_cases_adsp2[] = {
|
||||
KUNIT_CASE(bin_load_with_unknown_blocks),
|
||||
KUNIT_CASE(bin_err_wrong_magic),
|
||||
KUNIT_CASE(bin_err_too_short_for_header),
|
||||
KUNIT_CASE(bin_err_bad_header_length),
|
||||
KUNIT_CASE(bin_err_bad_core_type),
|
||||
|
||||
KUNIT_CASE_PARAM(bin_too_short_for_block_header, bin_test_block_types_gen_params),
|
||||
KUNIT_CASE_PARAM(bin_too_short_for_block_payload, bin_test_block_types_gen_params),
|
||||
KUNIT_CASE_PARAM(bin_block_payload_len_garbage, bin_test_block_types_gen_params),
|
||||
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
static struct kunit_suite cs_dsp_bin_err_test_halo = {
|
||||
.name = "cs_dsp_bin_err_halo",
|
||||
.init = cs_dsp_bin_err_test_halo_init,
|
||||
.exit = cs_dsp_bin_err_test_exit,
|
||||
.test_cases = cs_dsp_bin_err_test_cases_halo,
|
||||
};
|
||||
|
||||
static struct kunit_suite cs_dsp_bin_err_test_adsp2_32bit = {
|
||||
.name = "cs_dsp_bin_err_adsp2_32bit",
|
||||
.init = cs_dsp_bin_err_test_adsp2_32bit_init,
|
||||
.exit = cs_dsp_bin_err_test_exit,
|
||||
.test_cases = cs_dsp_bin_err_test_cases_adsp2,
|
||||
};
|
||||
|
||||
static struct kunit_suite cs_dsp_bin_err_test_adsp2_16bit = {
|
||||
.name = "cs_dsp_bin_err_adsp2_16bit",
|
||||
.init = cs_dsp_bin_err_test_adsp2_16bit_init,
|
||||
.exit = cs_dsp_bin_err_test_exit,
|
||||
.test_cases = cs_dsp_bin_err_test_cases_adsp2,
|
||||
};
|
||||
|
||||
kunit_test_suites(&cs_dsp_bin_err_test_halo,
|
||||
&cs_dsp_bin_err_test_adsp2_32bit,
|
||||
&cs_dsp_bin_err_test_adsp2_16bit);
|
||||
688
drivers/firmware/cirrus/test/cs_dsp_test_callbacks.c
Normal file
688
drivers/firmware/cirrus/test/cs_dsp_test_callbacks.c
Normal file
@@ -0,0 +1,688 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// KUnit tests for cs_dsp.
|
||||
//
|
||||
// Copyright (C) 2024 Cirrus Logic, Inc. and
|
||||
// Cirrus Logic International Semiconductor Ltd.
|
||||
//
|
||||
|
||||
#include <kunit/device.h>
|
||||
#include <kunit/resource.h>
|
||||
#include <kunit/test.h>
|
||||
#include <kunit/test-bug.h>
|
||||
#include <linux/build_bug.h>
|
||||
#include <linux/firmware/cirrus/cs_dsp.h>
|
||||
#include <linux/firmware/cirrus/cs_dsp_test_utils.h>
|
||||
#include <linux/firmware/cirrus/wmfw.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#define ADSP2_LOCK_REGION_CTRL 0x7A
|
||||
#define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000
|
||||
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(_put_device_wrapper, put_device, struct device *)
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(_cs_dsp_remove_wrapper, cs_dsp_remove, struct cs_dsp *)
|
||||
|
||||
struct cs_dsp_test_local {
|
||||
struct cs_dsp_mock_wmfw_builder *wmfw_builder;
|
||||
|
||||
int num_control_add;
|
||||
int num_control_remove;
|
||||
int num_pre_run;
|
||||
int num_post_run;
|
||||
int num_pre_stop;
|
||||
int num_post_stop;
|
||||
int num_watchdog_expired;
|
||||
|
||||
struct cs_dsp_coeff_ctl *passed_ctl[16];
|
||||
struct cs_dsp *passed_dsp;
|
||||
};
|
||||
|
||||
struct cs_dsp_callbacks_test_param {
|
||||
const struct cs_dsp_client_ops *ops;
|
||||
const char *case_name;
|
||||
};
|
||||
|
||||
static const struct cs_dsp_mock_alg_def cs_dsp_callbacks_test_mock_algs[] = {
|
||||
{
|
||||
.id = 0xfafa,
|
||||
.ver = 0x100000,
|
||||
.xm_size_words = 164,
|
||||
.ym_size_words = 164,
|
||||
.zm_size_words = 164,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct cs_dsp_mock_coeff_def mock_coeff_template = {
|
||||
.shortname = "Dummy Coeff",
|
||||
.type = WMFW_CTL_TYPE_BYTES,
|
||||
.mem_type = WMFW_ADSP2_YM,
|
||||
.flags = WMFW_CTL_FLAG_VOLATILE,
|
||||
.length_bytes = 4,
|
||||
};
|
||||
|
||||
static int cs_dsp_test_control_add_callback(struct cs_dsp_coeff_ctl *ctl)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
|
||||
local->passed_ctl[local->num_control_add] = ctl;
|
||||
local->num_control_add++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cs_dsp_test_control_remove_callback(struct cs_dsp_coeff_ctl *ctl)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
|
||||
local->passed_ctl[local->num_control_remove] = ctl;
|
||||
local->num_control_remove++;
|
||||
}
|
||||
|
||||
static int cs_dsp_test_pre_run_callback(struct cs_dsp *dsp)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
|
||||
local->passed_dsp = dsp;
|
||||
local->num_pre_run++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs_dsp_test_post_run_callback(struct cs_dsp *dsp)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
|
||||
local->passed_dsp = dsp;
|
||||
local->num_post_run++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cs_dsp_test_pre_stop_callback(struct cs_dsp *dsp)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
|
||||
local->passed_dsp = dsp;
|
||||
local->num_pre_stop++;
|
||||
}
|
||||
|
||||
static void cs_dsp_test_post_stop_callback(struct cs_dsp *dsp)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
|
||||
local->passed_dsp = dsp;
|
||||
local->num_post_stop++;
|
||||
}
|
||||
|
||||
static void cs_dsp_test_watchdog_expired_callback(struct cs_dsp *dsp)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
|
||||
local->passed_dsp = dsp;
|
||||
local->num_watchdog_expired++;
|
||||
}
|
||||
|
||||
static const struct cs_dsp_client_ops cs_dsp_callback_test_client_ops = {
|
||||
.control_add = cs_dsp_test_control_add_callback,
|
||||
.control_remove = cs_dsp_test_control_remove_callback,
|
||||
.pre_run = cs_dsp_test_pre_run_callback,
|
||||
.post_run = cs_dsp_test_post_run_callback,
|
||||
.pre_stop = cs_dsp_test_pre_stop_callback,
|
||||
.post_stop = cs_dsp_test_post_stop_callback,
|
||||
.watchdog_expired = cs_dsp_test_watchdog_expired_callback,
|
||||
};
|
||||
|
||||
static const struct cs_dsp_client_ops cs_dsp_callback_test_empty_client_ops = {
|
||||
/* No entries */
|
||||
};
|
||||
|
||||
static void cs_dsp_test_run_stop_callbacks(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *wmfw;
|
||||
|
||||
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
|
||||
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
|
||||
KUNIT_EXPECT_EQ(test, local->num_pre_run, 1);
|
||||
KUNIT_EXPECT_EQ(test, local->num_post_run, 1);
|
||||
KUNIT_EXPECT_EQ(test, local->num_pre_stop, 0);
|
||||
KUNIT_EXPECT_EQ(test, local->num_post_stop, 0);
|
||||
KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
|
||||
local->passed_dsp = NULL;
|
||||
|
||||
cs_dsp_stop(priv->dsp);
|
||||
KUNIT_EXPECT_EQ(test, local->num_pre_run, 1);
|
||||
KUNIT_EXPECT_EQ(test, local->num_post_run, 1);
|
||||
KUNIT_EXPECT_EQ(test, local->num_pre_stop, 1);
|
||||
KUNIT_EXPECT_EQ(test, local->num_post_stop, 1);
|
||||
KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
|
||||
local->passed_dsp = NULL;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
|
||||
KUNIT_EXPECT_EQ(test, local->num_pre_run, 2);
|
||||
KUNIT_EXPECT_EQ(test, local->num_post_run, 2);
|
||||
KUNIT_EXPECT_EQ(test, local->num_pre_stop, 1);
|
||||
KUNIT_EXPECT_EQ(test, local->num_post_stop, 1);
|
||||
KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
|
||||
local->passed_dsp = NULL;
|
||||
|
||||
cs_dsp_stop(priv->dsp);
|
||||
KUNIT_EXPECT_EQ(test, local->num_pre_run, 2);
|
||||
KUNIT_EXPECT_EQ(test, local->num_post_run, 2);
|
||||
KUNIT_EXPECT_EQ(test, local->num_pre_stop, 2);
|
||||
KUNIT_EXPECT_EQ(test, local->num_post_stop, 2);
|
||||
KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
|
||||
local->passed_dsp = NULL;
|
||||
}
|
||||
|
||||
static void cs_dsp_test_ctl_v1_callbacks(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct cs_dsp_mock_coeff_def def = mock_coeff_template;
|
||||
struct cs_dsp_coeff_ctl *ctl;
|
||||
struct firmware *wmfw;
|
||||
int i;
|
||||
|
||||
/* Add a control for each memory */
|
||||
cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
|
||||
cs_dsp_callbacks_test_mock_algs[0].id,
|
||||
"dummyalg", NULL);
|
||||
def.shortname = "zm";
|
||||
def.mem_type = WMFW_ADSP2_ZM;
|
||||
cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
|
||||
|
||||
def.shortname = "ym";
|
||||
def.mem_type = WMFW_ADSP2_YM;
|
||||
cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
|
||||
|
||||
def.shortname = "xm";
|
||||
def.mem_type = WMFW_ADSP2_XM;
|
||||
cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
|
||||
|
||||
cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);
|
||||
|
||||
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
|
||||
/* There should have been an add callback for each control */
|
||||
KUNIT_EXPECT_EQ(test, list_count_nodes(&priv->dsp->ctl_list), 3);
|
||||
KUNIT_EXPECT_EQ(test, local->num_control_add, 3);
|
||||
KUNIT_EXPECT_EQ(test, local->num_control_remove, 0);
|
||||
|
||||
i = 0;
|
||||
list_for_each_entry_reverse(ctl, &priv->dsp->ctl_list, list)
|
||||
KUNIT_EXPECT_PTR_EQ(test, local->passed_ctl[i++], ctl);
|
||||
|
||||
/*
|
||||
* Call cs_dsp_remove() and there should be a remove callback
|
||||
* for each control
|
||||
*/
|
||||
memset(local->passed_ctl, 0, sizeof(local->passed_ctl));
|
||||
cs_dsp_remove(priv->dsp);
|
||||
|
||||
/* Prevent double cleanup */
|
||||
kunit_remove_action(priv->test, _cs_dsp_remove_wrapper, priv->dsp);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, local->num_control_add, 3);
|
||||
KUNIT_EXPECT_EQ(test, local->num_control_remove, 3);
|
||||
|
||||
i = 0;
|
||||
list_for_each_entry_reverse(ctl, &priv->dsp->ctl_list, list)
|
||||
KUNIT_EXPECT_PTR_EQ(test, local->passed_ctl[i++], ctl);
|
||||
}
|
||||
|
||||
static void cs_dsp_test_ctl_v2_callbacks(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct cs_dsp_mock_coeff_def def = mock_coeff_template;
|
||||
struct cs_dsp_coeff_ctl *ctl;
|
||||
struct firmware *wmfw;
|
||||
char name[2] = { };
|
||||
int i;
|
||||
|
||||
/* Add some controls */
|
||||
def.shortname = name;
|
||||
cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
|
||||
cs_dsp_callbacks_test_mock_algs[0].id,
|
||||
"dummyalg", NULL);
|
||||
for (i = 0; i < ARRAY_SIZE(local->passed_ctl); ++i) {
|
||||
name[0] = 'A' + i;
|
||||
def.offset_dsp_words = i;
|
||||
cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
|
||||
}
|
||||
cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);
|
||||
|
||||
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
|
||||
/* There should have been an add callback for each control */
|
||||
KUNIT_EXPECT_EQ(test, list_count_nodes(&priv->dsp->ctl_list),
|
||||
ARRAY_SIZE(local->passed_ctl));
|
||||
KUNIT_EXPECT_EQ(test, local->num_control_add, ARRAY_SIZE(local->passed_ctl));
|
||||
KUNIT_EXPECT_EQ(test, local->num_control_remove, 0);
|
||||
|
||||
i = 0;
|
||||
list_for_each_entry_reverse(ctl, &priv->dsp->ctl_list, list)
|
||||
KUNIT_EXPECT_PTR_EQ(test, local->passed_ctl[i++], ctl);
|
||||
|
||||
/*
|
||||
* Call cs_dsp_remove() and there should be a remove callback
|
||||
* for each control
|
||||
*/
|
||||
memset(local->passed_ctl, 0, sizeof(local->passed_ctl));
|
||||
cs_dsp_remove(priv->dsp);
|
||||
|
||||
/* Prevent double cleanup */
|
||||
kunit_remove_action(priv->test, _cs_dsp_remove_wrapper, priv->dsp);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, local->num_control_add, ARRAY_SIZE(local->passed_ctl));
|
||||
KUNIT_EXPECT_EQ(test, local->num_control_remove, ARRAY_SIZE(local->passed_ctl));
|
||||
|
||||
i = 0;
|
||||
list_for_each_entry_reverse(ctl, &priv->dsp->ctl_list, list)
|
||||
KUNIT_EXPECT_PTR_EQ(test, local->passed_ctl[i++], ctl);
|
||||
}
|
||||
|
||||
static void cs_dsp_test_no_callbacks(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct cs_dsp_mock_coeff_def def = mock_coeff_template;
|
||||
struct firmware *wmfw;
|
||||
|
||||
/* Add a controls */
|
||||
def.shortname = "A";
|
||||
cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
|
||||
cs_dsp_callbacks_test_mock_algs[0].id,
|
||||
"dummyalg", NULL);
|
||||
cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
|
||||
cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);
|
||||
|
||||
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
|
||||
|
||||
/* Run a sequence of ops that would invoke callbacks */
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
|
||||
cs_dsp_stop(priv->dsp);
|
||||
cs_dsp_remove(priv->dsp);
|
||||
|
||||
/* Prevent double cleanup */
|
||||
kunit_remove_action(priv->test, _cs_dsp_remove_wrapper, priv->dsp);
|
||||
|
||||
/* Something went very wrong if any of our callbacks were called */
|
||||
KUNIT_EXPECT_EQ(test, local->num_control_add, 0);
|
||||
KUNIT_EXPECT_EQ(test, local->num_control_remove, 0);
|
||||
KUNIT_EXPECT_EQ(test, local->num_pre_run, 0);
|
||||
KUNIT_EXPECT_EQ(test, local->num_post_run, 0);
|
||||
KUNIT_EXPECT_EQ(test, local->num_pre_stop, 0);
|
||||
KUNIT_EXPECT_EQ(test, local->num_post_stop, 0);
|
||||
}
|
||||
|
||||
static void cs_dsp_test_adsp2v2_watchdog_callback(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *wmfw;
|
||||
|
||||
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
|
||||
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
|
||||
|
||||
/* Set the watchdog timeout bit */
|
||||
regmap_write(priv->dsp->regmap, priv->dsp->base + ADSP2_LOCK_REGION_CTRL,
|
||||
ADSP2_WDT_TIMEOUT_STS_MASK);
|
||||
|
||||
/* Notify an interrupt and the watchdog callback should be called */
|
||||
cs_dsp_adsp2_bus_error(priv->dsp);
|
||||
KUNIT_EXPECT_EQ(test, local->num_watchdog_expired, 1);
|
||||
KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
|
||||
}
|
||||
|
||||
static void cs_dsp_test_adsp2v2_watchdog_no_callbacks(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *wmfw;
|
||||
|
||||
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
|
||||
|
||||
/* Set the watchdog timeout bit */
|
||||
regmap_write(priv->dsp->regmap, priv->dsp->base + ADSP2_LOCK_REGION_CTRL,
|
||||
ADSP2_WDT_TIMEOUT_STS_MASK);
|
||||
|
||||
/* Notify an interrupt, which will look for a watchdog callback */
|
||||
cs_dsp_adsp2_bus_error(priv->dsp);
|
||||
KUNIT_EXPECT_EQ(test, local->num_watchdog_expired, 0);
|
||||
}
|
||||
|
||||
static void cs_dsp_test_halo_watchdog_callback(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *wmfw;
|
||||
|
||||
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
|
||||
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
|
||||
|
||||
/* Notify an interrupt and the watchdog callback should be called */
|
||||
cs_dsp_halo_wdt_expire(priv->dsp);
|
||||
KUNIT_EXPECT_EQ(test, local->num_watchdog_expired, 1);
|
||||
KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
|
||||
}
|
||||
|
||||
static void cs_dsp_test_halo_watchdog_no_callbacks(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *wmfw;
|
||||
|
||||
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
|
||||
|
||||
/* Notify an interrupt, which will look for a watchdog callback */
|
||||
cs_dsp_halo_wdt_expire(priv->dsp);
|
||||
KUNIT_EXPECT_EQ(test, local->num_watchdog_expired, 0);
|
||||
}
|
||||
|
||||
static int cs_dsp_callbacks_test_common_init(struct kunit *test, struct cs_dsp *dsp,
|
||||
int wmfw_version)
|
||||
{
|
||||
const struct cs_dsp_callbacks_test_param *param = test->param_value;
|
||||
struct cs_dsp_test *priv;
|
||||
struct cs_dsp_test_local *local;
|
||||
struct device *test_dev;
|
||||
struct cs_dsp_mock_xm_header *xm_header;
|
||||
int ret;
|
||||
|
||||
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
local = kunit_kzalloc(test, sizeof(struct cs_dsp_test_local), GFP_KERNEL);
|
||||
if (!local)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->test = test;
|
||||
priv->dsp = dsp;
|
||||
test->priv = priv;
|
||||
priv->local = local;
|
||||
|
||||
/* Create dummy struct device */
|
||||
test_dev = kunit_device_register(test, "cs_dsp_test_drv");
|
||||
if (IS_ERR(test_dev))
|
||||
return PTR_ERR(test_dev);
|
||||
|
||||
dsp->dev = get_device(test_dev);
|
||||
if (!dsp->dev)
|
||||
return -ENODEV;
|
||||
|
||||
ret = kunit_add_action_or_reset(test, _put_device_wrapper, dsp->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_set_drvdata(dsp->dev, priv);
|
||||
|
||||
/* Allocate regmap */
|
||||
ret = cs_dsp_mock_regmap_init(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* There must always be a XM header with at least 1 algorithm,
|
||||
* so create a dummy one and pre-populate XM so the wmfw doesn't
|
||||
* have to contain an XM blob.
|
||||
*/
|
||||
xm_header = cs_dsp_create_mock_xm_header(priv,
|
||||
cs_dsp_callbacks_test_mock_algs,
|
||||
ARRAY_SIZE(cs_dsp_callbacks_test_mock_algs));
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xm_header);
|
||||
cs_dsp_mock_xm_header_write_to_regmap(xm_header);
|
||||
|
||||
local->wmfw_builder = cs_dsp_mock_wmfw_init(priv, wmfw_version);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, local->wmfw_builder);
|
||||
|
||||
/* Add dummy XM header payload to wmfw */
|
||||
cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
|
||||
WMFW_ADSP2_XM, 0,
|
||||
xm_header->blob_data,
|
||||
xm_header->blob_size_bytes);
|
||||
|
||||
/* Init cs_dsp */
|
||||
dsp->client_ops = param->ops;
|
||||
|
||||
switch (dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
ret = cs_dsp_adsp2_init(dsp);
|
||||
break;
|
||||
case WMFW_HALO:
|
||||
ret = cs_dsp_halo_init(dsp);
|
||||
break;
|
||||
default:
|
||||
KUNIT_FAIL(test, "Untested DSP type %d\n", dsp->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Automatically call cs_dsp_remove() when test case ends */
|
||||
return kunit_add_action_or_reset(priv->test, _cs_dsp_remove_wrapper, dsp);
|
||||
}
|
||||
|
||||
static int cs_dsp_callbacks_test_halo_init(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp *dsp;
|
||||
|
||||
/* Fill in cs_dsp and initialize */
|
||||
dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
|
||||
if (!dsp)
|
||||
return -ENOMEM;
|
||||
|
||||
dsp->num = 1;
|
||||
dsp->type = WMFW_HALO;
|
||||
dsp->mem = cs_dsp_mock_halo_dsp1_regions;
|
||||
dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_halo_dsp1_region_sizes);
|
||||
dsp->base = cs_dsp_mock_halo_core_base;
|
||||
dsp->base_sysinfo = cs_dsp_mock_halo_sysinfo_base;
|
||||
|
||||
return cs_dsp_callbacks_test_common_init(test, dsp, 3);
|
||||
}
|
||||
|
||||
static int cs_dsp_callbacks_test_adsp2_32bit_init(struct kunit *test, int rev)
|
||||
{
|
||||
struct cs_dsp *dsp;
|
||||
|
||||
/* Fill in cs_dsp and initialize */
|
||||
dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
|
||||
if (!dsp)
|
||||
return -ENOMEM;
|
||||
|
||||
dsp->num = 1;
|
||||
dsp->type = WMFW_ADSP2;
|
||||
dsp->rev = rev;
|
||||
dsp->mem = cs_dsp_mock_adsp2_32bit_dsp1_regions;
|
||||
dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_32bit_dsp1_region_sizes);
|
||||
dsp->base = cs_dsp_mock_adsp2_32bit_sysbase;
|
||||
|
||||
return cs_dsp_callbacks_test_common_init(test, dsp, 2);
|
||||
}
|
||||
|
||||
static int cs_dsp_callbacks_test_adsp2v2_32bit_init(struct kunit *test)
|
||||
{
|
||||
return cs_dsp_callbacks_test_adsp2_32bit_init(test, 2);
|
||||
}
|
||||
|
||||
static int cs_dsp_callbacks_test_adsp2v1_32bit_init(struct kunit *test)
|
||||
{
|
||||
return cs_dsp_callbacks_test_adsp2_32bit_init(test, 1);
|
||||
}
|
||||
|
||||
static int cs_dsp_callbacks_test_adsp2_16bit_init(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp *dsp;
|
||||
|
||||
/* Fill in cs_dsp and initialize */
|
||||
dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
|
||||
if (!dsp)
|
||||
return -ENOMEM;
|
||||
|
||||
dsp->num = 1;
|
||||
dsp->type = WMFW_ADSP2;
|
||||
dsp->rev = 0;
|
||||
dsp->mem = cs_dsp_mock_adsp2_16bit_dsp1_regions;
|
||||
dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_16bit_dsp1_region_sizes);
|
||||
dsp->base = cs_dsp_mock_adsp2_16bit_sysbase;
|
||||
|
||||
return cs_dsp_callbacks_test_common_init(test, dsp, 1);
|
||||
}
|
||||
|
||||
static void cs_dsp_callbacks_param_desc(const struct cs_dsp_callbacks_test_param *param,
|
||||
char *desc)
|
||||
{
|
||||
snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s", param->case_name);
|
||||
}
|
||||
|
||||
/* Parameterize on different client callback ops tables */
|
||||
static const struct cs_dsp_callbacks_test_param cs_dsp_callbacks_ops_cases[] = {
|
||||
{ .ops = &cs_dsp_callback_test_client_ops, .case_name = "all ops" },
|
||||
};
|
||||
|
||||
KUNIT_ARRAY_PARAM(cs_dsp_callbacks_ops,
|
||||
cs_dsp_callbacks_ops_cases,
|
||||
cs_dsp_callbacks_param_desc);
|
||||
|
||||
static const struct cs_dsp_callbacks_test_param cs_dsp_no_callbacks_cases[] = {
|
||||
{ .ops = &cs_dsp_callback_test_empty_client_ops, .case_name = "empty ops" },
|
||||
};
|
||||
|
||||
KUNIT_ARRAY_PARAM(cs_dsp_no_callbacks,
|
||||
cs_dsp_no_callbacks_cases,
|
||||
cs_dsp_callbacks_param_desc);
|
||||
|
||||
static struct kunit_case cs_dsp_callbacks_adsp2_wmfwv1_test_cases[] = {
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_run_stop_callbacks, cs_dsp_callbacks_ops_gen_params),
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_ctl_v1_callbacks, cs_dsp_callbacks_ops_gen_params),
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_no_callbacks, cs_dsp_no_callbacks_gen_params),
|
||||
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
static struct kunit_case cs_dsp_callbacks_adsp2_wmfwv2_test_cases[] = {
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_run_stop_callbacks, cs_dsp_callbacks_ops_gen_params),
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_ctl_v2_callbacks, cs_dsp_callbacks_ops_gen_params),
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_no_callbacks, cs_dsp_no_callbacks_gen_params),
|
||||
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
static struct kunit_case cs_dsp_callbacks_halo_test_cases[] = {
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_run_stop_callbacks, cs_dsp_callbacks_ops_gen_params),
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_ctl_v2_callbacks, cs_dsp_callbacks_ops_gen_params),
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_no_callbacks, cs_dsp_no_callbacks_gen_params),
|
||||
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
static struct kunit_case cs_dsp_watchdog_adsp2v2_test_cases[] = {
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_adsp2v2_watchdog_callback, cs_dsp_callbacks_ops_gen_params),
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_adsp2v2_watchdog_no_callbacks, cs_dsp_no_callbacks_gen_params),
|
||||
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
static struct kunit_case cs_dsp_watchdog_halo_test_cases[] = {
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_halo_watchdog_callback, cs_dsp_callbacks_ops_gen_params),
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_halo_watchdog_no_callbacks, cs_dsp_no_callbacks_gen_params),
|
||||
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
static struct kunit_suite cs_dsp_callbacks_test_halo = {
|
||||
.name = "cs_dsp_callbacks_halo",
|
||||
.init = cs_dsp_callbacks_test_halo_init,
|
||||
.test_cases = cs_dsp_callbacks_halo_test_cases,
|
||||
};
|
||||
|
||||
static struct kunit_suite cs_dsp_callbacks_test_adsp2v2_32bit = {
|
||||
.name = "cs_dsp_callbacks_adsp2v2_32bit_wmfwv2",
|
||||
.init = cs_dsp_callbacks_test_adsp2v2_32bit_init,
|
||||
.test_cases = cs_dsp_callbacks_adsp2_wmfwv2_test_cases,
|
||||
};
|
||||
|
||||
static struct kunit_suite cs_dsp_callbacks_test_adsp2v1_32bit = {
|
||||
.name = "cs_dsp_callbacks_adsp2v1_32bit_wmfwv2",
|
||||
.init = cs_dsp_callbacks_test_adsp2v1_32bit_init,
|
||||
.test_cases = cs_dsp_callbacks_adsp2_wmfwv2_test_cases,
|
||||
};
|
||||
|
||||
static struct kunit_suite cs_dsp_callbacks_test_adsp2_16bit = {
|
||||
.name = "cs_dsp_callbacks_adsp2_16bit_wmfwv1",
|
||||
.init = cs_dsp_callbacks_test_adsp2_16bit_init,
|
||||
.test_cases = cs_dsp_callbacks_adsp2_wmfwv1_test_cases,
|
||||
};
|
||||
|
||||
static struct kunit_suite cs_dsp_watchdog_test_adsp2v2_32bit = {
|
||||
.name = "cs_dsp_watchdog_adsp2v2_32bit",
|
||||
.init = cs_dsp_callbacks_test_adsp2v2_32bit_init,
|
||||
.test_cases = cs_dsp_watchdog_adsp2v2_test_cases,
|
||||
};
|
||||
|
||||
static struct kunit_suite cs_dsp_watchdog_test_halo_32bit = {
|
||||
.name = "cs_dsp_watchdog_halo",
|
||||
.init = cs_dsp_callbacks_test_halo_init,
|
||||
.test_cases = cs_dsp_watchdog_halo_test_cases,
|
||||
};
|
||||
|
||||
kunit_test_suites(&cs_dsp_callbacks_test_halo,
|
||||
&cs_dsp_callbacks_test_adsp2v2_32bit,
|
||||
&cs_dsp_callbacks_test_adsp2v1_32bit,
|
||||
&cs_dsp_callbacks_test_adsp2_16bit,
|
||||
&cs_dsp_watchdog_test_adsp2v2_32bit,
|
||||
&cs_dsp_watchdog_test_halo_32bit);
|
||||
3282
drivers/firmware/cirrus/test/cs_dsp_test_control_cache.c
Normal file
3282
drivers/firmware/cirrus/test/cs_dsp_test_control_cache.c
Normal file
File diff suppressed because it is too large
Load Diff
1851
drivers/firmware/cirrus/test/cs_dsp_test_control_parse.c
Normal file
1851
drivers/firmware/cirrus/test/cs_dsp_test_control_parse.c
Normal file
File diff suppressed because it is too large
Load Diff
2669
drivers/firmware/cirrus/test/cs_dsp_test_control_rw.c
Normal file
2669
drivers/firmware/cirrus/test/cs_dsp_test_control_rw.c
Normal file
File diff suppressed because it is too large
Load Diff
2211
drivers/firmware/cirrus/test/cs_dsp_test_wmfw.c
Normal file
2211
drivers/firmware/cirrus/test/cs_dsp_test_wmfw.c
Normal file
File diff suppressed because it is too large
Load Diff
1347
drivers/firmware/cirrus/test/cs_dsp_test_wmfw_error.c
Normal file
1347
drivers/firmware/cirrus/test/cs_dsp_test_wmfw_error.c
Normal file
File diff suppressed because it is too large
Load Diff
14
drivers/firmware/cirrus/test/cs_dsp_tests.c
Normal file
14
drivers/firmware/cirrus/test/cs_dsp_tests.c
Normal file
@@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// Utility module for cs_dsp KUnit testing.
|
||||
//
|
||||
// Copyright (C) 2024 Cirrus Logic, Inc. and
|
||||
// Cirrus Logic International Semiconductor Ltd.
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
MODULE_DESCRIPTION("KUnit tests for Cirrus Logic DSP driver");
|
||||
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS("FW_CS_DSP");
|
||||
MODULE_IMPORT_NS("FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
160
include/linux/firmware/cirrus/cs_dsp_test_utils.h
Normal file
160
include/linux/firmware/cirrus/cs_dsp_test_utils.h
Normal file
@@ -0,0 +1,160 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Support utilities for cs_dsp testing.
|
||||
*
|
||||
* Copyright (C) 2024 Cirrus Logic, Inc. and
|
||||
* Cirrus Logic International Semiconductor Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/firmware/cirrus/wmfw.h>
|
||||
|
||||
struct kunit;
|
||||
struct cs_dsp_test;
|
||||
struct cs_dsp_test_local;
|
||||
|
||||
/**
|
||||
* struct cs_dsp_test - base class for test utilities
|
||||
*
|
||||
* @test: Pointer to struct kunit instance.
|
||||
* @dsp: Pointer to struct cs_dsp instance.
|
||||
* @local: Private data for each test suite.
|
||||
*/
|
||||
struct cs_dsp_test {
|
||||
struct kunit *test;
|
||||
struct cs_dsp *dsp;
|
||||
|
||||
struct cs_dsp_test_local *local;
|
||||
|
||||
/* Following members are private */
|
||||
bool saw_bus_write;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cs_dsp_mock_alg_def - Info for creating a mock algorithm entry.
|
||||
*
|
||||
* @id Algorithm ID.
|
||||
* @ver; Algorithm version.
|
||||
* @xm_base_words XM base address in DSP words.
|
||||
* @xm_size_words XM size in DSP words.
|
||||
* @ym_base_words YM base address in DSP words.
|
||||
* @ym_size_words YM size in DSP words.
|
||||
* @zm_base_words ZM base address in DSP words.
|
||||
* @zm_size_words ZM size in DSP words.
|
||||
*/
|
||||
struct cs_dsp_mock_alg_def {
|
||||
unsigned int id;
|
||||
unsigned int ver;
|
||||
unsigned int xm_base_words;
|
||||
unsigned int xm_size_words;
|
||||
unsigned int ym_base_words;
|
||||
unsigned int ym_size_words;
|
||||
unsigned int zm_base_words;
|
||||
unsigned int zm_size_words;
|
||||
};
|
||||
|
||||
struct cs_dsp_mock_coeff_def {
|
||||
const char *shortname;
|
||||
const char *fullname;
|
||||
const char *description;
|
||||
u16 type;
|
||||
u16 flags;
|
||||
u16 mem_type;
|
||||
unsigned int offset_dsp_words;
|
||||
unsigned int length_bytes;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cs_dsp_mock_xm_header - XM header builder
|
||||
*
|
||||
* @test_priv: Pointer to the struct cs_dsp_test.
|
||||
* @blob_data: Pointer to the created blob data.
|
||||
* @blob_size_bytes: Size of the data at blob_data.
|
||||
*/
|
||||
struct cs_dsp_mock_xm_header {
|
||||
struct cs_dsp_test *test_priv;
|
||||
void *blob_data;
|
||||
size_t blob_size_bytes;
|
||||
};
|
||||
|
||||
struct cs_dsp_mock_wmfw_builder;
|
||||
struct cs_dsp_mock_bin_builder;
|
||||
|
||||
extern const unsigned int cs_dsp_mock_adsp2_32bit_sysbase;
|
||||
extern const unsigned int cs_dsp_mock_adsp2_16bit_sysbase;
|
||||
extern const unsigned int cs_dsp_mock_halo_core_base;
|
||||
extern const unsigned int cs_dsp_mock_halo_sysinfo_base;
|
||||
|
||||
extern const struct cs_dsp_region cs_dsp_mock_halo_dsp1_regions[];
|
||||
extern const unsigned int cs_dsp_mock_halo_dsp1_region_sizes[];
|
||||
extern const struct cs_dsp_region cs_dsp_mock_adsp2_32bit_dsp1_regions[];
|
||||
extern const unsigned int cs_dsp_mock_adsp2_32bit_dsp1_region_sizes[];
|
||||
extern const struct cs_dsp_region cs_dsp_mock_adsp2_16bit_dsp1_regions[];
|
||||
extern const unsigned int cs_dsp_mock_adsp2_16bit_dsp1_region_sizes[];
|
||||
int cs_dsp_mock_count_regions(const unsigned int *region_sizes);
|
||||
unsigned int cs_dsp_mock_size_of_region(const struct cs_dsp *dsp, int mem_type);
|
||||
unsigned int cs_dsp_mock_base_addr_for_mem(struct cs_dsp_test *priv, int mem_type);
|
||||
unsigned int cs_dsp_mock_reg_addr_inc_per_unpacked_word(struct cs_dsp_test *priv);
|
||||
unsigned int cs_dsp_mock_reg_block_length_bytes(struct cs_dsp_test *priv, int mem_type);
|
||||
unsigned int cs_dsp_mock_reg_block_length_registers(struct cs_dsp_test *priv, int mem_type);
|
||||
unsigned int cs_dsp_mock_reg_block_length_dsp_words(struct cs_dsp_test *priv, int mem_type);
|
||||
bool cs_dsp_mock_has_zm(struct cs_dsp_test *priv);
|
||||
int cs_dsp_mock_packed_to_unpacked_mem_type(int packed_mem_type);
|
||||
unsigned int cs_dsp_mock_num_dsp_words_to_num_packed_regs(unsigned int num_dsp_words);
|
||||
unsigned int cs_dsp_mock_xm_header_get_alg_base_in_words(struct cs_dsp_test *priv,
|
||||
unsigned int alg_id,
|
||||
int mem_type);
|
||||
unsigned int cs_dsp_mock_xm_header_get_fw_version_from_regmap(struct cs_dsp_test *priv);
|
||||
unsigned int cs_dsp_mock_xm_header_get_fw_version(struct cs_dsp_mock_xm_header *header);
|
||||
void cs_dsp_mock_xm_header_drop_from_regmap_cache(struct cs_dsp_test *priv);
|
||||
int cs_dsp_mock_xm_header_write_to_regmap(struct cs_dsp_mock_xm_header *header);
|
||||
struct cs_dsp_mock_xm_header *cs_dsp_create_mock_xm_header(struct cs_dsp_test *priv,
|
||||
const struct cs_dsp_mock_alg_def *algs,
|
||||
size_t num_algs);
|
||||
|
||||
int cs_dsp_mock_regmap_init(struct cs_dsp_test *priv);
|
||||
void cs_dsp_mock_regmap_drop_range(struct cs_dsp_test *priv,
|
||||
unsigned int first_reg, unsigned int last_reg);
|
||||
void cs_dsp_mock_regmap_drop_regs(struct cs_dsp_test *priv,
|
||||
unsigned int first_reg, size_t num_regs);
|
||||
void cs_dsp_mock_regmap_drop_bytes(struct cs_dsp_test *priv,
|
||||
unsigned int first_reg, size_t num_bytes);
|
||||
void cs_dsp_mock_regmap_drop_system_regs(struct cs_dsp_test *priv);
|
||||
bool cs_dsp_mock_regmap_is_dirty(struct cs_dsp_test *priv, bool drop_system_regs);
|
||||
|
||||
struct cs_dsp_mock_bin_builder *cs_dsp_mock_bin_init(struct cs_dsp_test *priv,
|
||||
int format_version,
|
||||
unsigned int fw_version);
|
||||
void cs_dsp_mock_bin_add_raw_block(struct cs_dsp_mock_bin_builder *builder,
|
||||
unsigned int alg_id, unsigned int alg_ver,
|
||||
int type, unsigned int offset,
|
||||
const void *payload_data, size_t payload_len_bytes);
|
||||
void cs_dsp_mock_bin_add_info(struct cs_dsp_mock_bin_builder *builder,
|
||||
const char *info);
|
||||
void cs_dsp_mock_bin_add_name(struct cs_dsp_mock_bin_builder *builder,
|
||||
const char *name);
|
||||
void cs_dsp_mock_bin_add_patch(struct cs_dsp_mock_bin_builder *builder,
|
||||
unsigned int alg_id, unsigned int alg_ver,
|
||||
int mem_region, unsigned int reg_addr_offset,
|
||||
const void *payload_data, size_t payload_len_bytes);
|
||||
struct firmware *cs_dsp_mock_bin_get_firmware(struct cs_dsp_mock_bin_builder *builder);
|
||||
|
||||
struct cs_dsp_mock_wmfw_builder *cs_dsp_mock_wmfw_init(struct cs_dsp_test *priv,
|
||||
int format_version);
|
||||
void cs_dsp_mock_wmfw_add_raw_block(struct cs_dsp_mock_wmfw_builder *builder,
|
||||
int mem_region, unsigned int mem_offset_dsp_words,
|
||||
const void *payload_data, size_t payload_len_bytes);
|
||||
void cs_dsp_mock_wmfw_add_info(struct cs_dsp_mock_wmfw_builder *builder,
|
||||
const char *info);
|
||||
void cs_dsp_mock_wmfw_add_data_block(struct cs_dsp_mock_wmfw_builder *builder,
|
||||
int mem_region, unsigned int mem_offset_dsp_words,
|
||||
const void *payload_data, size_t payload_len_bytes);
|
||||
void cs_dsp_mock_wmfw_start_alg_info_block(struct cs_dsp_mock_wmfw_builder *builder,
|
||||
unsigned int alg_id,
|
||||
const char *name,
|
||||
const char *description);
|
||||
void cs_dsp_mock_wmfw_add_coeff_desc(struct cs_dsp_mock_wmfw_builder *builder,
|
||||
const struct cs_dsp_mock_coeff_def *def);
|
||||
void cs_dsp_mock_wmfw_end_alg_info_block(struct cs_dsp_mock_wmfw_builder *builder);
|
||||
struct firmware *cs_dsp_mock_wmfw_get_firmware(struct cs_dsp_mock_wmfw_builder *builder);
|
||||
int cs_dsp_mock_wmfw_format_version(struct cs_dsp_mock_wmfw_builder *builder);
|
||||
Reference in New Issue
Block a user