xref: /linux/sound/soc/codecs/cs-amp-lib-test.c (revision ae22a94997b8a03dcb3c922857c203246711f9d4)
1 // SPDX-License-Identifier: GPL-2.0-only
2 //
3 // KUnit test for the Cirrus common amplifier library.
4 //
5 // Copyright (C) 2024 Cirrus Logic, Inc. and
6 //                    Cirrus Logic International Semiconductor Ltd.
7 
8 #include <kunit/test.h>
9 #include <kunit/static_stub.h>
10 #include <linux/firmware/cirrus/cs_dsp.h>
11 #include <linux/firmware/cirrus/wmfw.h>
12 #include <linux/gpio/driver.h>
13 #include <linux/list.h>
14 #include <linux/module.h>
15 #include <linux/platform_device.h>
16 #include <linux/random.h>
17 #include <sound/cs-amp-lib.h>
18 
19 struct cs_amp_lib_test_priv {
20 	struct platform_device amp_pdev;
21 
22 	struct cirrus_amp_efi_data *cal_blob;
23 	struct list_head ctl_write_list;
24 };
25 
26 struct cs_amp_lib_test_ctl_write_entry {
27 	struct list_head list;
28 	unsigned int value;
29 	char name[16];
30 };
31 
32 struct cs_amp_lib_test_param {
33 	int num_amps;
34 	int amp_index;
35 };
36 
37 static void cs_amp_lib_test_init_dummy_cal_blob(struct kunit *test, int num_amps)
38 {
39 	struct cs_amp_lib_test_priv *priv = test->priv;
40 	unsigned int blob_size;
41 
42 	blob_size = offsetof(struct cirrus_amp_efi_data, data) +
43 		    sizeof(struct cirrus_amp_cal_data) * num_amps;
44 
45 	priv->cal_blob = kunit_kzalloc(test, blob_size, GFP_KERNEL);
46 	KUNIT_ASSERT_NOT_NULL(test, priv->cal_blob);
47 
48 	priv->cal_blob->size = blob_size;
49 	priv->cal_blob->count = num_amps;
50 
51 	get_random_bytes(priv->cal_blob->data, sizeof(struct cirrus_amp_cal_data) * num_amps);
52 }
53 
54 static u64 cs_amp_lib_test_get_target_uid(struct kunit *test)
55 {
56 	struct cs_amp_lib_test_priv *priv = test->priv;
57 	const struct cs_amp_lib_test_param *param = test->param_value;
58 	u64 uid;
59 
60 	uid = priv->cal_blob->data[param->amp_index].calTarget[1];
61 	uid <<= 32;
62 	uid |= priv->cal_blob->data[param->amp_index].calTarget[0];
63 
64 	return uid;
65 }
66 
67 /* Redirected get_efi_variable to simulate that the file is too short */
68 static efi_status_t cs_amp_lib_test_get_efi_variable_nohead(efi_char16_t *name,
69 							    efi_guid_t *guid,
70 							    unsigned long *size,
71 							    void *buf)
72 {
73 	if (!buf) {
74 		*size = offsetof(struct cirrus_amp_efi_data, data) - 1;
75 		return EFI_BUFFER_TOO_SMALL;
76 	}
77 
78 	return EFI_NOT_FOUND;
79 }
80 
81 /* Should return -EOVERFLOW if the header is larger than the EFI data */
82 static void cs_amp_lib_test_cal_data_too_short_test(struct kunit *test)
83 {
84 	struct cs_amp_lib_test_priv *priv = test->priv;
85 	struct cirrus_amp_cal_data result_data;
86 	int ret;
87 
88 	/* Redirect calls to get EFI data */
89 	kunit_activate_static_stub(test,
90 				   cs_amp_test_hooks->get_efi_variable,
91 				   cs_amp_lib_test_get_efi_variable_nohead);
92 
93 	ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 0, &result_data);
94 	KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW);
95 
96 	kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
97 }
98 
99 /* Redirected get_efi_variable to simulate that the count is larger than the file */
100 static efi_status_t cs_amp_lib_test_get_efi_variable_bad_count(efi_char16_t *name,
101 							       efi_guid_t *guid,
102 							       unsigned long *size,
103 							       void *buf)
104 {
105 	struct kunit *test = kunit_get_current_test();
106 	struct cs_amp_lib_test_priv *priv = test->priv;
107 
108 	if (!buf) {
109 		/*
110 		 * Return a size that is shorter than required for the
111 		 * declared number of entries.
112 		 */
113 		*size = priv->cal_blob->size - 1;
114 		return EFI_BUFFER_TOO_SMALL;
115 	}
116 
117 	memcpy(buf, priv->cal_blob, priv->cal_blob->size - 1);
118 
119 	return EFI_SUCCESS;
120 }
121 
122 /* Should return -EOVERFLOW if the entry count is larger than the EFI data */
123 static void cs_amp_lib_test_cal_count_too_big_test(struct kunit *test)
124 {
125 	struct cs_amp_lib_test_priv *priv = test->priv;
126 	struct cirrus_amp_cal_data result_data;
127 	int ret;
128 
129 	cs_amp_lib_test_init_dummy_cal_blob(test, 8);
130 
131 	/* Redirect calls to get EFI data */
132 	kunit_activate_static_stub(test,
133 				   cs_amp_test_hooks->get_efi_variable,
134 				   cs_amp_lib_test_get_efi_variable_bad_count);
135 
136 	ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 0, &result_data);
137 	KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW);
138 
139 	kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
140 }
141 
142 /* Redirected get_efi_variable to simulate that the variable not found */
143 static efi_status_t cs_amp_lib_test_get_efi_variable_none(efi_char16_t *name,
144 							  efi_guid_t *guid,
145 							  unsigned long *size,
146 							  void *buf)
147 {
148 	return EFI_NOT_FOUND;
149 }
150 
151 /* If EFI doesn't contain a cal data variable the result should be -ENOENT */
152 static void cs_amp_lib_test_no_cal_data_test(struct kunit *test)
153 {
154 	struct cs_amp_lib_test_priv *priv = test->priv;
155 	struct cirrus_amp_cal_data result_data;
156 	int ret;
157 
158 	/* Redirect calls to get EFI data */
159 	kunit_activate_static_stub(test,
160 				   cs_amp_test_hooks->get_efi_variable,
161 				   cs_amp_lib_test_get_efi_variable_none);
162 
163 	ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 0, &result_data);
164 	KUNIT_EXPECT_EQ(test, ret, -ENOENT);
165 
166 	kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
167 }
168 
169 /* Redirected get_efi_variable to simulate reading a cal data blob */
170 static efi_status_t cs_amp_lib_test_get_efi_variable(efi_char16_t *name,
171 						     efi_guid_t *guid,
172 						     unsigned long *size,
173 						     void *buf)
174 {
175 	static const efi_char16_t expected_name[] = L"CirrusSmartAmpCalibrationData";
176 	static const efi_guid_t expected_guid =
177 		EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3);
178 	struct kunit *test = kunit_get_current_test();
179 	struct cs_amp_lib_test_priv *priv = test->priv;
180 
181 	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, name);
182 	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, guid);
183 	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, size);
184 
185 	KUNIT_EXPECT_MEMEQ(test, name, expected_name, sizeof(expected_name));
186 	KUNIT_EXPECT_MEMEQ(test, guid, &expected_guid, sizeof(expected_guid));
187 
188 	if (!buf) {
189 		*size = priv->cal_blob->size;
190 		return EFI_BUFFER_TOO_SMALL;
191 	}
192 
193 	KUNIT_ASSERT_GE_MSG(test, ksize(buf), priv->cal_blob->size, "Buffer to small");
194 
195 	memcpy(buf, priv->cal_blob, priv->cal_blob->size);
196 
197 	return EFI_SUCCESS;
198 }
199 
200 /* Get cal data block for a given amp, matched by target UID. */
201 static void cs_amp_lib_test_get_efi_cal_by_uid_test(struct kunit *test)
202 {
203 	struct cs_amp_lib_test_priv *priv = test->priv;
204 	const struct cs_amp_lib_test_param *param = test->param_value;
205 	struct cirrus_amp_cal_data result_data;
206 	u64 target_uid;
207 	int ret;
208 
209 	cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
210 
211 	/* Redirect calls to get EFI data */
212 	kunit_activate_static_stub(test,
213 				   cs_amp_test_hooks->get_efi_variable,
214 				   cs_amp_lib_test_get_efi_variable);
215 
216 	target_uid = cs_amp_lib_test_get_target_uid(test);
217 	ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, target_uid, -1, &result_data);
218 	KUNIT_EXPECT_EQ(test, ret, 0);
219 
220 	kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
221 
222 	KUNIT_EXPECT_EQ(test, result_data.calTarget[0], target_uid & 0xFFFFFFFFULL);
223 	KUNIT_EXPECT_EQ(test, result_data.calTarget[1], target_uid >> 32);
224 	KUNIT_EXPECT_EQ(test, result_data.calTime[0],
225 			      priv->cal_blob->data[param->amp_index].calTime[0]);
226 	KUNIT_EXPECT_EQ(test, result_data.calTime[1],
227 			      priv->cal_blob->data[param->amp_index].calTime[1]);
228 	KUNIT_EXPECT_EQ(test, result_data.calAmbient,
229 			      priv->cal_blob->data[param->amp_index].calAmbient);
230 	KUNIT_EXPECT_EQ(test, result_data.calStatus,
231 			      priv->cal_blob->data[param->amp_index].calStatus);
232 	KUNIT_EXPECT_EQ(test, result_data.calR,
233 			      priv->cal_blob->data[param->amp_index].calR);
234 }
235 
236 /* Get cal data block for a given amp index without checking target UID. */
237 static void cs_amp_lib_test_get_efi_cal_by_index_unchecked_test(struct kunit *test)
238 {
239 	struct cs_amp_lib_test_priv *priv = test->priv;
240 	const struct cs_amp_lib_test_param *param = test->param_value;
241 	struct cirrus_amp_cal_data result_data;
242 	int ret;
243 
244 	cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
245 
246 	/* Redirect calls to get EFI data */
247 	kunit_activate_static_stub(test,
248 				   cs_amp_test_hooks->get_efi_variable,
249 				   cs_amp_lib_test_get_efi_variable);
250 
251 	ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0,
252 					      param->amp_index, &result_data);
253 	KUNIT_EXPECT_EQ(test, ret, 0);
254 
255 	kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
256 
257 	KUNIT_EXPECT_EQ(test, result_data.calTime[0],
258 			      priv->cal_blob->data[param->amp_index].calTime[0]);
259 	KUNIT_EXPECT_EQ(test, result_data.calTime[1],
260 			      priv->cal_blob->data[param->amp_index].calTime[1]);
261 	KUNIT_EXPECT_EQ(test, result_data.calAmbient,
262 			      priv->cal_blob->data[param->amp_index].calAmbient);
263 	KUNIT_EXPECT_EQ(test, result_data.calStatus,
264 			      priv->cal_blob->data[param->amp_index].calStatus);
265 	KUNIT_EXPECT_EQ(test, result_data.calR,
266 			      priv->cal_blob->data[param->amp_index].calR);
267 }
268 
269 /* Get cal data block for a given amp index with checked target UID. */
270 static void cs_amp_lib_test_get_efi_cal_by_index_checked_test(struct kunit *test)
271 {
272 	struct cs_amp_lib_test_priv *priv = test->priv;
273 	const struct cs_amp_lib_test_param *param = test->param_value;
274 	struct cirrus_amp_cal_data result_data;
275 	u64 target_uid;
276 	int ret;
277 
278 	cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
279 
280 	/* Redirect calls to get EFI data */
281 	kunit_activate_static_stub(test,
282 				   cs_amp_test_hooks->get_efi_variable,
283 				   cs_amp_lib_test_get_efi_variable);
284 
285 	target_uid = cs_amp_lib_test_get_target_uid(test);
286 	ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, target_uid,
287 					      param->amp_index, &result_data);
288 	KUNIT_EXPECT_EQ(test, ret, 0);
289 
290 	kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
291 
292 	KUNIT_EXPECT_EQ(test, result_data.calTime[0],
293 			      priv->cal_blob->data[param->amp_index].calTime[0]);
294 	KUNIT_EXPECT_EQ(test, result_data.calTime[1],
295 			      priv->cal_blob->data[param->amp_index].calTime[1]);
296 	KUNIT_EXPECT_EQ(test, result_data.calAmbient,
297 			      priv->cal_blob->data[param->amp_index].calAmbient);
298 	KUNIT_EXPECT_EQ(test, result_data.calStatus,
299 			      priv->cal_blob->data[param->amp_index].calStatus);
300 	KUNIT_EXPECT_EQ(test, result_data.calR,
301 			      priv->cal_blob->data[param->amp_index].calR);
302 }
303 
304 /*
305  * Get cal data block for a given amp index with checked target UID.
306  * The UID does not match so the result should be -ENOENT.
307  */
308 static void cs_amp_lib_test_get_efi_cal_by_index_uid_mismatch_test(struct kunit *test)
309 {
310 	struct cs_amp_lib_test_priv *priv = test->priv;
311 	const struct cs_amp_lib_test_param *param = test->param_value;
312 	struct cirrus_amp_cal_data result_data;
313 	u64 target_uid;
314 	int ret;
315 
316 	cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
317 
318 	/* Redirect calls to get EFI data */
319 	kunit_activate_static_stub(test,
320 				   cs_amp_test_hooks->get_efi_variable,
321 				   cs_amp_lib_test_get_efi_variable);
322 
323 	/* Get a target UID that won't match the entry */
324 	target_uid = ~cs_amp_lib_test_get_target_uid(test);
325 	ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, target_uid,
326 					      param->amp_index, &result_data);
327 	KUNIT_EXPECT_EQ(test, ret, -ENOENT);
328 
329 	kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
330 }
331 
332 /*
333  * Get cal data block for a given amp, where the cal data does not
334  * specify calTarget so the lookup falls back to using the index
335  */
336 static void cs_amp_lib_test_get_efi_cal_by_index_fallback_test(struct kunit *test)
337 {
338 	struct cs_amp_lib_test_priv *priv = test->priv;
339 	const struct cs_amp_lib_test_param *param = test->param_value;
340 	struct cirrus_amp_cal_data result_data;
341 	static const u64 bad_target_uid = 0xBADCA100BABABABAULL;
342 	int i, ret;
343 
344 	cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
345 
346 	/* Make all the target values zero so they are ignored */
347 	for (i = 0; i < priv->cal_blob->count; ++i) {
348 		priv->cal_blob->data[i].calTarget[0] = 0;
349 		priv->cal_blob->data[i].calTarget[1] = 0;
350 	}
351 
352 	/* Redirect calls to get EFI data */
353 	kunit_activate_static_stub(test,
354 				   cs_amp_test_hooks->get_efi_variable,
355 				   cs_amp_lib_test_get_efi_variable);
356 
357 	ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, bad_target_uid,
358 					      param->amp_index, &result_data);
359 	KUNIT_EXPECT_EQ(test, ret, 0);
360 
361 	kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
362 
363 	KUNIT_EXPECT_EQ(test, result_data.calTime[0],
364 			      priv->cal_blob->data[param->amp_index].calTime[0]);
365 	KUNIT_EXPECT_EQ(test, result_data.calTime[1],
366 			      priv->cal_blob->data[param->amp_index].calTime[1]);
367 	KUNIT_EXPECT_EQ(test, result_data.calAmbient,
368 			      priv->cal_blob->data[param->amp_index].calAmbient);
369 	KUNIT_EXPECT_EQ(test, result_data.calStatus,
370 			      priv->cal_blob->data[param->amp_index].calStatus);
371 	KUNIT_EXPECT_EQ(test, result_data.calR,
372 			      priv->cal_blob->data[param->amp_index].calR);
373 }
374 
375 /*
376  * If the target UID isn't present in the cal data, and there isn't an
377  * index to fall back do, the result should be -ENOENT.
378  */
379 static void cs_amp_lib_test_get_efi_cal_uid_not_found_noindex_test(struct kunit *test)
380 {
381 	struct cs_amp_lib_test_priv *priv = test->priv;
382 	struct cirrus_amp_cal_data result_data;
383 	static const u64 bad_target_uid = 0xBADCA100BABABABAULL;
384 	int i, ret;
385 
386 	cs_amp_lib_test_init_dummy_cal_blob(test, 8);
387 
388 	/* Make all the target values != bad_target_uid */
389 	for (i = 0; i < priv->cal_blob->count; ++i) {
390 		priv->cal_blob->data[i].calTarget[0] &= ~(bad_target_uid & 0xFFFFFFFFULL);
391 		priv->cal_blob->data[i].calTarget[1] &= ~(bad_target_uid >> 32);
392 	}
393 
394 	/* Redirect calls to get EFI data */
395 	kunit_activate_static_stub(test,
396 				   cs_amp_test_hooks->get_efi_variable,
397 				   cs_amp_lib_test_get_efi_variable);
398 
399 	ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, bad_target_uid, -1,
400 					      &result_data);
401 	KUNIT_EXPECT_EQ(test, ret, -ENOENT);
402 
403 	kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
404 }
405 
406 /*
407  * If the target UID isn't present in the cal data, and the index is
408  * out of range, the result should be -ENOENT.
409  */
410 static void cs_amp_lib_test_get_efi_cal_uid_not_found_index_not_found_test(struct kunit *test)
411 {
412 	struct cs_amp_lib_test_priv *priv = test->priv;
413 	struct cirrus_amp_cal_data result_data;
414 	static const u64 bad_target_uid = 0xBADCA100BABABABAULL;
415 	int i, ret;
416 
417 	cs_amp_lib_test_init_dummy_cal_blob(test, 8);
418 
419 	/* Make all the target values != bad_target_uid */
420 	for (i = 0; i < priv->cal_blob->count; ++i) {
421 		priv->cal_blob->data[i].calTarget[0] &= ~(bad_target_uid & 0xFFFFFFFFULL);
422 		priv->cal_blob->data[i].calTarget[1] &= ~(bad_target_uid >> 32);
423 	}
424 
425 	/* Redirect calls to get EFI data */
426 	kunit_activate_static_stub(test,
427 				   cs_amp_test_hooks->get_efi_variable,
428 				   cs_amp_lib_test_get_efi_variable);
429 
430 	ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, bad_target_uid, 99,
431 					      &result_data);
432 	KUNIT_EXPECT_EQ(test, ret, -ENOENT);
433 
434 	kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
435 }
436 
437 /*
438  * If the target UID isn't given, and the index is out of range, the
439  * result should be -ENOENT.
440  */
441 static void cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test(struct kunit *test)
442 {
443 	struct cs_amp_lib_test_priv *priv = test->priv;
444 	struct cirrus_amp_cal_data result_data;
445 	int ret;
446 
447 	cs_amp_lib_test_init_dummy_cal_blob(test, 8);
448 
449 	/* Redirect calls to get EFI data */
450 	kunit_activate_static_stub(test,
451 				   cs_amp_test_hooks->get_efi_variable,
452 				   cs_amp_lib_test_get_efi_variable);
453 
454 	ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 99, &result_data);
455 	KUNIT_EXPECT_EQ(test, ret, -ENOENT);
456 
457 	kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
458 }
459 
460 /* If neither the target UID or the index is given the result should be -ENOENT. */
461 static void cs_amp_lib_test_get_efi_cal_no_uid_no_index_test(struct kunit *test)
462 {
463 	struct cs_amp_lib_test_priv *priv = test->priv;
464 	struct cirrus_amp_cal_data result_data;
465 	int ret;
466 
467 	cs_amp_lib_test_init_dummy_cal_blob(test, 8);
468 
469 	/* Redirect calls to get EFI data */
470 	kunit_activate_static_stub(test,
471 				   cs_amp_test_hooks->get_efi_variable,
472 				   cs_amp_lib_test_get_efi_variable);
473 
474 	ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, -1, &result_data);
475 	KUNIT_EXPECT_EQ(test, ret, -ENOENT);
476 
477 	kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
478 }
479 
480 /*
481  * If the UID is passed as 0 this must not match an entry with an
482  * unpopulated calTarget
483  */
484 static void cs_amp_lib_test_get_efi_cal_zero_not_matched_test(struct kunit *test)
485 {
486 	struct cs_amp_lib_test_priv *priv = test->priv;
487 	struct cirrus_amp_cal_data result_data;
488 	int i, ret;
489 
490 	cs_amp_lib_test_init_dummy_cal_blob(test, 8);
491 
492 	/* Make all the target values zero so they are ignored */
493 	for (i = 0; i < priv->cal_blob->count; ++i) {
494 		priv->cal_blob->data[i].calTarget[0] = 0;
495 		priv->cal_blob->data[i].calTarget[1] = 0;
496 	}
497 
498 	/* Redirect calls to get EFI data */
499 	kunit_activate_static_stub(test,
500 				   cs_amp_test_hooks->get_efi_variable,
501 				   cs_amp_lib_test_get_efi_variable);
502 
503 	ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, -1, &result_data);
504 	KUNIT_EXPECT_EQ(test, ret, -ENOENT);
505 
506 	kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
507 }
508 
509 static const struct cirrus_amp_cal_controls cs_amp_lib_test_calibration_controls = {
510 	.alg_id =	0x9f210,
511 	.mem_region =	WMFW_ADSP2_YM,
512 	.ambient =	"CAL_AMBIENT",
513 	.calr =		"CAL_R",
514 	.status =	"CAL_STATUS",
515 	.checksum =	"CAL_CHECKSUM",
516 };
517 
518 static int cs_amp_lib_test_write_cal_coeff(struct cs_dsp *dsp,
519 					   const struct cirrus_amp_cal_controls *controls,
520 					   const char *ctl_name, u32 val)
521 {
522 	struct kunit *test = kunit_get_current_test();
523 	struct cs_amp_lib_test_priv *priv = test->priv;
524 	struct cs_amp_lib_test_ctl_write_entry *entry;
525 
526 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctl_name);
527 	KUNIT_EXPECT_PTR_EQ(test, controls, &cs_amp_lib_test_calibration_controls);
528 
529 	entry = kunit_kzalloc(test, sizeof(*entry), GFP_KERNEL);
530 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, entry);
531 
532 	INIT_LIST_HEAD(&entry->list);
533 	strscpy(entry->name, ctl_name, sizeof(entry->name));
534 	entry->value = val;
535 
536 	list_add_tail(&entry->list, &priv->ctl_write_list);
537 
538 	return 0;
539 }
540 
541 static void cs_amp_lib_test_write_cal_data_test(struct kunit *test)
542 {
543 	struct cs_amp_lib_test_priv *priv = test->priv;
544 	struct cs_amp_lib_test_ctl_write_entry *entry;
545 	struct cirrus_amp_cal_data data;
546 	struct cs_dsp *dsp;
547 	int ret;
548 
549 	dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
550 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dsp);
551 	dsp->dev = &priv->amp_pdev.dev;
552 
553 	get_random_bytes(&data, sizeof(data));
554 
555 	/* Redirect calls to write firmware controls */
556 	kunit_activate_static_stub(test,
557 				   cs_amp_test_hooks->write_cal_coeff,
558 				   cs_amp_lib_test_write_cal_coeff);
559 
560 	ret = cs_amp_write_cal_coeffs(dsp, &cs_amp_lib_test_calibration_controls, &data);
561 	KUNIT_EXPECT_EQ(test, ret, 0);
562 
563 	kunit_deactivate_static_stub(test, cs_amp_test_hooks->write_cal_coeff);
564 
565 	KUNIT_EXPECT_EQ(test, list_count_nodes(&priv->ctl_write_list), 4);
566 
567 	/* Checksum control must be written last */
568 	entry = list_last_entry(&priv->ctl_write_list, typeof(*entry), list);
569 	KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.checksum);
570 	KUNIT_EXPECT_EQ(test, entry->value, data.calR + 1);
571 	list_del(&entry->list);
572 
573 	entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list);
574 	KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.ambient);
575 	KUNIT_EXPECT_EQ(test, entry->value, data.calAmbient);
576 	list_del(&entry->list);
577 
578 	entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list);
579 	KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.calr);
580 	KUNIT_EXPECT_EQ(test, entry->value, data.calR);
581 	list_del(&entry->list);
582 
583 	entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list);
584 	KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.status);
585 	KUNIT_EXPECT_EQ(test, entry->value, data.calStatus);
586 }
587 
588 static void cs_amp_lib_test_dev_release(struct device *dev)
589 {
590 }
591 
592 static int cs_amp_lib_test_case_init(struct kunit *test)
593 {
594 	struct cs_amp_lib_test_priv *priv;
595 	int ret;
596 
597 	KUNIT_ASSERT_NOT_NULL(test, cs_amp_test_hooks);
598 
599 	priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
600 	if (!priv)
601 		return -ENOMEM;
602 
603 	test->priv = priv;
604 	INIT_LIST_HEAD(&priv->ctl_write_list);
605 
606 	/* Create dummy amp driver dev */
607 	priv->amp_pdev.name = "cs_amp_lib_test_drv";
608 	priv->amp_pdev.id = -1;
609 	priv->amp_pdev.dev.release = cs_amp_lib_test_dev_release;
610 	ret = platform_device_register(&priv->amp_pdev);
611 	KUNIT_ASSERT_GE_MSG(test, ret, 0, "Failed to register amp platform device\n");
612 
613 	return 0;
614 }
615 
616 static void cs_amp_lib_test_case_exit(struct kunit *test)
617 {
618 	struct cs_amp_lib_test_priv *priv = test->priv;
619 
620 	if (priv->amp_pdev.name)
621 		platform_device_unregister(&priv->amp_pdev);
622 }
623 
624 static const struct cs_amp_lib_test_param cs_amp_lib_test_get_cal_param_cases[] = {
625 	{ .num_amps = 2, .amp_index = 0 },
626 	{ .num_amps = 2, .amp_index = 1 },
627 
628 	{ .num_amps = 3, .amp_index = 0 },
629 	{ .num_amps = 3, .amp_index = 1 },
630 	{ .num_amps = 3, .amp_index = 2 },
631 
632 	{ .num_amps = 4, .amp_index = 0 },
633 	{ .num_amps = 4, .amp_index = 1 },
634 	{ .num_amps = 4, .amp_index = 2 },
635 	{ .num_amps = 4, .amp_index = 3 },
636 
637 	{ .num_amps = 5, .amp_index = 0 },
638 	{ .num_amps = 5, .amp_index = 1 },
639 	{ .num_amps = 5, .amp_index = 2 },
640 	{ .num_amps = 5, .amp_index = 3 },
641 	{ .num_amps = 5, .amp_index = 4 },
642 
643 	{ .num_amps = 6, .amp_index = 0 },
644 	{ .num_amps = 6, .amp_index = 1 },
645 	{ .num_amps = 6, .amp_index = 2 },
646 	{ .num_amps = 6, .amp_index = 3 },
647 	{ .num_amps = 6, .amp_index = 4 },
648 	{ .num_amps = 6, .amp_index = 5 },
649 
650 	{ .num_amps = 8, .amp_index = 0 },
651 	{ .num_amps = 8, .amp_index = 1 },
652 	{ .num_amps = 8, .amp_index = 2 },
653 	{ .num_amps = 8, .amp_index = 3 },
654 	{ .num_amps = 8, .amp_index = 4 },
655 	{ .num_amps = 8, .amp_index = 5 },
656 	{ .num_amps = 8, .amp_index = 6 },
657 	{ .num_amps = 8, .amp_index = 7 },
658 };
659 
660 static void cs_amp_lib_test_get_cal_param_desc(const struct cs_amp_lib_test_param *param,
661 					       char *desc)
662 {
663 	snprintf(desc, KUNIT_PARAM_DESC_SIZE, "num_amps:%d amp_index:%d",
664 		 param->num_amps, param->amp_index);
665 }
666 
667 KUNIT_ARRAY_PARAM(cs_amp_lib_test_get_cal, cs_amp_lib_test_get_cal_param_cases,
668 		  cs_amp_lib_test_get_cal_param_desc);
669 
670 static struct kunit_case cs_amp_lib_test_cases[] = {
671 	/* Tests for getting calibration data from EFI */
672 	KUNIT_CASE(cs_amp_lib_test_cal_data_too_short_test),
673 	KUNIT_CASE(cs_amp_lib_test_cal_count_too_big_test),
674 	KUNIT_CASE(cs_amp_lib_test_no_cal_data_test),
675 	KUNIT_CASE(cs_amp_lib_test_get_efi_cal_uid_not_found_noindex_test),
676 	KUNIT_CASE(cs_amp_lib_test_get_efi_cal_uid_not_found_index_not_found_test),
677 	KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test),
678 	KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_no_index_test),
679 	KUNIT_CASE(cs_amp_lib_test_get_efi_cal_zero_not_matched_test),
680 	KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_uid_test,
681 			 cs_amp_lib_test_get_cal_gen_params),
682 	KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_unchecked_test,
683 			 cs_amp_lib_test_get_cal_gen_params),
684 	KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_checked_test,
685 			 cs_amp_lib_test_get_cal_gen_params),
686 	KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_uid_mismatch_test,
687 			 cs_amp_lib_test_get_cal_gen_params),
688 	KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_fallback_test,
689 			 cs_amp_lib_test_get_cal_gen_params),
690 
691 	/* Tests for writing calibration data */
692 	KUNIT_CASE(cs_amp_lib_test_write_cal_data_test),
693 
694 	{ } /* terminator */
695 };
696 
697 static struct kunit_suite cs_amp_lib_test_suite = {
698 	.name = "snd-soc-cs-amp-lib-test",
699 	.init = cs_amp_lib_test_case_init,
700 	.exit = cs_amp_lib_test_case_exit,
701 	.test_cases = cs_amp_lib_test_cases,
702 };
703 
704 kunit_test_suite(cs_amp_lib_test_suite);
705 
706 MODULE_IMPORT_NS(SND_SOC_CS_AMP_LIB);
707 MODULE_DESCRIPTION("KUnit test for Cirrus Logic amplifier library");
708 MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
709 MODULE_LICENSE("GPL");
710