xref: /linux/drivers/firmware/cirrus/test/cs_dsp_mock_bin.c (revision 1fd1dc41724319406b0aff221a352a400b0ddfc5)
1 // SPDX-License-Identifier: GPL-2.0-only
2 //
3 // bin file builder for cs_dsp KUnit tests.
4 //
5 // Copyright (C) 2024 Cirrus Logic, Inc. and
6 //                    Cirrus Logic International Semiconductor Ltd.
7 
8 #include <kunit/resource.h>
9 #include <kunit/test.h>
10 #include <linux/firmware/cirrus/cs_dsp.h>
11 #include <linux/firmware/cirrus/cs_dsp_test_utils.h>
12 #include <linux/firmware/cirrus/wmfw.h>
13 #include <linux/firmware.h>
14 #include <linux/math.h>
15 #include <linux/overflow.h>
16 #include <linux/string.h>
17 #include <linux/vmalloc.h>
18 
19 /* Buffer large enough for bin file content */
20 #define CS_DSP_MOCK_BIN_BUF_SIZE	32768
21 
22 KUNIT_DEFINE_ACTION_WRAPPER(vfree_action_wrapper, vfree, void *)
23 
24 struct cs_dsp_mock_bin_builder {
25 	struct cs_dsp_test *test_priv;
26 	void *buf;
27 	void *write_p;
28 	size_t bytes_used;
29 };
30 
31 /**
32  * cs_dsp_mock_bin_get_firmware() - Get struct firmware wrapper for data.
33  *
34  * @builder:	Pointer to struct cs_dsp_mock_bin_builder.
35  *
36  * Return: Pointer to a struct firmware wrapper for the data.
37  */
38 struct firmware *cs_dsp_mock_bin_get_firmware(struct cs_dsp_mock_bin_builder *builder)
39 {
40 	struct firmware *fw;
41 
42 	fw = kunit_kzalloc(builder->test_priv->test, sizeof(*fw), GFP_KERNEL);
43 	KUNIT_ASSERT_NOT_ERR_OR_NULL(builder->test_priv->test, fw);
44 
45 	fw->data = builder->buf;
46 	fw->size = builder->bytes_used;
47 
48 	return fw;
49 }
50 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_get_firmware, "FW_CS_DSP_KUNIT_TEST_UTILS");
51 
52 /**
53  * cs_dsp_mock_bin_add_raw_block() - Add a data block to the bin file.
54  *
55  * @builder:		Pointer to struct cs_dsp_mock_bin_builder.
56  * @alg_id:		Algorithm ID.
57  * @alg_ver:		Algorithm version.
58  * @type:		Type of the block.
59  * @offset:		16-bit offset.
60  * @offset32:		32-bit offset (sample rate on V1 and V2 file formats).
61  * @payload_data:	Pointer to buffer containing the payload data.
62  * @payload_len_bytes:	Length of payload data in bytes.
63  */
64 void cs_dsp_mock_bin_add_raw_block(struct cs_dsp_mock_bin_builder *builder,
65 				   unsigned int alg_id, unsigned int alg_ver,
66 				   int type, u16 offset, u32 offset32,
67 				   const void *payload_data, size_t payload_len_bytes)
68 {
69 	struct wmfw_coeff_item *item;
70 	size_t bytes_needed = struct_size_t(struct wmfw_coeff_item, data, payload_len_bytes);
71 
72 	KUNIT_ASSERT_TRUE(builder->test_priv->test,
73 			  (builder->write_p + bytes_needed) <
74 			  (builder->buf + CS_DSP_MOCK_BIN_BUF_SIZE));
75 
76 	item = builder->write_p;
77 
78 	item->offset = cpu_to_le16(offset);
79 	item->offset32 = cpu_to_le32(offset32);
80 	item->type = cpu_to_le16(type);
81 	item->id = cpu_to_le32(alg_id);
82 	item->ver = cpu_to_le32(alg_ver << 8);
83 	item->len = cpu_to_le32(payload_len_bytes);
84 
85 	if (payload_len_bytes)
86 		memcpy(item->data, payload_data, payload_len_bytes);
87 
88 	builder->write_p += bytes_needed;
89 	builder->bytes_used += bytes_needed;
90 }
91 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_raw_block, "FW_CS_DSP_KUNIT_TEST_UTILS");
92 
93 static void cs_dsp_mock_bin_add_name_or_info(struct cs_dsp_mock_bin_builder *builder,
94 					     const char *info, int type)
95 {
96 	size_t info_len = strlen(info);
97 	char *tmp = NULL;
98 
99 	if (info_len % 4) {
100 		/* Create a padded string with length a multiple of 4 */
101 		size_t copy_len = info_len;
102 		info_len = round_up(info_len, 4);
103 		tmp = kunit_kzalloc(builder->test_priv->test, info_len, GFP_KERNEL);
104 		KUNIT_ASSERT_NOT_ERR_OR_NULL(builder->test_priv->test, tmp);
105 		memcpy(tmp, info, copy_len);
106 		info = tmp;
107 	}
108 
109 	cs_dsp_mock_bin_add_raw_block(builder, 0, 0, WMFW_INFO_TEXT, 0, 0, info, info_len);
110 	kunit_kfree(builder->test_priv->test, tmp);
111 }
112 
113 /**
114  * cs_dsp_mock_bin_add_info() - Add an info block to the bin file.
115  *
116  * @builder:	Pointer to struct cs_dsp_mock_bin_builder.
117  * @info:	Pointer to info string to be copied into the file.
118  *
119  * The string will be padded to a length that is a multiple of 4 bytes.
120  */
121 void cs_dsp_mock_bin_add_info(struct cs_dsp_mock_bin_builder *builder,
122 			      const char *info)
123 {
124 	cs_dsp_mock_bin_add_name_or_info(builder, info, WMFW_INFO_TEXT);
125 }
126 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_info, "FW_CS_DSP_KUNIT_TEST_UTILS");
127 
128 /**
129  * cs_dsp_mock_bin_add_name() - Add a name block to the bin file.
130  *
131  * @builder:	Pointer to struct cs_dsp_mock_bin_builder.
132  * @name:	Pointer to name string to be copied into the file.
133  */
134 void cs_dsp_mock_bin_add_name(struct cs_dsp_mock_bin_builder *builder,
135 			      const char *name)
136 {
137 	cs_dsp_mock_bin_add_name_or_info(builder, name, WMFW_NAME_TEXT);
138 }
139 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_name, "FW_CS_DSP_KUNIT_TEST_UTILS");
140 
141 /**
142  * cs_dsp_mock_bin_add_patch() - Add a patch data block to the bin file.
143  *
144  * @builder:		Pointer to struct cs_dsp_mock_bin_builder.
145  * @alg_id:		Algorithm ID for the patch.
146  * @alg_ver:		Algorithm version for the patch.
147  * @mem_region:		Memory region for the patch.
148  * @reg_addr_offset:	Offset to start of data in register addresses.
149  * @payload_data:	Pointer to buffer containing the payload data.
150  * @payload_len_bytes:	Length of payload data in bytes.
151  */
152 void cs_dsp_mock_bin_add_patch(struct cs_dsp_mock_bin_builder *builder,
153 			       unsigned int alg_id, unsigned int alg_ver,
154 			       int mem_region, unsigned int reg_addr_offset,
155 			       const void *payload_data, size_t payload_len_bytes)
156 {
157 	/* Payload length must be a multiple of 4 */
158 	KUNIT_ASSERT_EQ(builder->test_priv->test, payload_len_bytes % 4, 0);
159 
160 	cs_dsp_mock_bin_add_raw_block(builder, alg_id, alg_ver,
161 				      mem_region, (u16)reg_addr_offset, 0,
162 				      payload_data, payload_len_bytes);
163 }
164 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_patch, "FW_CS_DSP_KUNIT_TEST_UTILS");
165 
166 /**
167  * cs_dsp_mock_bin_add_patch_off32() - Add a patch data block with 32-bit offset.
168  *
169  * @builder:		Pointer to struct cs_dsp_mock_bin_builder.
170  * @alg_id:		Algorithm ID for the patch.
171  * @alg_ver:		Algorithm version for the patch.
172  * @mem_region:		Memory region for the patch.
173  * @reg_addr_offset:	Offset to start of data in register addresses.
174  * @payload_data:	Pointer to buffer containing the payload data.
175  * @payload_len_bytes:	Length of payload data in bytes.
176  */
177 void cs_dsp_mock_bin_add_patch_off32(struct cs_dsp_mock_bin_builder *builder,
178 				     unsigned int alg_id, unsigned int alg_ver,
179 				     int mem_region, unsigned int reg_addr_offset,
180 				     const void *payload_data, size_t payload_len_bytes)
181 {
182 	/* Payload length must be a multiple of 4 */
183 	KUNIT_ASSERT_EQ(builder->test_priv->test, payload_len_bytes % 4, 0);
184 
185 	/* Mark the block as using the 32-bit offset */
186 	mem_region |= 0xf400;
187 
188 	cs_dsp_mock_bin_add_raw_block(builder, alg_id, alg_ver,
189 				      mem_region, 0, reg_addr_offset,
190 				      payload_data, payload_len_bytes);
191 }
192 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_patch_off32, "FW_CS_DSP_KUNIT_TEST_UTILS");
193 
194 /**
195  * cs_dsp_mock_bin_init() - Initialize a struct cs_dsp_mock_bin_builder.
196  *
197  * @priv:		Pointer to struct cs_dsp_test.
198  * @format_version:	Required bin format version.
199  * @fw_version:		Firmware version to put in bin file.
200  *
201  * Return: Pointer to created struct cs_dsp_mock_bin_builder.
202  */
203 struct cs_dsp_mock_bin_builder *cs_dsp_mock_bin_init(struct cs_dsp_test *priv,
204 						     int format_version,
205 						     unsigned int fw_version)
206 {
207 	struct cs_dsp_mock_bin_builder *builder;
208 	struct wmfw_coeff_hdr *hdr;
209 
210 	KUNIT_ASSERT_LE(priv->test, format_version, 0xff);
211 	KUNIT_ASSERT_LE(priv->test, fw_version, 0xffffff);
212 
213 	builder = kunit_kzalloc(priv->test, sizeof(*builder), GFP_KERNEL);
214 	KUNIT_ASSERT_NOT_ERR_OR_NULL(priv->test, builder);
215 	builder->test_priv = priv;
216 
217 	builder->buf = vmalloc(CS_DSP_MOCK_BIN_BUF_SIZE);
218 	KUNIT_ASSERT_NOT_NULL(priv->test, builder->buf);
219 	kunit_add_action_or_reset(priv->test, vfree_action_wrapper, builder->buf);
220 
221 	/* Create header */
222 	hdr = builder->buf;
223 	memcpy(hdr->magic, "WMDR", sizeof(hdr->magic));
224 	hdr->len = cpu_to_le32(offsetof(struct wmfw_coeff_hdr, data));
225 	hdr->ver = cpu_to_le32(fw_version | (format_version << 24));
226 	hdr->core_ver = cpu_to_le32(((u32)priv->dsp->type << 24) | priv->dsp->rev);
227 
228 	builder->write_p = hdr->data;
229 	builder->bytes_used = offsetof(struct wmfw_coeff_hdr, data);
230 
231 	return builder;
232 }
233 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_init, "FW_CS_DSP_KUNIT_TEST_UTILS");
234