Unverified Commit d0ab8995 authored by Richard Fitzgerald's avatar Richard Fitzgerald Committed by Mark Brown
Browse files

ASoC: cs35l56: Add KUnit testing of cs35l56_set_fw_suffix()



Add a new KUnit test for testing the creation of firmware name
qualifiers in the cs35l56 driver. The initial set of test cases
are for cs35l56_set_fw_suffix().

Signed-off-by: default avatarRichard Fitzgerald <rf@opensource.cirrus.com>
Link: https://patch.msgid.link/20260121132243.1256019-6-rf@opensource.cirrus.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 3f086a4f
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -933,6 +933,19 @@ config SND_SOC_CS35L56_CAL_SET_CTRL
	  On most platforms this is not needed.

	  If unsure select "N".

config SND_SOC_CS35L56_TEST
	tristate "KUnit test for Cirrus Logic cs35l56 driver" if !KUNIT_ALL_TESTS
	depends on SND_SOC_CS35L56 && KUNIT
	default KUNIT_ALL_TESTS
	select SND_SOC_CS_AMP_LIB_TEST_HOOKS
	help
	  This builds KUnit tests for the Cirrus Logic cs35l56
	  codec driver.
	  For more information on KUnit and unit tests in general,
	  please refer to the KUnit documentation in
	  Documentation/dev-tools/kunit/.
	  If in doubt, say "N".
endmenu

config SND_SOC_CS40L50
+2 −0
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ snd-soc-cs35l56-shared-y := cs35l56-shared.o
snd-soc-cs35l56-i2c-y := cs35l56-i2c.o
snd-soc-cs35l56-spi-y := cs35l56-spi.o
snd-soc-cs35l56-sdw-y := cs35l56-sdw.o
snd-soc-cs35l56-test-y := cs35l56-test.o
snd-soc-cs40l50-y := cs40l50-codec.o
snd-soc-cs42l42-y := cs42l42.o
snd-soc-cs42l42-i2c-y := cs42l42-i2c.o
@@ -516,6 +517,7 @@ obj-$(CONFIG_SND_SOC_CS35L56_SHARED) += snd-soc-cs35l56-shared.o
obj-$(CONFIG_SND_SOC_CS35L56_I2C)	+= snd-soc-cs35l56-i2c.o
obj-$(CONFIG_SND_SOC_CS35L56_SPI)	+= snd-soc-cs35l56-spi.o
obj-$(CONFIG_SND_SOC_CS35L56_SDW)	+= snd-soc-cs35l56-sdw.o
obj-$(CONFIG_SND_SOC_CS35L56_TEST)	+= snd-soc-cs35l56-test.o
obj-$(CONFIG_SND_SOC_CS40L50)		+= snd-soc-cs40l50.o
obj-$(CONFIG_SND_SOC_CS42L42_CORE)	+= snd-soc-cs42l42.o
obj-$(CONFIG_SND_SOC_CS42L42)	+= snd-soc-cs42l42-i2c.o
+3 −0
Original line number Diff line number Diff line
@@ -806,6 +806,9 @@ const char *cs_amp_devm_get_vendor_specific_variant_id(struct device *dev,
						       int ssid_vendor,
						       int ssid_device)
{
	KUNIT_STATIC_STUB_REDIRECT(cs_amp_devm_get_vendor_specific_variant_id,
				   dev, ssid_vendor, ssid_device);

	if ((ssid_vendor == PCI_VENDOR_ID_DELL) || (ssid_vendor < 0))
		return cs_amp_devm_get_dell_ssidex(dev, ssid_vendor, ssid_device);

+365 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
//
// KUnit test for the Cirrus Logic cs35l56 driver.
//
// Copyright (C) 2026 Cirrus Logic, Inc. and
//                    Cirrus Logic International Semiconductor Ltd.

#include <kunit/resource.h>
#include <kunit/test.h>
#include <kunit/static_stub.h>
#include <linux/efi.h>
#include <linux/device/faux.h>
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/firmware/cirrus/wmfw.h>
#include <linux/module.h>
#include <linux/overflow.h>
#include <linux/pci_ids.h>
#include <linux/soundwire/sdw.h>
#include <sound/cs35l56.h>
#include <sound/cs-amp-lib.h>
#include "cs35l56.h"

KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy,
			    struct faux_device *)

struct cs35l56_test_priv {
	struct faux_device *amp_dev;
	struct cs35l56_private *cs35l56_priv;

	const char *ssidexv2;
};

struct cs35l56_test_param {
	u8 type;
	u8 rev;
};

static const char *cs35l56_test_devm_get_vendor_specific_variant_id_none(struct device *dev,
									 int ssid_vendor,
									 int ssid_device)
{
	return ERR_PTR(-ENOENT);
}

static void cs35l56_test_l56_b0_suffix_sdw(struct kunit *test)
{
	struct cs35l56_test_priv *priv = test->priv;
	struct cs35l56_private *cs35l56 = priv->cs35l56_priv;

	/* Set device type info */
	cs35l56->base.type = 0x56;
	cs35l56->base.rev = 0xb0;

	/* Set the ALSA name prefix */
	cs35l56->component->name_prefix = "AMP1";

	/* Set SoundWire link and UID number */
	cs35l56->sdw_link_num = 1;
	cs35l56->sdw_unique_id = 5;

	kunit_activate_static_stub(test,
				   cs35l56_test_devm_get_vendor_specific_variant_id_none,
				   cs_amp_devm_get_vendor_specific_variant_id);

	KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56));

	/* Priority suffix should be the legacy ALSA prefix */
	KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "AMP1");

	/* Fallback suffix should be the new SoundWire ID */
	KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "l1u5");
}

static void cs35l56_test_suffix_sdw(struct kunit *test)
{
	struct cs35l56_test_priv *priv = test->priv;
	struct cs35l56_private *cs35l56 = priv->cs35l56_priv;

	/* Set the ALSA name prefix */
	cs35l56->component->name_prefix = "AMP1";

	/* Set SoundWire link and UID number */
	cs35l56->sdw_link_num = 1;
	cs35l56->sdw_unique_id = 5;

	kunit_activate_static_stub(test,
				   cs35l56_test_devm_get_vendor_specific_variant_id_none,
				   cs_amp_devm_get_vendor_specific_variant_id);

	KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56));

	/* Suffix should be the SoundWire ID without a fallback */
	KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "l1u5");
	KUNIT_EXPECT_NULL(test, cs35l56->fallback_fw_suffix);
}

static void cs35l56_test_suffix_i2cspi(struct kunit *test)
{
	struct cs35l56_test_priv *priv = test->priv;
	struct cs35l56_private *cs35l56 = priv->cs35l56_priv;

	/* Set the ALSA name prefix */
	cs35l56->component->name_prefix = "AMP1";

	kunit_activate_static_stub(test,
				   cs35l56_test_devm_get_vendor_specific_variant_id_none,
				   cs_amp_devm_get_vendor_specific_variant_id);

	KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56));

	/* Suffix strings should not be set: use default wm_adsp suffixing */
	KUNIT_EXPECT_NULL(test, cs35l56->dsp.fwf_suffix);
	KUNIT_EXPECT_NULL(test, cs35l56->fallback_fw_suffix);
}

static efi_status_t cs35l56_test_get_efi_ssidexv2(efi_char16_t *name,
						  efi_guid_t *guid,
						  u32 *returned_attr,
						  unsigned long *size,
						  void *buf)
{
	struct kunit *test = kunit_get_current_test();
	struct cs35l56_test_priv *priv = test->priv;
	unsigned int len;

	KUNIT_ASSERT_NOT_NULL(test, priv->ssidexv2);
	len = strlen(priv->ssidexv2);

	if (*size < len) {
		*size = len;
		return EFI_BUFFER_TOO_SMALL;
	}

	KUNIT_ASSERT_NOT_NULL(test, buf);
	memcpy(buf, priv->ssidexv2, len);

	return EFI_SUCCESS;
}

static void cs35l56_test_ssidexv2_suffix_sdw(struct kunit *test)
{
	struct cs35l56_test_priv *priv = test->priv;
	struct cs35l56_private *cs35l56 = priv->cs35l56_priv;

	/* Set the ALSA name prefix */
	cs35l56->component->name_prefix = "AMP1";

	/* Set SoundWire link and UID number */
	cs35l56->sdw_link_num = 1;
	cs35l56->sdw_unique_id = 5;

	/* Set a SSID to enable lookup of SSIDExV2 */
	snd_soc_card_set_pci_ssid(cs35l56->component->card, PCI_VENDOR_ID_DELL, 0x1234);

	priv->ssidexv2 = "10281234_01_BB_CC";

	kunit_activate_static_stub(test,
				   cs_amp_test_hooks->get_efi_variable,
				   cs35l56_test_get_efi_ssidexv2);

	KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56));

	/* Priority suffix should be the SSIDExV2 string with SoundWire ID */
	KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "01-l1u5");

	/* Fallback suffix should be the SoundWireID */
	KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "l1u5");
}

static void cs35l56_test_ssidexv2_suffix_i2cspi(struct kunit *test)
{
	struct cs35l56_test_priv *priv = test->priv;
	struct cs35l56_private *cs35l56 = priv->cs35l56_priv;

	/* Set the ALSA name prefix */
	cs35l56->component->name_prefix = "AMP1";

	/* Set a SSID to enable lookup of SSIDExV2 */
	snd_soc_card_set_pci_ssid(cs35l56->component->card, PCI_VENDOR_ID_DELL, 0x1234);

	priv->ssidexv2 = "10281234_01_BB_CC";

	kunit_activate_static_stub(test,
				   cs_amp_test_hooks->get_efi_variable,
				   cs35l56_test_get_efi_ssidexv2);

	KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56));

	/* Priority suffix should be the SSIDExV2 string with ALSA name prefix */
	KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "01-AMP1");

	/* Fallback suffix should be the ALSA name prefix */
	KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "AMP1");
}

/*
 * CS35L56 B0 SoundWire should ignore any SSIDExV2 suffix. It isn't needed
 * on any products with B0 silicon and would interfere with the fallback
 * to legacy naming convention for early B0-based laptops.
 */
static void cs35l56_test_l56_b0_ssidexv2_ignored_suffix_sdw(struct kunit *test)
{
	struct cs35l56_test_priv *priv = test->priv;
	struct cs35l56_private *cs35l56 = priv->cs35l56_priv;

	/* Set device type info */
	cs35l56->base.type = 0x56;
	cs35l56->base.rev = 0xb0;

	/* Set the ALSA name prefix */
	cs35l56->component->name_prefix = "AMP1";

	/* Set SoundWire link and UID number */
	cs35l56->sdw_link_num = 1;
	cs35l56->sdw_unique_id = 5;

	/* Set a SSID to enable lookup of SSIDExV2 */
	snd_soc_card_set_pci_ssid(cs35l56->component->card, PCI_VENDOR_ID_DELL, 0x1234);

	priv->ssidexv2 = "10281234_01_BB_CC";

	kunit_activate_static_stub(test,
				   cs_amp_test_hooks->get_efi_variable,
				   cs35l56_test_get_efi_ssidexv2);

	KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56));

	/* Priority suffix should be the legacy ALSA prefix */
	KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "AMP1");

	/* Fallback suffix should be the new SoundWire ID */
	KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "l1u5");
}

static int cs35l56_test_case_init_common(struct kunit *test)
{
	struct cs35l56_test_priv *priv;
	const struct cs35l56_test_param *param = test->param_value;
	struct cs35l56_private *cs35l56;

	KUNIT_ASSERT_NOT_NULL(test, cs_amp_test_hooks);

	priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	test->priv = priv;

	/* Create dummy amp driver dev */
	priv->amp_dev = faux_device_create("cs35l56_test_drv", NULL, NULL);
	KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev);
	KUNIT_ASSERT_EQ(test, 0,
			kunit_add_action_or_reset(test,
						  faux_device_destroy_wrapper,
						  priv->amp_dev));

	/* Construct minimal set of driver structs */
	priv->cs35l56_priv = kunit_kzalloc(test, sizeof(*priv->cs35l56_priv), GFP_KERNEL);
	KUNIT_ASSERT_NOT_NULL(test, priv->cs35l56_priv);
	cs35l56 = priv->cs35l56_priv;
	cs35l56->base.dev = &priv->amp_dev->dev;

	cs35l56->component = kunit_kzalloc(test, sizeof(*cs35l56->component), GFP_KERNEL);
	KUNIT_ASSERT_NOT_NULL(test, cs35l56->component);
	cs35l56->component->dev = cs35l56->base.dev;

	cs35l56->component->card = kunit_kzalloc(test, sizeof(*cs35l56->component->card),
						 GFP_KERNEL);
	KUNIT_ASSERT_NOT_NULL(test, cs35l56->component->card);

	if (param) {
		cs35l56->base.type = param->type;
		cs35l56->base.rev = param->rev;
	}

	return 0;
}

static int cs35l56_test_case_init_soundwire(struct kunit *test)
{
	struct cs35l56_test_priv *priv;
	struct cs35l56_private *cs35l56;
	int ret;

	ret = cs35l56_test_case_init_common(test);
	if (ret)
		return ret;

	priv = test->priv;
	cs35l56 = priv->cs35l56_priv;

	/* Dummy to indicate this is Soundwire */
	cs35l56->sdw_peripheral = kunit_kzalloc(test, sizeof(*cs35l56->sdw_peripheral),
						GFP_KERNEL);
	if (!cs35l56->sdw_peripheral)
		return -ENOMEM;


	return 0;
}

static void cs35l56_test_type_rev_param_desc(const struct cs35l56_test_param *param,
					     char *desc)
{
	snprintf(desc, KUNIT_PARAM_DESC_SIZE, "type: %02x rev: %02x",
		 param->type, param->rev);
}

static const struct cs35l56_test_param cs35l56_test_type_rev_ex_b0_param_cases[] = {
	{ .type = 0x56, .rev = 0xb2 },
	{ .type = 0x57, .rev = 0xb2 },
	{ .type = 0x63, .rev = 0xa1 },
};
KUNIT_ARRAY_PARAM(cs35l56_test_type_rev_ex_b0, cs35l56_test_type_rev_ex_b0_param_cases,
		  cs35l56_test_type_rev_param_desc);


static const struct cs35l56_test_param cs35l56_test_type_rev_all_param_cases[] = {
	{ .type = 0x56, .rev = 0xb0 },
	{ .type = 0x56, .rev = 0xb2 },
	{ .type = 0x57, .rev = 0xb2 },
	{ .type = 0x63, .rev = 0xa1 },
};
KUNIT_ARRAY_PARAM(cs35l56_test_type_rev_all, cs35l56_test_type_rev_all_param_cases,
		  cs35l56_test_type_rev_param_desc);

static struct kunit_case cs35l56_test_cases_soundwire[] = {
	KUNIT_CASE(cs35l56_test_l56_b0_suffix_sdw),
	KUNIT_CASE_PARAM(cs35l56_test_suffix_sdw, cs35l56_test_type_rev_ex_b0_gen_params),
	KUNIT_CASE_PARAM(cs35l56_test_ssidexv2_suffix_sdw,
			 cs35l56_test_type_rev_ex_b0_gen_params),
	KUNIT_CASE(cs35l56_test_l56_b0_ssidexv2_ignored_suffix_sdw),

	{ } /* terminator */
};

static struct kunit_case cs35l56_test_cases_not_soundwire[] = {
	KUNIT_CASE_PARAM(cs35l56_test_suffix_i2cspi, cs35l56_test_type_rev_all_gen_params),
	KUNIT_CASE_PARAM(cs35l56_test_ssidexv2_suffix_i2cspi,
			 cs35l56_test_type_rev_all_gen_params),

	{ } /* terminator */
};

static struct kunit_suite cs35l56_test_suite_soundwire = {
	.name = "snd-soc-cs35l56-test-soundwire",
	.init = cs35l56_test_case_init_soundwire,
	.test_cases = cs35l56_test_cases_soundwire,
};

static struct kunit_suite cs35l56_test_suite_not_soundwire = {
	.name = "snd-soc-cs35l56-test-not-soundwire",
	.init = cs35l56_test_case_init_common,
	.test_cases = cs35l56_test_cases_not_soundwire,
};

kunit_test_suites(
	&cs35l56_test_suite_soundwire,
	&cs35l56_test_suite_not_soundwire,
);

MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
MODULE_DESCRIPTION("KUnit test for Cirrus Logic cs35l56 codec driver");
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
MODULE_LICENSE("GPL");
+4 −1
Original line number Diff line number Diff line
@@ -5,6 +5,8 @@
// Copyright (C) 2023 Cirrus Logic, Inc. and
//                    Cirrus Logic International Semiconductor Ltd.

#include <kunit/static_stub.h>
#include <kunit/visibility.h>
#include <linux/acpi.h>
#include <linux/array_size.h>
#include <linux/completion.h>
@@ -1107,7 +1109,7 @@ static const struct snd_kcontrol_new cs35l56_cal_data_restore_controls[] = {
			SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE),
};

static int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56)
VISIBLE_IF_KUNIT int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56)
{
	unsigned short vendor, device;
	const char *vendor_id;
@@ -1175,6 +1177,7 @@ static int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56)

	return 0;
}
EXPORT_SYMBOL_IF_KUNIT(cs35l56_set_fw_suffix);

static int cs35l56_component_probe(struct snd_soc_component *component)
{
Loading