xref: /linux/drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c (revision f694f30e81c4ade358eb8c75273bac1a48f0cb8f)
1 // SPDX-License-Identifier: GPL-2.0-only
2 //
3 // Mock DSP memory maps for cs_dsp KUnit tests.
4 //
5 // Copyright (C) 2024 Cirrus Logic, Inc. and
6 //                    Cirrus Logic International Semiconductor Ltd.
7 
8 #include <kunit/test.h>
9 #include <linux/firmware/cirrus/cs_dsp.h>
10 #include <linux/firmware/cirrus/cs_dsp_test_utils.h>
11 #include <linux/firmware/cirrus/wmfw.h>
12 #include <linux/math.h>
13 
14 const struct cs_dsp_region cs_dsp_mock_halo_dsp1_regions[] = {
15 	{ .type = WMFW_HALO_PM_PACKED,	.base = 0x3800000 },
16 	{ .type = WMFW_HALO_XM_PACKED,	.base = 0x2000000 },
17 	{ .type = WMFW_HALO_YM_PACKED,	.base = 0x2C00000 },
18 	{ .type = WMFW_ADSP2_XM,	.base = 0x2800000 },
19 	{ .type = WMFW_ADSP2_YM,	.base = 0x3400000 },
20 };
21 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_halo_dsp1_regions, "FW_CS_DSP_KUNIT_TEST_UTILS");
22 
23 /*  List of sizes in bytes, for each entry above */
24 const unsigned int cs_dsp_mock_halo_dsp1_region_sizes[] = {
25 	0x5000,		/* PM_PACKED */
26 	0x6000,		/* XM_PACKED */
27 	0x47F4,		/* YM_PACKED */
28 	0x8000,		/* XM_UNPACKED_24 */
29 	0x5FF8,		/* YM_UNPACKED_24 */
30 
31 	0		/* terminator */
32 };
33 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_halo_dsp1_region_sizes, "FW_CS_DSP_KUNIT_TEST_UTILS");
34 
35 const struct cs_dsp_region cs_dsp_mock_adsp2_32bit_dsp1_regions[] = {
36 	{ .type = WMFW_ADSP2_PM, .base = 0x080000 },
37 	{ .type = WMFW_ADSP2_XM, .base = 0x0a0000 },
38 	{ .type = WMFW_ADSP2_YM, .base = 0x0c0000 },
39 	{ .type = WMFW_ADSP2_ZM, .base = 0x0e0000 },
40 };
41 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_32bit_dsp1_regions, "FW_CS_DSP_KUNIT_TEST_UTILS");
42 
43 /* List of sizes in bytes, for each entry above */
44 const unsigned int cs_dsp_mock_adsp2_32bit_dsp1_region_sizes[] = {
45 	0x9000,	/* PM */
46 	0xa000,	/* ZM */
47 	0x2000,	/* XM */
48 	0x2000,	/* YM */
49 
50 	0	/* terminator */
51 };
52 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_32bit_dsp1_region_sizes, "FW_CS_DSP_KUNIT_TEST_UTILS");
53 
54 const struct cs_dsp_region cs_dsp_mock_adsp2_16bit_dsp1_regions[] = {
55 	{ .type = WMFW_ADSP2_PM, .base = 0x100000 },
56 	{ .type = WMFW_ADSP2_ZM, .base = 0x180000 },
57 	{ .type = WMFW_ADSP2_XM, .base = 0x190000 },
58 	{ .type = WMFW_ADSP2_YM, .base = 0x1a8000 },
59 };
60 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_16bit_dsp1_regions, "FW_CS_DSP_KUNIT_TEST_UTILS");
61 
62 /* List of sizes in bytes, for each entry above */
63 const unsigned int cs_dsp_mock_adsp2_16bit_dsp1_region_sizes[] = {
64 	0x6000,	/* PM */
65 	0x800,	/* ZM */
66 	0x800,	/* XM */
67 	0x800,	/* YM */
68 
69 	0	/* terminator */
70 };
71 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_16bit_dsp1_region_sizes, "FW_CS_DSP_KUNIT_TEST_UTILS");
72 
73 int cs_dsp_mock_count_regions(const unsigned int *region_sizes)
74 {
75 	int i;
76 
77 	for (i = 0; region_sizes[i]; ++i)
78 		;
79 
80 	return i;
81 }
82 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_count_regions, "FW_CS_DSP_KUNIT_TEST_UTILS");
83 
84 /**
85  * cs_dsp_mock_size_of_region() - Return size of given memory region.
86  *
87  * @dsp:	Pointer to struct cs_dsp.
88  * @mem_type:	Memory region type.
89  *
90  * Return: Size of region in bytes.
91  */
92 unsigned int cs_dsp_mock_size_of_region(const struct cs_dsp *dsp, int mem_type)
93 {
94 	const unsigned int *sizes;
95 	int i;
96 
97 	if (dsp->mem == cs_dsp_mock_halo_dsp1_regions)
98 		sizes = cs_dsp_mock_halo_dsp1_region_sizes;
99 	else if (dsp->mem == cs_dsp_mock_adsp2_32bit_dsp1_regions)
100 		sizes = cs_dsp_mock_adsp2_32bit_dsp1_region_sizes;
101 	else if (dsp->mem == cs_dsp_mock_adsp2_16bit_dsp1_regions)
102 		sizes = cs_dsp_mock_adsp2_16bit_dsp1_region_sizes;
103 	else
104 		return 0;
105 
106 	for (i = 0; i < dsp->num_mems; ++i) {
107 		if (dsp->mem[i].type == mem_type)
108 			return sizes[i];
109 	}
110 
111 	return 0;
112 }
113 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_size_of_region, "FW_CS_DSP_KUNIT_TEST_UTILS");
114 
115 /**
116  * cs_dsp_mock_base_addr_for_mem() - Base register address for memory region.
117  *
118  * @priv:	Pointer to struct cs_dsp_test.
119  * @mem_type:	Memory region type.
120  *
121  * Return: Base register address of region.
122  */
123 unsigned int cs_dsp_mock_base_addr_for_mem(struct cs_dsp_test *priv, int mem_type)
124 {
125 	int num_mems = priv->dsp->num_mems;
126 	const struct cs_dsp_region *region = priv->dsp->mem;
127 	int i;
128 
129 	for (i = 0; i < num_mems; ++i) {
130 		if (region[i].type == mem_type)
131 			return region[i].base;
132 	}
133 
134 	KUNIT_FAIL(priv->test, "Unexpected region %d\n", mem_type);
135 
136 	return 0;
137 }
138 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_base_addr_for_mem, "FW_CS_DSP_KUNIT_TEST_UTILS");
139 
140 /**
141  * cs_dsp_mock_reg_addr_inc_per_unpacked_word() - Unpacked register address increment per DSP word.
142  *
143  * @priv:	Pointer to struct cs_dsp_test.
144  *
145  * Return: Amount by which register address increments to move to the next
146  *	   DSP word in unpacked XM/YM/ZM.
147  */
148 unsigned int cs_dsp_mock_reg_addr_inc_per_unpacked_word(struct cs_dsp_test *priv)
149 {
150 	switch (priv->dsp->type) {
151 	case WMFW_ADSP2:
152 		return 2; /* two 16-bit register indexes per XM/YM/ZM word */
153 	case WMFW_HALO:
154 		return 4; /* one byte-addressed 32-bit register per XM/YM/ZM word */
155 	default:
156 		KUNIT_FAIL(priv->test, "Unexpected DSP type\n");
157 		return -1;
158 	}
159 }
160 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_reg_addr_inc_per_unpacked_word, "FW_CS_DSP_KUNIT_TEST_UTILS");
161 
162 /**
163  * cs_dsp_mock_reg_block_length_bytes() - Number of bytes in an access block.
164  *
165  * @priv:	Pointer to struct cs_dsp_test.
166  * @mem_type:	Memory region type.
167  *
168  * Return: Total number of bytes in a group of registers forming the
169  * smallest bus access size (including any padding bits). For unpacked
170  * memory this is the number of registers containing one DSP word.
171  * For packed memory this is the number of registers in one packed
172  * access block.
173  */
174 unsigned int cs_dsp_mock_reg_block_length_bytes(struct cs_dsp_test *priv, int mem_type)
175 {
176 	switch (priv->dsp->type) {
177 	case WMFW_ADSP2:
178 		switch (mem_type) {
179 		case WMFW_ADSP2_PM:
180 			return 3 * regmap_get_val_bytes(priv->dsp->regmap);
181 		case WMFW_ADSP2_XM:
182 		case WMFW_ADSP2_YM:
183 		case WMFW_ADSP2_ZM:
184 			return sizeof(u32);
185 		default:
186 			break;
187 		}
188 		break;
189 	case WMFW_HALO:
190 		switch (mem_type) {
191 		case WMFW_ADSP2_XM:
192 		case WMFW_ADSP2_YM:
193 			return sizeof(u32);
194 		case WMFW_HALO_PM_PACKED:
195 			return 5 * sizeof(u32);
196 		case WMFW_HALO_XM_PACKED:
197 		case WMFW_HALO_YM_PACKED:
198 			return 3 * sizeof(u32);
199 		default:
200 			break;
201 		}
202 		break;
203 	default:
204 		KUNIT_FAIL(priv->test, "Unexpected DSP type\n");
205 		return 0;
206 	}
207 
208 	KUNIT_FAIL(priv->test, "Unexpected mem type\n");
209 
210 	return 0;
211 }
212 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_reg_block_length_bytes, "FW_CS_DSP_KUNIT_TEST_UTILS");
213 
214 /**
215  * cs_dsp_mock_reg_block_length_registers() - Number of registers in an access block.
216  *
217  * @priv:	Pointer to struct cs_dsp_test.
218  * @mem_type:	Memory region type.
219  *
220  * Return: Total number of register forming the smallest bus access size.
221  * For unpacked memory this is the number of registers containing one
222  * DSP word. For packed memory this is the number of registers in one
223  * packed access block.
224  */
225 unsigned int cs_dsp_mock_reg_block_length_registers(struct cs_dsp_test *priv, int mem_type)
226 {
227 	return cs_dsp_mock_reg_block_length_bytes(priv, mem_type) /
228 	       regmap_get_val_bytes(priv->dsp->regmap);
229 }
230 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_reg_block_length_registers, "FW_CS_DSP_KUNIT_TEST_UTILS");
231 
232 /**
233  * cs_dsp_mock_reg_block_length_dsp_words() - Number of dsp_words in an access block.
234  *
235  * @priv:	Pointer to struct cs_dsp_test.
236  * @mem_type:	Memory region type.
237  *
238  * Return: Total number of DSP words in a group of registers forming the
239  * smallest bus access size.
240  */
241 unsigned int cs_dsp_mock_reg_block_length_dsp_words(struct cs_dsp_test *priv, int mem_type)
242 {
243 	switch (priv->dsp->type) {
244 	case WMFW_ADSP2:
245 		switch (mem_type) {
246 		case WMFW_ADSP2_PM:
247 			return regmap_get_val_bytes(priv->dsp->regmap) / 2;
248 		case WMFW_ADSP2_XM:
249 		case WMFW_ADSP2_YM:
250 		case WMFW_ADSP2_ZM:
251 			return 1;
252 		default:
253 			break;
254 		}
255 		break;
256 	case WMFW_HALO:
257 		switch (mem_type) {
258 		case WMFW_ADSP2_XM:
259 		case WMFW_ADSP2_YM:
260 			return 1;
261 		case WMFW_HALO_PM_PACKED:
262 		case WMFW_HALO_XM_PACKED:
263 		case WMFW_HALO_YM_PACKED:
264 			return 4;
265 		default:
266 			break;
267 		}
268 		break;
269 	default:
270 		KUNIT_FAIL(priv->test, "Unexpected DSP type\n");
271 		return 0;
272 	}
273 
274 	KUNIT_FAIL(priv->test, "Unexpected mem type\n");
275 
276 	return 0;
277 }
278 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_reg_block_length_dsp_words, "FW_CS_DSP_KUNIT_TEST_UTILS");
279 
280 /**
281  * cs_dsp_mock_has_zm() - DSP has ZM
282  *
283  * @priv:	Pointer to struct cs_dsp_test.
284  *
285  * Return: True if DSP has ZM.
286  */
287 bool cs_dsp_mock_has_zm(struct cs_dsp_test *priv)
288 {
289 	switch (priv->dsp->type) {
290 	case WMFW_ADSP2:
291 		return true;
292 	default:
293 		return false;
294 	}
295 }
296 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_has_zm, "FW_CS_DSP_KUNIT_TEST_UTILS");
297 
298 /**
299  * cs_dsp_mock_packed_to_unpacked_mem_type() - Unpacked region that is
300  * the same memory as a packed region.
301  *
302  * @packed_mem_type:	Type of packed memory region.
303  *
304  * Return: unpacked type that is the same memory as packed_mem_type.
305  */
306 int cs_dsp_mock_packed_to_unpacked_mem_type(int packed_mem_type)
307 {
308 	switch (packed_mem_type) {
309 	case WMFW_HALO_XM_PACKED:
310 		return WMFW_ADSP2_XM;
311 	case WMFW_HALO_YM_PACKED:
312 		return WMFW_ADSP2_YM;
313 	default:
314 		return -1;
315 	}
316 }
317 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_packed_to_unpacked_mem_type, "FW_CS_DSP_KUNIT_TEST_UTILS");
318 
319 /**
320  * cs_dsp_mock_num_dsp_words_to_num_packed_regs() - Number of DSP words
321  * to number of packed registers.
322  *
323  * @num_dsp_words:	Number of DSP words.
324  *
325  * Convert number of DSP words to number of packed registers rounded
326  * down to the nearest register.
327  *
328  * Return: Number of packed registers.
329  */
330 unsigned int cs_dsp_mock_num_dsp_words_to_num_packed_regs(unsigned int num_dsp_words)
331 {
332 	/* There are 3 registers for every 4 packed words */
333 	return (num_dsp_words * 3) / 4;
334 }
335 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_num_dsp_words_to_num_packed_regs, "FW_CS_DSP_KUNIT_TEST_UTILS");
336 
337 static const struct wmfw_halo_id_hdr cs_dsp_mock_halo_xm_hdr = {
338 	.fw = {
339 		.core_id = cpu_to_be32(WMFW_HALO << 16),
340 		.block_rev = cpu_to_be32(3 << 16),
341 		.vendor_id = cpu_to_be32(0x2),
342 		.id = cpu_to_be32(0xabcdef),
343 		.ver = cpu_to_be32(0x090101),
344 	},
345 
346 	/*
347 	 * Leave enough space for this header and 40 algorithm descriptors.
348 	 * base and size are counted in DSP words.
349 	 */
350 	.xm_base = cpu_to_be32(((sizeof(struct wmfw_halo_id_hdr) +
351 				(40 * sizeof(struct wmfw_halo_alg_hdr)))
352 				/ 4) * 3),
353 	.xm_size = cpu_to_be32(0x20),
354 
355 	/* Allocate a dummy word of YM */
356 	.ym_base = cpu_to_be32(0),
357 	.ym_size = cpu_to_be32(1),
358 
359 	.n_algs = 0,
360 };
361 
362 static const struct wmfw_adsp2_id_hdr cs_dsp_mock_adsp2_xm_hdr = {
363 	.fw = {
364 		.core_id = cpu_to_be32(WMFW_ADSP2 << 16),
365 		.core_rev = cpu_to_be32(2 << 16),
366 		.id = cpu_to_be32(0xabcdef),
367 		.ver = cpu_to_be32(0x090101),
368 	},
369 
370 	/*
371 	 * Leave enough space for this header and 40 algorithm descriptors.
372 	 * base and size are counted in DSP words.
373 	 */
374 	.xm = cpu_to_be32(((sizeof(struct wmfw_adsp2_id_hdr) +
375 				(40 * sizeof(struct wmfw_adsp2_alg_hdr)))
376 				/ 4) * 3),
377 
378 	.ym = cpu_to_be32(0),
379 	.zm = cpu_to_be32(0),
380 
381 	.n_algs = 0,
382 };
383 
384 /**
385  * cs_dsp_mock_xm_header_get_alg_base_in_words() - Algorithm base offset in DSP words.
386  *
387  * @priv:	Pointer to struct cs_dsp_test.
388  * @alg_id:	Algorithm ID.
389  * @mem_type:	Memory region type.
390  *
391  * Lookup an algorithm in the XM header and return the base offset in
392  * DSP words of the algorithm data in the requested memory region.
393  *
394  * Return: Offset in DSP words.
395  */
396 unsigned int cs_dsp_mock_xm_header_get_alg_base_in_words(struct cs_dsp_test *priv,
397 							 unsigned int alg_id,
398 							 int mem_type)
399 {
400 	unsigned int xm = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_XM);
401 	union {
402 		struct wmfw_adsp2_alg_hdr adsp2;
403 		struct wmfw_halo_alg_hdr halo;
404 	} alg;
405 	unsigned int alg_hdr_addr;
406 	unsigned int val, xm_base = 0, ym_base = 0, zm_base = 0;
407 	int ret;
408 
409 	switch (priv->dsp->type) {
410 	case WMFW_ADSP2:
411 		alg_hdr_addr = xm + (sizeof(struct wmfw_adsp2_id_hdr) / 2);
412 		for (;; alg_hdr_addr += sizeof(alg.adsp2) / 2) {
413 			ret = regmap_read(priv->dsp->regmap, alg_hdr_addr, &val);
414 			KUNIT_ASSERT_GE(priv->test, ret, 0);
415 			KUNIT_ASSERT_NE(priv->test, val, 0xbedead);
416 			ret = regmap_raw_read(priv->dsp->regmap, alg_hdr_addr,
417 					      &alg.adsp2, sizeof(alg.adsp2));
418 			KUNIT_ASSERT_GE(priv->test, ret, 0);
419 			if (be32_to_cpu(alg.adsp2.alg.id) == alg_id) {
420 				xm_base = be32_to_cpu(alg.adsp2.xm);
421 				ym_base = be32_to_cpu(alg.adsp2.ym);
422 				zm_base = be32_to_cpu(alg.adsp2.zm);
423 				break;
424 			}
425 		}
426 		break;
427 	case WMFW_HALO:
428 		alg_hdr_addr = xm + sizeof(struct wmfw_halo_id_hdr);
429 		for (;; alg_hdr_addr += sizeof(alg.halo)) {
430 			ret = regmap_read(priv->dsp->regmap, alg_hdr_addr, &val);
431 			KUNIT_ASSERT_GE(priv->test, ret, 0);
432 			KUNIT_ASSERT_NE(priv->test, val, 0xbedead);
433 			ret = regmap_raw_read(priv->dsp->regmap, alg_hdr_addr,
434 					      &alg.halo, sizeof(alg.halo));
435 			KUNIT_ASSERT_GE(priv->test, ret, 0);
436 			if (be32_to_cpu(alg.halo.alg.id) == alg_id) {
437 				xm_base = be32_to_cpu(alg.halo.xm_base);
438 				ym_base = be32_to_cpu(alg.halo.ym_base);
439 				break;
440 			}
441 		}
442 		break;
443 	default:
444 		KUNIT_FAIL(priv->test, "Unexpected DSP type %d\n", priv->dsp->type);
445 		return 0;
446 	}
447 
448 	switch (mem_type) {
449 	case WMFW_ADSP2_XM:
450 	case WMFW_HALO_XM_PACKED:
451 		return xm_base;
452 	case WMFW_ADSP2_YM:
453 	case WMFW_HALO_YM_PACKED:
454 		return ym_base;
455 	case WMFW_ADSP2_ZM:
456 		return zm_base;
457 	default:
458 		KUNIT_FAIL(priv->test, "Bad mem_type\n");
459 		return 0;
460 	}
461 }
462 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_get_alg_base_in_words, "FW_CS_DSP_KUNIT_TEST_UTILS");
463 
464 /**
465  * cs_dsp_mock_xm_header_get_fw_version_from_regmap() - Firmware version.
466  *
467  * @priv:	Pointer to struct cs_dsp_test.
468  *
469  * Return: Firmware version word value.
470  */
471 unsigned int cs_dsp_mock_xm_header_get_fw_version_from_regmap(struct cs_dsp_test *priv)
472 {
473 	unsigned int xm = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_XM);
474 	union {
475 		struct wmfw_id_hdr adsp2;
476 		struct wmfw_v3_id_hdr halo;
477 	} hdr;
478 
479 	switch (priv->dsp->type) {
480 	case WMFW_ADSP2:
481 		regmap_raw_read(priv->dsp->regmap, xm, &hdr.adsp2, sizeof(hdr.adsp2));
482 		return be32_to_cpu(hdr.adsp2.ver);
483 	case WMFW_HALO:
484 		regmap_raw_read(priv->dsp->regmap, xm, &hdr.halo, sizeof(hdr.halo));
485 		return be32_to_cpu(hdr.halo.ver);
486 	default:
487 		KUNIT_FAIL(priv->test, NULL);
488 		return 0;
489 	}
490 }
491 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_get_fw_version_from_regmap,
492 		     "FW_CS_DSP_KUNIT_TEST_UTILS");
493 
494 /**
495  * cs_dsp_mock_xm_header_get_fw_version() - Firmware version.
496  *
497  * @header:	Pointer to struct cs_dsp_mock_xm_header.
498  *
499  * Return: Firmware version word value.
500  */
501 unsigned int cs_dsp_mock_xm_header_get_fw_version(struct cs_dsp_mock_xm_header *header)
502 {
503 	const struct wmfw_id_hdr *adsp2_hdr;
504 	const struct wmfw_v3_id_hdr *halo_hdr;
505 
506 	switch (header->test_priv->dsp->type) {
507 	case WMFW_ADSP2:
508 		adsp2_hdr = header->blob_data;
509 		return be32_to_cpu(adsp2_hdr->ver);
510 	case WMFW_HALO:
511 		halo_hdr = header->blob_data;
512 		return be32_to_cpu(halo_hdr->ver);
513 	default:
514 		KUNIT_FAIL(header->test_priv->test, NULL);
515 		return 0;
516 	}
517 }
518 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_get_fw_version, "FW_CS_DSP_KUNIT_TEST_UTILS");
519 
520 /**
521  * cs_dsp_mock_xm_header_drop_from_regmap_cache() - Drop XM header from regmap cache.
522  *
523  * @priv:	Pointer to struct cs_dsp_test.
524  */
525 void cs_dsp_mock_xm_header_drop_from_regmap_cache(struct cs_dsp_test *priv)
526 {
527 	unsigned int xm = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_XM);
528 	unsigned int bytes;
529 	__be32 num_algs_be32;
530 	unsigned int num_algs;
531 
532 	switch (priv->dsp->type) {
533 	case WMFW_ADSP2:
534 		/*
535 		 * Could be one 32-bit register or two 16-bit registers.
536 		 * A raw read will read the requested number of bytes.
537 		 */
538 		regmap_raw_read(priv->dsp->regmap,
539 				xm + (offsetof(struct wmfw_adsp2_id_hdr, n_algs) / 2),
540 				&num_algs_be32, sizeof(num_algs_be32));
541 		num_algs = be32_to_cpu(num_algs_be32);
542 		bytes = sizeof(struct wmfw_adsp2_id_hdr) +
543 			(num_algs * sizeof(struct wmfw_adsp2_alg_hdr)) +
544 			4 /* terminator word */;
545 
546 		regcache_drop_region(priv->dsp->regmap, xm, xm + (bytes / 2) - 1);
547 		break;
548 	case WMFW_HALO:
549 		regmap_read(priv->dsp->regmap,
550 			    xm + offsetof(struct wmfw_halo_id_hdr, n_algs),
551 			    &num_algs);
552 		bytes = sizeof(struct wmfw_halo_id_hdr) +
553 			(num_algs * sizeof(struct wmfw_halo_alg_hdr)) +
554 			4 /* terminator word */;
555 
556 		regcache_drop_region(priv->dsp->regmap, xm, xm + bytes - 4);
557 		break;
558 	default:
559 		break;
560 	}
561 }
562 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_drop_from_regmap_cache, "FW_CS_DSP_KUNIT_TEST_UTILS");
563 
564 static void cs_dsp_mock_xm_header_add_adsp2_algs(struct cs_dsp_mock_xm_header *builder,
565 						 const struct cs_dsp_mock_alg_def *algs,
566 						 size_t num_algs)
567 {
568 	struct wmfw_adsp2_id_hdr *hdr = builder->blob_data;
569 	unsigned int next_free_xm_word, next_free_ym_word, next_free_zm_word;
570 
571 	next_free_xm_word = be32_to_cpu(hdr->xm);
572 	next_free_ym_word = be32_to_cpu(hdr->ym);
573 	next_free_zm_word = be32_to_cpu(hdr->zm);
574 
575 	/* Set num_algs in XM header. */
576 	hdr->n_algs = cpu_to_be32(num_algs);
577 
578 	/* Create algorithm descriptor list */
579 	struct wmfw_adsp2_alg_hdr *alg_info =
580 			(struct wmfw_adsp2_alg_hdr *)(&hdr[1]);
581 
582 	for (; num_algs > 0; num_algs--, algs++, alg_info++) {
583 		unsigned int alg_xm_last, alg_ym_last, alg_zm_last;
584 
585 		alg_info->alg.id = cpu_to_be32(algs->id);
586 		alg_info->alg.ver = cpu_to_be32(algs->ver);
587 		alg_info->xm = cpu_to_be32(algs->xm_base_words);
588 		alg_info->ym = cpu_to_be32(algs->ym_base_words);
589 		alg_info->zm = cpu_to_be32(algs->zm_base_words);
590 
591 		/* Check if we need to auto-allocate base addresses */
592 		if (!alg_info->xm && algs->xm_size_words)
593 			alg_info->xm = cpu_to_be32(next_free_xm_word);
594 
595 		if (!alg_info->ym && algs->ym_size_words)
596 			alg_info->ym = cpu_to_be32(next_free_ym_word);
597 
598 		if (!alg_info->zm && algs->zm_size_words)
599 			alg_info->zm = cpu_to_be32(next_free_zm_word);
600 
601 		alg_xm_last = be32_to_cpu(alg_info->xm) + algs->xm_size_words - 1;
602 		if (alg_xm_last > next_free_xm_word)
603 			next_free_xm_word = alg_xm_last;
604 
605 		alg_ym_last = be32_to_cpu(alg_info->ym) + algs->ym_size_words - 1;
606 		if (alg_ym_last > next_free_ym_word)
607 			next_free_ym_word = alg_ym_last;
608 
609 		alg_zm_last = be32_to_cpu(alg_info->zm) + algs->zm_size_words - 1;
610 		if (alg_zm_last > next_free_zm_word)
611 			next_free_zm_word = alg_zm_last;
612 	}
613 
614 	/* Write list terminator */
615 	*(__be32 *)(alg_info) = cpu_to_be32(0xbedead);
616 }
617 
618 static void cs_dsp_mock_xm_header_add_halo_algs(struct cs_dsp_mock_xm_header *builder,
619 						const struct cs_dsp_mock_alg_def *algs,
620 						size_t num_algs)
621 {
622 	struct wmfw_halo_id_hdr *hdr = builder->blob_data;
623 	unsigned int next_free_xm_word, next_free_ym_word;
624 
625 	/* Assume we're starting with bare header */
626 	next_free_xm_word = be32_to_cpu(hdr->xm_base) + be32_to_cpu(hdr->xm_size) - 1;
627 	next_free_ym_word = be32_to_cpu(hdr->ym_base) + be32_to_cpu(hdr->ym_size) - 1;
628 
629 	/* Set num_algs in XM header */
630 	hdr->n_algs = cpu_to_be32(num_algs);
631 
632 	/* Create algorithm descriptor list */
633 	struct wmfw_halo_alg_hdr *alg_info =
634 			(struct wmfw_halo_alg_hdr *)(&hdr[1]);
635 
636 	for (; num_algs > 0; num_algs--, algs++, alg_info++) {
637 		unsigned int alg_xm_last, alg_ym_last;
638 
639 		alg_info->alg.id = cpu_to_be32(algs->id);
640 		alg_info->alg.ver = cpu_to_be32(algs->ver);
641 		alg_info->xm_base = cpu_to_be32(algs->xm_base_words);
642 		alg_info->xm_size = cpu_to_be32(algs->xm_size_words);
643 		alg_info->ym_base = cpu_to_be32(algs->ym_base_words);
644 		alg_info->ym_size = cpu_to_be32(algs->ym_size_words);
645 
646 		/* Check if we need to auto-allocate base addresses */
647 		if (!alg_info->xm_base && alg_info->xm_size)
648 			alg_info->xm_base = cpu_to_be32(next_free_xm_word);
649 
650 		if (!alg_info->ym_base && alg_info->ym_size)
651 			alg_info->ym_base = cpu_to_be32(next_free_ym_word);
652 
653 		alg_xm_last = be32_to_cpu(alg_info->xm_base) + be32_to_cpu(alg_info->xm_size) - 1;
654 		if (alg_xm_last > next_free_xm_word)
655 			next_free_xm_word = alg_xm_last;
656 
657 		alg_ym_last = be32_to_cpu(alg_info->ym_base) + be32_to_cpu(alg_info->ym_size) - 1;
658 		if (alg_ym_last > next_free_ym_word)
659 			next_free_ym_word = alg_ym_last;
660 	}
661 
662 	/* Write list terminator */
663 	*(__be32 *)(alg_info) = cpu_to_be32(0xbedead);
664 }
665 
666 /**
667  * cs_dsp_mock_xm_header_write_to_regmap() - Write XM header to regmap.
668  *
669  * @header:	Pointer to struct cs_dsp_mock_xm_header.
670  *
671  * The data in header is written to the XM addresses in the regmap.
672  *
673  * Return: 0 on success, else negative error code.
674  */
675 int cs_dsp_mock_xm_header_write_to_regmap(struct cs_dsp_mock_xm_header *header)
676 {
677 	struct cs_dsp_test *priv = header->test_priv;
678 	unsigned int reg_addr = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_XM);
679 
680 	/*
681 	 * One 32-bit word corresponds to one 32-bit unpacked XM word so the
682 	 * blob can be written directly to the regmap.
683 	 */
684 	return regmap_raw_write(priv->dsp->regmap, reg_addr,
685 				header->blob_data, header->blob_size_bytes);
686 }
687 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_write_to_regmap, "FW_CS_DSP_KUNIT_TEST_UTILS");
688 
689 /**
690  * cs_dsp_create_mock_xm_header() - Create a dummy XM header.
691  *
692  * @priv:	Pointer to struct cs_dsp_test.
693  * @algs:	Pointer to array of struct cs_dsp_mock_alg_def listing the
694  *		dummy algorithm entries to include in the XM header.
695  * @num_algs:	Number of entries in the algs array.
696  *
697  * Return: Pointer to created struct cs_dsp_mock_xm_header.
698  */
699 struct cs_dsp_mock_xm_header *cs_dsp_create_mock_xm_header(struct cs_dsp_test *priv,
700 							   const struct cs_dsp_mock_alg_def *algs,
701 							   size_t num_algs)
702 {
703 	struct cs_dsp_mock_xm_header *builder;
704 	size_t total_bytes_required;
705 	const void *header;
706 	size_t header_size_bytes;
707 
708 	builder = kunit_kzalloc(priv->test, sizeof(*builder), GFP_KERNEL);
709 	KUNIT_ASSERT_NOT_ERR_OR_NULL(priv->test, builder);
710 	builder->test_priv = priv;
711 
712 	switch (priv->dsp->type) {
713 	case WMFW_ADSP2:
714 		header = &cs_dsp_mock_adsp2_xm_hdr;
715 		header_size_bytes = sizeof(cs_dsp_mock_adsp2_xm_hdr);
716 		total_bytes_required = header_size_bytes +
717 				       (num_algs * sizeof(struct wmfw_adsp2_alg_hdr))
718 				       + 4; /* terminator word */
719 		break;
720 	case WMFW_HALO:
721 		header = &cs_dsp_mock_halo_xm_hdr,
722 		header_size_bytes = sizeof(cs_dsp_mock_halo_xm_hdr);
723 		total_bytes_required = header_size_bytes +
724 				       (num_algs * sizeof(struct wmfw_halo_alg_hdr))
725 				       + 4; /* terminator word */
726 		break;
727 	default:
728 		KUNIT_FAIL(priv->test, "%s unexpected DSP type %d\n",
729 			   __func__, priv->dsp->type);
730 		return NULL;
731 	}
732 
733 	builder->blob_data = kunit_kzalloc(priv->test, total_bytes_required, GFP_KERNEL);
734 	KUNIT_ASSERT_NOT_ERR_OR_NULL(priv->test, builder->blob_data);
735 	builder->blob_size_bytes = total_bytes_required;
736 
737 	memcpy(builder->blob_data, header, header_size_bytes);
738 
739 	switch (priv->dsp->type) {
740 	case WMFW_ADSP2:
741 		cs_dsp_mock_xm_header_add_adsp2_algs(builder, algs, num_algs);
742 		break;
743 	case WMFW_HALO:
744 		cs_dsp_mock_xm_header_add_halo_algs(builder, algs, num_algs);
745 		break;
746 	default:
747 		break;
748 	}
749 
750 	return builder;
751 }
752 EXPORT_SYMBOL_NS_GPL(cs_dsp_create_mock_xm_header, "FW_CS_DSP_KUNIT_TEST_UTILS");
753