Skip to content

Commit 6f22044

Browse files
rfvirgilbroonie
authored andcommitted
ASoC: cs35l56: KUnit tests for parsing and using onchip GPIOs
Add KUnit test cases for: - cs35l56_process_xu_properties() which reads the onchip GPIO definitions. - cs35l56_set_fw_name() calls functions to configure and set the GPIOs. - cs35l56_set_fw_name() saves the ID value in cs35l56_priv.speaker_id. - cs35l56_set_fw_name() does not overwrite a speaker ID that was already found some other way. Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com> Link: https://patch.msgid.link/20260205164838.1611295-4-rf@opensource.cirrus.com Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent 9bca0f0 commit 6f22044

1 file changed

Lines changed: 273 additions & 0 deletions

File tree

sound/soc/codecs/cs35l56-test.c

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include <linux/module.h>
1616
#include <linux/overflow.h>
1717
#include <linux/pci_ids.h>
18+
#include <linux/property.h>
19+
#include <linux/seq_buf.h>
1820
#include <linux/soundwire/sdw.h>
1921
#include <sound/cs35l56.h>
2022
#include <sound/cs-amp-lib.h>
@@ -23,16 +25,46 @@
2325
KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy,
2426
struct faux_device *)
2527

28+
KUNIT_DEFINE_ACTION_WRAPPER(software_node_unregister_node_group_wrapper,
29+
software_node_unregister_node_group,
30+
const struct software_node * const *)
31+
32+
KUNIT_DEFINE_ACTION_WRAPPER(software_node_unregister_wrapper,
33+
software_node_unregister,
34+
const struct software_node *)
35+
36+
KUNIT_DEFINE_ACTION_WRAPPER(device_remove_software_node_wrapper,
37+
device_remove_software_node,
38+
struct device *)
39+
2640
struct cs35l56_test_priv {
2741
struct faux_device *amp_dev;
2842
struct cs35l56_private *cs35l56_priv;
2943

3044
const char *ssidexv2;
45+
46+
bool read_onchip_spkid_called;
47+
bool configure_onchip_spkid_pads_called;
3148
};
3249

3350
struct cs35l56_test_param {
3451
u8 type;
3552
u8 rev;
53+
54+
s32 spkid_gpios[4];
55+
s32 spkid_pulls[4];
56+
};
57+
58+
static const struct software_node cs35l56_test_dev_sw_node =
59+
SOFTWARE_NODE("SWD1", NULL, NULL);
60+
61+
static const struct software_node cs35l56_test_af01_sw_node =
62+
SOFTWARE_NODE("AF01", NULL, &cs35l56_test_dev_sw_node);
63+
64+
static const struct software_node *cs35l56_test_dev_and_af01_node_group[] = {
65+
&cs35l56_test_dev_sw_node,
66+
&cs35l56_test_af01_sw_node,
67+
NULL
3668
};
3769

3870
static const char *cs35l56_test_devm_get_vendor_specific_variant_id_none(struct device *dev,
@@ -232,6 +264,190 @@ static void cs35l56_test_l56_b0_ssidexv2_ignored_suffix_sdw(struct kunit *test)
232264
KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "l1u5");
233265
}
234266

267+
/*
268+
* Test that cs35l56_process_xu_properties() correctly parses the GPIO and
269+
* pull values from properties into the arrays in struct cs35l56_base.
270+
*
271+
* This test creates the node tree:
272+
*
273+
* Node("SWD1") { // top-level device node
274+
* Node("AF01") {
275+
* Node("mipi-sdca-function-expansion-subproperties") {
276+
* property: "01fa-spk-id-gpios-onchip"
277+
* property: 01fa-spk-id-gpios-onchip-pull
278+
* }
279+
* }
280+
* }
281+
*
282+
* Note that in ACPI "mipi-sdca-function-expansion-subproperties" is
283+
* a special _DSD property that points to a Device(EXT0) node but behaves
284+
* as an alias of the EXT0 node. The equivalent in software nodes is to
285+
* create a Node named "mipi-sdca-function-expansion-subproperties" with
286+
* the properties.
287+
*
288+
*/
289+
static void cs35l56_test_parse_xu_onchip_spkid(struct kunit *test)
290+
{
291+
const struct cs35l56_test_param *param = test->param_value;
292+
struct cs35l56_test_priv *priv = test->priv;
293+
struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
294+
struct software_node *ext0_node;
295+
int num_gpios = 0;
296+
int num_pulls = 0;
297+
int i;
298+
299+
for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++, num_gpios++) {
300+
if (param->spkid_gpios[i] < 0)
301+
break;
302+
}
303+
KUNIT_ASSERT_LE(test, num_gpios, ARRAY_SIZE(cs35l56->base.onchip_spkid_gpios));
304+
305+
for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++, num_pulls++) {
306+
if (param->spkid_pulls[i] < 0)
307+
break;
308+
}
309+
KUNIT_ASSERT_LE(test, num_pulls, ARRAY_SIZE(cs35l56->base.onchip_spkid_pulls));
310+
311+
const struct property_entry ext0_props[] = {
312+
PROPERTY_ENTRY_U32_ARRAY_LEN("01fa-spk-id-gpios-onchip",
313+
param->spkid_gpios, num_gpios),
314+
PROPERTY_ENTRY_U32_ARRAY_LEN("01fa-spk-id-gpios-onchip-pull",
315+
param->spkid_pulls, num_pulls),
316+
{ }
317+
};
318+
319+
KUNIT_ASSERT_EQ(test,
320+
software_node_register_node_group(cs35l56_test_dev_and_af01_node_group),
321+
0);
322+
KUNIT_ASSERT_EQ(test,
323+
kunit_add_action_or_reset(test,
324+
software_node_unregister_node_group_wrapper,
325+
cs35l56_test_dev_and_af01_node_group),
326+
0);
327+
328+
ext0_node = kunit_kzalloc(test, sizeof(*ext0_node), GFP_KERNEL);
329+
KUNIT_ASSERT_NOT_NULL(test, ext0_node);
330+
*ext0_node = SOFTWARE_NODE("mipi-sdca-function-expansion-subproperties",
331+
ext0_props, &cs35l56_test_af01_sw_node);
332+
333+
KUNIT_ASSERT_EQ(test, software_node_register(ext0_node), 0);
334+
KUNIT_ASSERT_EQ(test,
335+
kunit_add_action_or_reset(test,
336+
software_node_unregister_wrapper,
337+
ext0_node),
338+
0);
339+
340+
KUNIT_ASSERT_EQ(test,
341+
device_add_software_node(cs35l56->base.dev, &cs35l56_test_dev_sw_node), 0);
342+
KUNIT_ASSERT_EQ(test, 0,
343+
kunit_add_action_or_reset(test,
344+
device_remove_software_node_wrapper,
345+
cs35l56->base.dev));
346+
347+
KUNIT_EXPECT_EQ(test, cs35l56_process_xu_properties(cs35l56), 0);
348+
349+
KUNIT_EXPECT_EQ(test, cs35l56->base.num_onchip_spkid_gpios, num_gpios);
350+
KUNIT_EXPECT_EQ(test, cs35l56->base.num_onchip_spkid_pulls, num_pulls);
351+
352+
for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
353+
if (param->spkid_gpios[i] < 0)
354+
break;
355+
356+
/*
357+
* cs35l56_process_xu_properties() stores the GPIO numbers
358+
* zero-based, which is one less than the value in the property.
359+
*/
360+
KUNIT_EXPECT_EQ_MSG(test, cs35l56->base.onchip_spkid_gpios[i],
361+
param->spkid_gpios[i] - 1,
362+
"i=%d", i);
363+
}
364+
365+
for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) {
366+
if (param->spkid_pulls[i] < 0)
367+
break;
368+
369+
KUNIT_EXPECT_EQ_MSG(test, cs35l56->base.onchip_spkid_pulls[i],
370+
param->spkid_pulls[i], "i=%d", i);
371+
}
372+
}
373+
374+
static int cs35l56_test_dummy_read_onchip_spkid(struct cs35l56_base *cs35l56_base)
375+
{
376+
struct kunit *test = kunit_get_current_test();
377+
struct cs35l56_test_priv *priv = test->priv;
378+
379+
priv->read_onchip_spkid_called = true;
380+
381+
return 4;
382+
}
383+
384+
static int cs35l56_test_dummy_configure_onchip_spkid_pads(struct cs35l56_base *cs35l56_base)
385+
{
386+
struct kunit *test = kunit_get_current_test();
387+
struct cs35l56_test_priv *priv = test->priv;
388+
389+
priv->configure_onchip_spkid_pads_called = true;
390+
391+
return 0;
392+
}
393+
394+
static void cs35l56_test_set_fw_name_reads_onchip_spkid(struct kunit *test)
395+
{
396+
struct cs35l56_test_priv *priv = test->priv;
397+
struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
398+
399+
/* Provide some on-chip GPIOs for spkid */
400+
cs35l56->base.onchip_spkid_gpios[0] = 1;
401+
cs35l56->base.num_onchip_spkid_gpios = 1;
402+
403+
cs35l56->speaker_id = -ENOENT;
404+
405+
kunit_activate_static_stub(test,
406+
cs35l56_configure_onchip_spkid_pads,
407+
cs35l56_test_dummy_configure_onchip_spkid_pads);
408+
kunit_activate_static_stub(test,
409+
cs35l56_read_onchip_spkid,
410+
cs35l56_test_dummy_read_onchip_spkid);
411+
412+
priv->configure_onchip_spkid_pads_called = false;
413+
priv->read_onchip_spkid_called = false;
414+
KUNIT_EXPECT_EQ(test, cs35l56_set_fw_name(cs35l56->component), 0);
415+
KUNIT_EXPECT_TRUE(test, priv->configure_onchip_spkid_pads_called);
416+
KUNIT_EXPECT_TRUE(test, priv->read_onchip_spkid_called);
417+
KUNIT_EXPECT_EQ(test, cs35l56->speaker_id,
418+
cs35l56_test_dummy_read_onchip_spkid(&cs35l56->base));
419+
}
420+
421+
static void cs35l56_test_set_fw_name_preserves_spkid_with_onchip_gpios(struct kunit *test)
422+
{
423+
struct cs35l56_test_priv *priv = test->priv;
424+
struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
425+
426+
/* Provide some on-chip GPIOs for spkid */
427+
cs35l56->base.onchip_spkid_gpios[0] = 1;
428+
cs35l56->base.num_onchip_spkid_gpios = 1;
429+
430+
/* Simulate that the driver already got a spkid from somewhere */
431+
cs35l56->speaker_id = 15;
432+
433+
KUNIT_EXPECT_EQ(test, cs35l56_set_fw_name(cs35l56->component), 0);
434+
KUNIT_EXPECT_EQ(test, cs35l56->speaker_id, 15);
435+
}
436+
437+
static void cs35l56_test_set_fw_name_preserves_spkid_without_onchip_gpios(struct kunit *test)
438+
{
439+
struct cs35l56_test_priv *priv = test->priv;
440+
struct cs35l56_private *cs35l56 = priv->cs35l56_priv;
441+
442+
cs35l56->base.num_onchip_spkid_gpios = 0;
443+
444+
/* Simulate that the driver already got a spkid from somewhere */
445+
cs35l56->speaker_id = 15;
446+
447+
KUNIT_EXPECT_EQ(test, cs35l56_set_fw_name(cs35l56->component), 0);
448+
KUNIT_EXPECT_EQ(test, cs35l56->speaker_id, 15);
449+
}
450+
235451
static int cs35l56_test_case_init_common(struct kunit *test)
236452
{
237453
struct cs35l56_test_priv *priv;
@@ -263,6 +479,7 @@ static int cs35l56_test_case_init_common(struct kunit *test)
263479
cs35l56->component = kunit_kzalloc(test, sizeof(*cs35l56->component), GFP_KERNEL);
264480
KUNIT_ASSERT_NOT_NULL(test, cs35l56->component);
265481
cs35l56->component->dev = cs35l56->base.dev;
482+
snd_soc_component_set_drvdata(cs35l56->component, cs35l56);
266483

267484
cs35l56->component->card = kunit_kzalloc(test, sizeof(*cs35l56->component->card),
268485
GFP_KERNEL);
@@ -299,6 +516,50 @@ static int cs35l56_test_case_init_soundwire(struct kunit *test)
299516
return 0;
300517
}
301518

519+
static void cs35l56_test_gpio_param_desc(const struct cs35l56_test_param *param, char *desc)
520+
{
521+
DECLARE_SEQ_BUF(gpios, 1 + (2 * ARRAY_SIZE(param->spkid_gpios)));
522+
DECLARE_SEQ_BUF(pulls, 1 + (2 * ARRAY_SIZE(param->spkid_pulls)));
523+
int i;
524+
525+
for (i = 0; i < ARRAY_SIZE(param->spkid_gpios); i++) {
526+
if (param->spkid_gpios[i] < 0)
527+
break;
528+
529+
seq_buf_printf(&gpios, "%s%d", (i == 0) ? "" : ",", param->spkid_gpios[i]);
530+
}
531+
532+
for (i = 0; i < ARRAY_SIZE(param->spkid_pulls); i++) {
533+
if (param->spkid_pulls[i] < 0)
534+
break;
535+
536+
seq_buf_printf(&pulls, "%s%d", (i == 0) ? "" : ",", param->spkid_pulls[i]);
537+
}
538+
539+
snprintf(desc, KUNIT_PARAM_DESC_SIZE, "gpios:{%s} pulls:{%s}",
540+
seq_buf_str(&gpios), seq_buf_str(&pulls));
541+
}
542+
543+
static const struct cs35l56_test_param cs35l56_test_onchip_spkid_cases[] = {
544+
{ .spkid_gpios = { 1, -1 }, .spkid_pulls = { 1, -1 }, },
545+
{ .spkid_gpios = { 1, -1 }, .spkid_pulls = { 2, -1 }, },
546+
547+
{ .spkid_gpios = { 7, -1 }, .spkid_pulls = { 1, -1 }, },
548+
{ .spkid_gpios = { 7, -1 }, .spkid_pulls = { 2, -1 }, },
549+
550+
{ .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { 1, 1, -1 }, },
551+
{ .spkid_gpios = { 1, 7, -1 }, .spkid_pulls = { 2, 2, -1 }, },
552+
553+
{ .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { 1, 1, -1 }, },
554+
{ .spkid_gpios = { 7, 1, -1 }, .spkid_pulls = { 2, 2, -1 }, },
555+
556+
{ .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { 1, 1, 1, -1 }, },
557+
{ .spkid_gpios = { 3, 7, 1, -1 }, .spkid_pulls = { 2, 2, 2, -1 }, },
558+
};
559+
KUNIT_ARRAY_PARAM(cs35l56_test_onchip_spkid,
560+
cs35l56_test_onchip_spkid_cases,
561+
cs35l56_test_gpio_param_desc);
562+
302563
static void cs35l56_test_type_rev_param_desc(const struct cs35l56_test_param *param,
303564
char *desc)
304565
{
@@ -331,6 +592,13 @@ static struct kunit_case cs35l56_test_cases_soundwire[] = {
331592
cs35l56_test_type_rev_ex_b0_gen_params),
332593
KUNIT_CASE(cs35l56_test_l56_b0_ssidexv2_ignored_suffix_sdw),
333594

595+
KUNIT_CASE_PARAM(cs35l56_test_parse_xu_onchip_spkid,
596+
cs35l56_test_onchip_spkid_gen_params),
597+
598+
KUNIT_CASE(cs35l56_test_set_fw_name_reads_onchip_spkid),
599+
KUNIT_CASE(cs35l56_test_set_fw_name_preserves_spkid_with_onchip_gpios),
600+
KUNIT_CASE(cs35l56_test_set_fw_name_preserves_spkid_without_onchip_gpios),
601+
334602
{ } /* terminator */
335603
};
336604

@@ -339,6 +607,10 @@ static struct kunit_case cs35l56_test_cases_not_soundwire[] = {
339607
KUNIT_CASE_PARAM(cs35l56_test_ssidexv2_suffix_i2cspi,
340608
cs35l56_test_type_rev_all_gen_params),
341609

610+
KUNIT_CASE(cs35l56_test_set_fw_name_reads_onchip_spkid),
611+
KUNIT_CASE(cs35l56_test_set_fw_name_preserves_spkid_with_onchip_gpios),
612+
KUNIT_CASE(cs35l56_test_set_fw_name_preserves_spkid_without_onchip_gpios),
613+
342614
{ } /* terminator */
343615
};
344616

@@ -360,6 +632,7 @@ kunit_test_suites(
360632
);
361633

362634
MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
635+
MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED");
363636
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
364637
MODULE_DESCRIPTION("KUnit test for Cirrus Logic cs35l56 codec driver");
365638
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");

0 commit comments

Comments
 (0)