File Index
Files and directories
Sign in
FILE

cs_dsp_mock_mem_maps.c

File preview with syntax highlighting.

linux-6.19.14/drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c C
  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
 14const 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};
 21EXPORT_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 */
 24const 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};
 33EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_halo_dsp1_region_sizes, "FW_CS_DSP_KUNIT_TEST_UTILS");
 34
 35const 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};
 41EXPORT_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 */
 44const 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};
 52EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_32bit_dsp1_region_sizes, "FW_CS_DSP_KUNIT_TEST_UTILS");
 53
 54const 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};
 60EXPORT_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 */
 63const 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};
 71EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_16bit_dsp1_region_sizes, "FW_CS_DSP_KUNIT_TEST_UTILS");
 72
 73int 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}
 82EXPORT_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 */
 92unsigned 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}
113EXPORT_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 */
123unsigned 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}
138EXPORT_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 */
148unsigned 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}
160EXPORT_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 */
174unsigned 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}
212EXPORT_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 */
225unsigned 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}
230EXPORT_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 */
241unsigned 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}
278EXPORT_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 */
287bool 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}
296EXPORT_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 */
306int 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}
317EXPORT_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 */
330unsigned 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}
335EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_num_dsp_words_to_num_packed_regs, "FW_CS_DSP_KUNIT_TEST_UTILS");
336
337static 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
362static 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 */
396unsigned 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}
462EXPORT_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() - Firmware version.
466 *
467 * @header:	Pointer to struct cs_dsp_mock_xm_header.
468 *
469 * Return: Firmware version word value.
470 */
471unsigned int cs_dsp_mock_xm_header_get_fw_version(struct cs_dsp_mock_xm_header *header)
472{
473	const struct wmfw_id_hdr *adsp2_hdr;
474	const struct wmfw_v3_id_hdr *halo_hdr;
475
476	switch (header->test_priv->dsp->type) {
477	case WMFW_ADSP2:
478		adsp2_hdr = header->blob_data;
479		return be32_to_cpu(adsp2_hdr->ver);
480	case WMFW_HALO:
481		halo_hdr = header->blob_data;
482		return be32_to_cpu(halo_hdr->ver);
483	default:
484		KUNIT_FAIL(header->test_priv->test, NULL);
485		return 0;
486	}
487}
488EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_get_fw_version, "FW_CS_DSP_KUNIT_TEST_UTILS");
489
490/**
491 * cs_dsp_mock_xm_header_drop_from_regmap_cache() - Drop XM header from regmap cache.
492 *
493 * @priv:	Pointer to struct cs_dsp_test.
494 */
495void cs_dsp_mock_xm_header_drop_from_regmap_cache(struct cs_dsp_test *priv)
496{
497	unsigned int xm = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_XM);
498	unsigned int bytes;
499	__be32 num_algs_be32;
500	unsigned int num_algs;
501
502	switch (priv->dsp->type) {
503	case WMFW_ADSP2:
504		/*
505		 * Could be one 32-bit register or two 16-bit registers.
506		 * A raw read will read the requested number of bytes.
507		 */
508		KUNIT_ASSERT_GE(priv->test, 0,
509				regmap_raw_read(priv->dsp->regmap,
510						xm +
511						(offsetof(struct wmfw_adsp2_id_hdr, n_algs) / 2),
512						&num_algs_be32, sizeof(num_algs_be32)));
513		num_algs = be32_to_cpu(num_algs_be32);
514		bytes = sizeof(struct wmfw_adsp2_id_hdr) +
515			(num_algs * sizeof(struct wmfw_adsp2_alg_hdr)) +
516			4 /* terminator word */;
517
518		regcache_drop_region(priv->dsp->regmap, xm, xm + (bytes / 2) - 1);
519		break;
520	case WMFW_HALO:
521		KUNIT_ASSERT_GE(priv->test, 0,
522				regmap_read(priv->dsp->regmap,
523					    xm + offsetof(struct wmfw_halo_id_hdr, n_algs),
524					    &num_algs));
525		bytes = sizeof(struct wmfw_halo_id_hdr) +
526			(num_algs * sizeof(struct wmfw_halo_alg_hdr)) +
527			4 /* terminator word */;
528
529		regcache_drop_region(priv->dsp->regmap, xm, xm + bytes - 4);
530		break;
531	default:
532		break;
533	}
534}
535EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_drop_from_regmap_cache, "FW_CS_DSP_KUNIT_TEST_UTILS");
536
537static void cs_dsp_mock_xm_header_add_adsp2_algs(struct cs_dsp_mock_xm_header *builder,
538						 const struct cs_dsp_mock_alg_def *algs,
539						 size_t num_algs)
540{
541	struct wmfw_adsp2_id_hdr *hdr = builder->blob_data;
542	unsigned int next_free_xm_word, next_free_ym_word, next_free_zm_word;
543
544	next_free_xm_word = be32_to_cpu(hdr->xm);
545	next_free_ym_word = be32_to_cpu(hdr->ym);
546	next_free_zm_word = be32_to_cpu(hdr->zm);
547
548	/* Set num_algs in XM header. */
549	hdr->n_algs = cpu_to_be32(num_algs);
550
551	/* Create algorithm descriptor list */
552	struct wmfw_adsp2_alg_hdr *alg_info =
553			(struct wmfw_adsp2_alg_hdr *)(&hdr[1]);
554
555	for (; num_algs > 0; num_algs--, algs++, alg_info++) {
556		unsigned int alg_xm_last, alg_ym_last, alg_zm_last;
557
558		alg_info->alg.id = cpu_to_be32(algs->id);
559		alg_info->alg.ver = cpu_to_be32(algs->ver);
560		alg_info->xm = cpu_to_be32(algs->xm_base_words);
561		alg_info->ym = cpu_to_be32(algs->ym_base_words);
562		alg_info->zm = cpu_to_be32(algs->zm_base_words);
563
564		/* Check if we need to auto-allocate base addresses */
565		if (!alg_info->xm && algs->xm_size_words)
566			alg_info->xm = cpu_to_be32(next_free_xm_word);
567
568		if (!alg_info->ym && algs->ym_size_words)
569			alg_info->ym = cpu_to_be32(next_free_ym_word);
570
571		if (!alg_info->zm && algs->zm_size_words)
572			alg_info->zm = cpu_to_be32(next_free_zm_word);
573
574		alg_xm_last = be32_to_cpu(alg_info->xm) + algs->xm_size_words - 1;
575		if (alg_xm_last > next_free_xm_word)
576			next_free_xm_word = alg_xm_last;
577
578		alg_ym_last = be32_to_cpu(alg_info->ym) + algs->ym_size_words - 1;
579		if (alg_ym_last > next_free_ym_word)
580			next_free_ym_word = alg_ym_last;
581
582		alg_zm_last = be32_to_cpu(alg_info->zm) + algs->zm_size_words - 1;
583		if (alg_zm_last > next_free_zm_word)
584			next_free_zm_word = alg_zm_last;
585	}
586
587	/* Write list terminator */
588	*(__be32 *)(alg_info) = cpu_to_be32(0xbedead);
589}
590
591static void cs_dsp_mock_xm_header_add_halo_algs(struct cs_dsp_mock_xm_header *builder,
592						const struct cs_dsp_mock_alg_def *algs,
593						size_t num_algs)
594{
595	struct wmfw_halo_id_hdr *hdr = builder->blob_data;
596	unsigned int next_free_xm_word, next_free_ym_word;
597
598	/* Assume we're starting with bare header */
599	next_free_xm_word = be32_to_cpu(hdr->xm_base) + be32_to_cpu(hdr->xm_size) - 1;
600	next_free_ym_word = be32_to_cpu(hdr->ym_base) + be32_to_cpu(hdr->ym_size) - 1;
601
602	/* Set num_algs in XM header */
603	hdr->n_algs = cpu_to_be32(num_algs);
604
605	/* Create algorithm descriptor list */
606	struct wmfw_halo_alg_hdr *alg_info =
607			(struct wmfw_halo_alg_hdr *)(&hdr[1]);
608
609	for (; num_algs > 0; num_algs--, algs++, alg_info++) {
610		unsigned int alg_xm_last, alg_ym_last;
611
612		alg_info->alg.id = cpu_to_be32(algs->id);
613		alg_info->alg.ver = cpu_to_be32(algs->ver);
614		alg_info->xm_base = cpu_to_be32(algs->xm_base_words);
615		alg_info->xm_size = cpu_to_be32(algs->xm_size_words);
616		alg_info->ym_base = cpu_to_be32(algs->ym_base_words);
617		alg_info->ym_size = cpu_to_be32(algs->ym_size_words);
618
619		/* Check if we need to auto-allocate base addresses */
620		if (!alg_info->xm_base && alg_info->xm_size)
621			alg_info->xm_base = cpu_to_be32(next_free_xm_word);
622
623		if (!alg_info->ym_base && alg_info->ym_size)
624			alg_info->ym_base = cpu_to_be32(next_free_ym_word);
625
626		alg_xm_last = be32_to_cpu(alg_info->xm_base) + be32_to_cpu(alg_info->xm_size) - 1;
627		if (alg_xm_last > next_free_xm_word)
628			next_free_xm_word = alg_xm_last;
629
630		alg_ym_last = be32_to_cpu(alg_info->ym_base) + be32_to_cpu(alg_info->ym_size) - 1;
631		if (alg_ym_last > next_free_ym_word)
632			next_free_ym_word = alg_ym_last;
633	}
634
635	/* Write list terminator */
636	*(__be32 *)(alg_info) = cpu_to_be32(0xbedead);
637}
638
639/**
640 * cs_dsp_mock_xm_header_write_to_regmap() - Write XM header to regmap.
641 *
642 * @header:	Pointer to struct cs_dsp_mock_xm_header.
643 *
644 * The data in header is written to the XM addresses in the regmap.
645 *
646 * Return: 0 on success, else negative error code.
647 */
648int cs_dsp_mock_xm_header_write_to_regmap(struct cs_dsp_mock_xm_header *header)
649{
650	struct cs_dsp_test *priv = header->test_priv;
651	unsigned int reg_addr = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_XM);
652
653	/*
654	 * One 32-bit word corresponds to one 32-bit unpacked XM word so the
655	 * blob can be written directly to the regmap.
656	 */
657	return regmap_raw_write(priv->dsp->regmap, reg_addr,
658				header->blob_data, header->blob_size_bytes);
659}
660EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_write_to_regmap, "FW_CS_DSP_KUNIT_TEST_UTILS");
661
662/**
663 * cs_dsp_create_mock_xm_header() - Create a dummy XM header.
664 *
665 * @priv:	Pointer to struct cs_dsp_test.
666 * @algs:	Pointer to array of struct cs_dsp_mock_alg_def listing the
667 *		dummy algorithm entries to include in the XM header.
668 * @num_algs:	Number of entries in the algs array.
669 *
670 * Return: Pointer to created struct cs_dsp_mock_xm_header.
671 */
672struct cs_dsp_mock_xm_header *cs_dsp_create_mock_xm_header(struct cs_dsp_test *priv,
673							   const struct cs_dsp_mock_alg_def *algs,
674							   size_t num_algs)
675{
676	struct cs_dsp_mock_xm_header *builder;
677	size_t total_bytes_required;
678	const void *header;
679	size_t header_size_bytes;
680
681	builder = kunit_kzalloc(priv->test, sizeof(*builder), GFP_KERNEL);
682	KUNIT_ASSERT_NOT_ERR_OR_NULL(priv->test, builder);
683	builder->test_priv = priv;
684
685	switch (priv->dsp->type) {
686	case WMFW_ADSP2:
687		header = &cs_dsp_mock_adsp2_xm_hdr;
688		header_size_bytes = sizeof(cs_dsp_mock_adsp2_xm_hdr);
689		total_bytes_required = header_size_bytes +
690				       (num_algs * sizeof(struct wmfw_adsp2_alg_hdr))
691				       + 4; /* terminator word */
692		break;
693	case WMFW_HALO:
694		header = &cs_dsp_mock_halo_xm_hdr,
695		header_size_bytes = sizeof(cs_dsp_mock_halo_xm_hdr);
696		total_bytes_required = header_size_bytes +
697				       (num_algs * sizeof(struct wmfw_halo_alg_hdr))
698				       + 4; /* terminator word */
699		break;
700	default:
701		KUNIT_FAIL(priv->test, "%s unexpected DSP type %d\n",
702			   __func__, priv->dsp->type);
703		return NULL;
704	}
705
706	builder->blob_data = kunit_kzalloc(priv->test, total_bytes_required, GFP_KERNEL);
707	KUNIT_ASSERT_NOT_ERR_OR_NULL(priv->test, builder->blob_data);
708	builder->blob_size_bytes = total_bytes_required;
709
710	memcpy(builder->blob_data, header, header_size_bytes);
711
712	switch (priv->dsp->type) {
713	case WMFW_ADSP2:
714		cs_dsp_mock_xm_header_add_adsp2_algs(builder, algs, num_algs);
715		break;
716	case WMFW_HALO:
717		cs_dsp_mock_xm_header_add_halo_algs(builder, algs, num_algs);
718		break;
719	default:
720		break;
721	}
722
723	return builder;
724}
725EXPORT_SYMBOL_NS_GPL(cs_dsp_create_mock_xm_header, "FW_CS_DSP_KUNIT_TEST_UTILS");
~/ Read-only file preview.