From 89ace3acf013eebb299290f41b4ac34bf6c62a10 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 9 Sep 2025 12:30:34 +0100 Subject: [PATCH 1/6] ASoC: cs-amp-lib: Rename defines for Cirrus Logic EFI Rename: CS_AMP_CAL_NAME => CIRRUS_LOGIC_CALIBRATION_EFI_NAME CS_AMP_CAL_GUID => CIRRUS_LOGIC_CALIBRATION_EFI_GUID This is to clarify that these are specific to Cirrus Logic, especially the GUID. As defined by the UEFI specification the GUID is a vendor identifier, not an EFI variable identifier. There has been some misunderstanding of the purpose of these, which has led to the Cirrus Logic GUID value being copied and used for other vendor's EFI variables. It is rather strange to have data from another vendor marked with the vendor GUID for Cirrus Logic. Signed-off-by: Richard Fitzgerald Message-ID: <20250909113039.922065-2-rf@opensource.cirrus.com> Signed-off-by: Mark Brown --- sound/soc/codecs/cs-amp-lib.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c index 808e67c90f7c..b1530e7c75e8 100644 --- a/sound/soc/codecs/cs-amp-lib.c +++ b/sound/soc/codecs/cs-amp-lib.c @@ -16,11 +16,10 @@ #include #include -#define CS_AMP_CAL_GUID \ +#define CIRRUS_LOGIC_CALIBRATION_EFI_NAME L"CirrusSmartAmpCalibrationData" +#define CIRRUS_LOGIC_CALIBRATION_EFI_GUID \ EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3) -#define CS_AMP_CAL_NAME L"CirrusSmartAmpCalibrationData" - static int cs_amp_write_cal_coeff(struct cs_dsp *dsp, const struct cirrus_amp_cal_controls *controls, const char *ctl_name, u32 val) @@ -124,7 +123,9 @@ static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev) int ret; /* Get real size of UEFI variable */ - status = cs_amp_get_efi_variable(CS_AMP_CAL_NAME, &CS_AMP_CAL_GUID, &data_size, NULL); + status = cs_amp_get_efi_variable(CIRRUS_LOGIC_CALIBRATION_EFI_NAME, + &CIRRUS_LOGIC_CALIBRATION_EFI_GUID, + &data_size, NULL); if (status != EFI_BUFFER_TOO_SMALL) return ERR_PTR(-ENOENT); @@ -138,7 +139,9 @@ static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev) if (!data) return ERR_PTR(-ENOMEM); - status = cs_amp_get_efi_variable(CS_AMP_CAL_NAME, &CS_AMP_CAL_GUID, &data_size, data); + status = cs_amp_get_efi_variable(CIRRUS_LOGIC_CALIBRATION_EFI_NAME, + &CIRRUS_LOGIC_CALIBRATION_EFI_GUID, + &data_size, data); if (status != EFI_SUCCESS) { ret = -EINVAL; goto err; From 267b9cdee522d03f95acf7c77de91056a4e004b3 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 9 Sep 2025 12:30:35 +0100 Subject: [PATCH 2/6] ASoC: cs-amp-lib: Add handling for Lenovo and HP UEFI speaker ID Add handling of the Lenovo-specific and HP-specific EFI variables for speaker ID. Future Lenovo and HP models will not give the codec driver access to the speaker detect GPIO. Instead, the BIOS will read the GPIO and create an EFI variable with a value indicating the state of the GPIO. The Lenovo and HP EFI variables are both defined to have only two valid values. But the variable name, GUID and values are different. This adds a new exported function cs_amp_get_vendor_spkid(). Signed-off-by: Richard Fitzgerald Message-ID: <20250909113039.922065-3-rf@opensource.cirrus.com> Signed-off-by: Mark Brown --- include/sound/cs-amp-lib.h | 1 + sound/soc/codecs/cs-amp-lib.c | 101 ++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/include/sound/cs-amp-lib.h b/include/sound/cs-amp-lib.h index 5459c221badf..43a87a39110c 100644 --- a/include/sound/cs-amp-lib.h +++ b/include/sound/cs-amp-lib.h @@ -49,6 +49,7 @@ int cs_amp_write_cal_coeffs(struct cs_dsp *dsp, const struct cirrus_amp_cal_data *data); int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index, struct cirrus_amp_cal_data *out_data); +int cs_amp_get_vendor_spkid(struct device *dev); struct cs_amp_test_hooks { efi_status_t (*get_efi_variable)(efi_char16_t *name, diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c index b1530e7c75e8..9b51d056d863 100644 --- a/sound/soc/codecs/cs-amp-lib.c +++ b/sound/soc/codecs/cs-amp-lib.c @@ -20,6 +20,14 @@ #define CIRRUS_LOGIC_CALIBRATION_EFI_GUID \ EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3) +#define LENOVO_SPEAKER_ID_EFI_NAME L"SdwSpeaker" +#define LENOVO_SPEAKER_ID_EFI_GUID \ + EFI_GUID(0x48df970e, 0xe27f, 0x460a, 0xb5, 0x86, 0x77, 0x19, 0x80, 0x1d, 0x92, 0x82) + +#define HP_SPEAKER_ID_EFI_NAME L"HPSpeakerID" +#define HP_SPEAKER_ID_EFI_GUID \ + EFI_GUID(0xc49593a4, 0xd099, 0x419b, 0xa2, 0xc3, 0x67, 0xe9, 0x80, 0xe6, 0x1d, 0x1e) + static int cs_amp_write_cal_coeff(struct cs_dsp *dsp, const struct cirrus_amp_cal_controls *controls, const char *ctl_name, u32 val) @@ -114,6 +122,24 @@ static efi_status_t cs_amp_get_efi_variable(efi_char16_t *name, return EFI_NOT_FOUND; } +static int cs_amp_convert_efi_status(efi_status_t status) +{ + switch (status) { + case EFI_SUCCESS: + return 0; + case EFI_NOT_FOUND: + return -ENOENT; + case EFI_BUFFER_TOO_SMALL: + return -EFBIG; + case EFI_UNSUPPORTED: + case EFI_ACCESS_DENIED: + case EFI_SECURITY_VIOLATION: + return -EACCES; + default: + return -EIO; + } +} + static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev) { struct cirrus_amp_efi_data *efi_data; @@ -276,6 +302,81 @@ int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_ } EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, "SND_SOC_CS_AMP_LIB"); +struct cs_amp_spkid_efi { + efi_char16_t *name; + efi_guid_t *guid; + u8 values[2]; +}; + +static int cs_amp_get_efi_byte_spkid(struct device *dev, const struct cs_amp_spkid_efi *info) +{ + efi_status_t status; + unsigned long size; + u8 spkid; + int i, ret; + + size = sizeof(spkid); + status = cs_amp_get_efi_variable(info->name, info->guid, &size, &spkid); + ret = cs_amp_convert_efi_status(status); + if (ret < 0) + return ret; + + if (size == 0) + return -ENOENT; + + for (i = 0; i < ARRAY_SIZE(info->values); i++) { + if (info->values[i] == spkid) + return i; + } + + dev_err(dev, "EFI speaker ID bad value %#x\n", spkid); + + return -EINVAL; +} + +static const struct cs_amp_spkid_efi cs_amp_spkid_byte_types[] = { + { + .name = LENOVO_SPEAKER_ID_EFI_NAME, + .guid = &LENOVO_SPEAKER_ID_EFI_GUID, + .values = { 0xd0, 0xd1 }, + }, + { + .name = HP_SPEAKER_ID_EFI_NAME, + .guid = &HP_SPEAKER_ID_EFI_GUID, + .values = { 0x30, 0x31 }, + }, +}; + +/** + * cs_amp_get_vendor_spkid - get a speaker ID from vendor-specific storage + * @dev: pointer to struct device + * + * Known vendor-specific methods of speaker ID are checked and if one is + * found its speaker ID value is returned. + * + * Return: >=0 is a valid speaker ID. -ENOENT if a vendor-specific method + * was not found. -EACCES if the vendor-specific storage could not + * be read. Other error values indicate that the data from the + * vendor-specific storage was found but could not be understood. + */ +int cs_amp_get_vendor_spkid(struct device *dev) +{ + int i, ret; + + if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) && + !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) + return -ENOENT; + + for (i = 0; i < ARRAY_SIZE(cs_amp_spkid_byte_types); i++) { + ret = cs_amp_get_efi_byte_spkid(dev, &cs_amp_spkid_byte_types[i]); + if (ret != -ENOENT) + return ret; + } + + return -ENOENT; +} +EXPORT_SYMBOL_NS_GPL(cs_amp_get_vendor_spkid, "SND_SOC_CS_AMP_LIB"); + static const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = { .get_efi_variable = cs_amp_get_efi_variable, .write_cal_coeff = cs_amp_write_cal_coeff, From 59255cfa4a0aa3484739ea58d67e02651424ffe3 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 9 Sep 2025 12:30:36 +0100 Subject: [PATCH 3/6] ASoC: cs35l56: Check for vendor-specific speaker ID value Call cs_amp_get_vendor_spkid() to get use a vendor-specific speaker ID value, if one exists. The speaker ID is used to load an appropriate set of firmware files for the speakers, and is usually read from a GPIO. Some manufacturers are instead using a custom UEFI variable for the speaker ID. Signed-off-by: Richard Fitzgerald Message-ID: <20250909113039.922065-4-rf@opensource.cirrus.com> Signed-off-by: Mark Brown --- sound/soc/codecs/cs35l56-shared.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 850fcf385996..95d018ecb953 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -1054,7 +1054,17 @@ int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base) u32 speaker_id; int i, ret; - /* Attempt to read the speaker type from a device property first */ + /* Check for vendor-specific speaker ID method */ + ret = cs_amp_get_vendor_spkid(cs35l56_base->dev); + if (ret >= 0) { + dev_dbg(cs35l56_base->dev, "Vendor Speaker ID = %d\n", ret); + return ret; + } else if (ret != -ENOENT) { + dev_err(cs35l56_base->dev, "Error getting vendor Speaker ID: %d\n", ret); + return ret; + } + + /* Attempt to read the speaker type from a device property */ ret = device_property_read_u32(cs35l56_base->dev, "cirrus,speaker-id", &speaker_id); if (!ret) { dev_dbg(cs35l56_base->dev, "Speaker ID = %d\n", speaker_id); From 7a4e5f4c663319f1e0c990ca62e3bb8d3d215c93 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 9 Sep 2025 12:30:37 +0100 Subject: [PATCH 4/6] ASoC: cs-amp-lib-test: Add tests for cs_amp_get_vendor_spkid() Add test cases for cs_amp_get_vendor_spkid() for the Lenovo speaker ID UEFI variable. This is a simple set of cases: - EFI variable is not found - EFI variable size is larger than the expect 1 byte - EFI variable has invalid value 0 - Lenovo UEFI variable valid values are interpreted correctly - HP UEFI variable valid values are interpreted correctly Signed-off-by: Richard Fitzgerald Message-ID: <20250909113039.922065-5-rf@opensource.cirrus.com> Signed-off-by: Mark Brown --- sound/soc/codecs/cs-amp-lib-test.c | 196 +++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) diff --git a/sound/soc/codecs/cs-amp-lib-test.c b/sound/soc/codecs/cs-amp-lib-test.c index f53650128fc3..e7492afa041e 100644 --- a/sound/soc/codecs/cs-amp-lib-test.c +++ b/sound/soc/codecs/cs-amp-lib-test.c @@ -19,6 +19,14 @@ #include #include +#define LENOVO_SPEAKER_ID_EFI_NAME L"SdwSpeaker" +#define LENOVO_SPEAKER_ID_EFI_GUID \ + EFI_GUID(0x48df970e, 0xe27f, 0x460a, 0xb5, 0x86, 0x77, 0x19, 0x80, 0x1d, 0x92, 0x82) + +#define HP_SPEAKER_ID_EFI_NAME L"HPSpeakerID" +#define HP_SPEAKER_ID_EFI_GUID \ + EFI_GUID(0xc49593a4, 0xd099, 0x419b, 0xa2, 0xc3, 0x67, 0xe9, 0x80, 0xe6, 0x1d, 0x1e) + KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy, struct faux_device *) @@ -642,6 +650,185 @@ static void cs_amp_lib_test_write_cal_data_test(struct kunit *test) KUNIT_EXPECT_EQ(test, entry->value, data.calStatus); } +static void cs_amp_lib_test_spkid_lenovo_not_present(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct device *dev = &priv->amp_dev->dev; + + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable_none); + + KUNIT_EXPECT_EQ(test, -ENOENT, cs_amp_get_vendor_spkid(dev)); +} + +static efi_status_t cs_amp_lib_test_get_efi_variable_lenovo_d0(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + struct kunit *test = kunit_get_current_test(); + + if (efi_guidcmp(*guid, LENOVO_SPEAKER_ID_EFI_GUID) || + memcmp(name, LENOVO_SPEAKER_ID_EFI_NAME, sizeof(LENOVO_SPEAKER_ID_EFI_NAME))) + return EFI_NOT_FOUND; + + KUNIT_ASSERT_EQ(test, *size, 1); + *size = 1; + *(u8 *)buf = 0xd0; + + return EFI_SUCCESS; +} + +static efi_status_t cs_amp_lib_test_get_efi_variable_lenovo_d1(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + struct kunit *test = kunit_get_current_test(); + + if (efi_guidcmp(*guid, LENOVO_SPEAKER_ID_EFI_GUID) || + memcmp(name, LENOVO_SPEAKER_ID_EFI_NAME, sizeof(LENOVO_SPEAKER_ID_EFI_NAME))) + return EFI_NOT_FOUND; + + KUNIT_ASSERT_EQ(test, *size, 1); + *size = 1; + *(u8 *)buf = 0xd1; + + return EFI_SUCCESS; +} + +static efi_status_t cs_amp_lib_test_get_efi_variable_lenovo_00(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + struct kunit *test = kunit_get_current_test(); + + KUNIT_ASSERT_EQ(test, 0, efi_guidcmp(*guid, LENOVO_SPEAKER_ID_EFI_GUID)); + KUNIT_ASSERT_EQ(test, *size, 1); + *size = 1; + *(u8 *)buf = 0; + + return EFI_SUCCESS; +} + +static void cs_amp_lib_test_spkid_lenovo_d0(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct device *dev = &priv->amp_dev->dev; + + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable_lenovo_d0); + + KUNIT_EXPECT_EQ(test, 0, cs_amp_get_vendor_spkid(dev)); +} + +static void cs_amp_lib_test_spkid_lenovo_d1(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct device *dev = &priv->amp_dev->dev; + + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable_lenovo_d1); + + KUNIT_EXPECT_EQ(test, 1, cs_amp_get_vendor_spkid(dev)); +} + +static void cs_amp_lib_test_spkid_lenovo_illegal(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct device *dev = &priv->amp_dev->dev; + + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable_lenovo_00); + + KUNIT_EXPECT_LT(test, cs_amp_get_vendor_spkid(dev), 0); +} + +static efi_status_t cs_amp_lib_test_get_efi_variable_buf_too_small(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + return EFI_BUFFER_TOO_SMALL; +} + +static void cs_amp_lib_test_spkid_lenovo_oversize(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct device *dev = &priv->amp_dev->dev; + + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable_buf_too_small); + + KUNIT_EXPECT_LT(test, cs_amp_get_vendor_spkid(dev), 0); +} + +static efi_status_t cs_amp_lib_test_get_efi_variable_hp_30(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + struct kunit *test = kunit_get_current_test(); + + if (efi_guidcmp(*guid, HP_SPEAKER_ID_EFI_GUID) || + memcmp(name, HP_SPEAKER_ID_EFI_NAME, sizeof(HP_SPEAKER_ID_EFI_NAME))) + return EFI_NOT_FOUND; + + KUNIT_ASSERT_EQ(test, *size, 1); + *size = 1; + *(u8 *)buf = 0x30; + + return EFI_SUCCESS; +} + +static efi_status_t cs_amp_lib_test_get_efi_variable_hp_31(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + struct kunit *test = kunit_get_current_test(); + + if (efi_guidcmp(*guid, HP_SPEAKER_ID_EFI_GUID) || + memcmp(name, HP_SPEAKER_ID_EFI_NAME, sizeof(HP_SPEAKER_ID_EFI_NAME))) + return EFI_NOT_FOUND; + + KUNIT_ASSERT_EQ(test, *size, 1); + *size = 1; + *(u8 *)buf = 0x31; + + return EFI_SUCCESS; +} + +static void cs_amp_lib_test_spkid_hp_30(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct device *dev = &priv->amp_dev->dev; + + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable_hp_30); + + KUNIT_EXPECT_EQ(test, 0, cs_amp_get_vendor_spkid(dev)); +} + +static void cs_amp_lib_test_spkid_hp_31(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct device *dev = &priv->amp_dev->dev; + + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_efi_variable_hp_31); + + KUNIT_EXPECT_EQ(test, 1, cs_amp_get_vendor_spkid(dev)); +} + static int cs_amp_lib_test_case_init(struct kunit *test) { struct cs_amp_lib_test_priv *priv; @@ -737,6 +924,15 @@ static struct kunit_case cs_amp_lib_test_cases[] = { /* Tests for writing calibration data */ KUNIT_CASE(cs_amp_lib_test_write_cal_data_test), + /* Test cases for speaker ID */ + KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_not_present), + KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_d0), + KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_d1), + KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_illegal), + KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_oversize), + KUNIT_CASE(cs_amp_lib_test_spkid_hp_30), + KUNIT_CASE(cs_amp_lib_test_spkid_hp_31), + { } /* terminator */ }; From b78dd64208a83f66fc21951bd8758281a7d445a5 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 9 Sep 2025 12:30:38 +0100 Subject: [PATCH 5/6] ASoC: cs-amp-lib: Add HP-specific EFI variable for calibration data Search for an HP-specific EFI variable for calibration before falling back to the normal Cirrus Logic EFI variable. Future HP models will use an HP-defined EFI variable for storage of amp calibration data. The content is the same as the normal Cirrus Logic EFI variable. The first step in cs_amp_get_cal_efi_buffer() is to get the size of the EFI variable, so this has been made a loop that walks through an array of possible variables. A small change is needed to the KUnit test, which is included in this patch. Originally the cs_amp_lib_test_get_efi_variable() hook function asserted that the passed name and GUID matched the Cirrus Logic EFI variable. Obviously this will fail because the code now tries the HP definition first. The function has been changed to return EFI_NOT_FOUND instead, which emulates the normal behaviour of trying to get the HP variable on a system that has the Cirrus variable. Signed-off-by: Richard Fitzgerald Message-ID: <20250909113039.922065-6-rf@opensource.cirrus.com> Signed-off-by: Mark Brown --- sound/soc/codecs/cs-amp-lib-test.c | 5 ++-- sound/soc/codecs/cs-amp-lib.c | 37 ++++++++++++++++++++++++------ 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/cs-amp-lib-test.c b/sound/soc/codecs/cs-amp-lib-test.c index e7492afa041e..c090498cbf78 100644 --- a/sound/soc/codecs/cs-amp-lib-test.c +++ b/sound/soc/codecs/cs-amp-lib-test.c @@ -204,8 +204,9 @@ static efi_status_t cs_amp_lib_test_get_efi_variable(efi_char16_t *name, KUNIT_EXPECT_NOT_ERR_OR_NULL(test, guid); KUNIT_EXPECT_NOT_ERR_OR_NULL(test, size); - KUNIT_EXPECT_MEMEQ(test, name, expected_name, sizeof(expected_name)); - KUNIT_EXPECT_MEMEQ(test, guid, &expected_guid, sizeof(expected_guid)); + if (memcmp(name, expected_name, sizeof(expected_name)) || + efi_guidcmp(*guid, expected_guid)) + return -EFI_NOT_FOUND; if (!buf) { *size = priv->cal_blob->size; diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c index 9b51d056d863..8434d5196107 100644 --- a/sound/soc/codecs/cs-amp-lib.c +++ b/sound/soc/codecs/cs-amp-lib.c @@ -28,6 +28,24 @@ #define HP_SPEAKER_ID_EFI_GUID \ EFI_GUID(0xc49593a4, 0xd099, 0x419b, 0xa2, 0xc3, 0x67, 0xe9, 0x80, 0xe6, 0x1d, 0x1e) +#define HP_CALIBRATION_EFI_NAME L"SmartAmpCalibrationData" +#define HP_CALIBRATION_EFI_GUID \ + EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93) + +static const struct cs_amp_lib_cal_efivar { + efi_char16_t *name; + efi_guid_t *guid; +} cs_amp_lib_cal_efivars[] = { + { + .name = HP_CALIBRATION_EFI_NAME, + .guid = &HP_CALIBRATION_EFI_GUID, + }, + { + .name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME, + .guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID, + }, +}; + static int cs_amp_write_cal_coeff(struct cs_dsp *dsp, const struct cirrus_amp_cal_controls *controls, const char *ctl_name, u32 val) @@ -146,12 +164,17 @@ static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev) unsigned long data_size = 0; u8 *data; efi_status_t status; - int ret; + int i, ret; + + /* Find EFI variable and get size */ + for (i = 0; i < ARRAY_SIZE(cs_amp_lib_cal_efivars); i++) { + status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name, + cs_amp_lib_cal_efivars[i].guid, + &data_size, NULL); + if (status == EFI_BUFFER_TOO_SMALL) + break; + } - /* Get real size of UEFI variable */ - status = cs_amp_get_efi_variable(CIRRUS_LOGIC_CALIBRATION_EFI_NAME, - &CIRRUS_LOGIC_CALIBRATION_EFI_GUID, - &data_size, NULL); if (status != EFI_BUFFER_TOO_SMALL) return ERR_PTR(-ENOENT); @@ -165,8 +188,8 @@ static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev) if (!data) return ERR_PTR(-ENOMEM); - status = cs_amp_get_efi_variable(CIRRUS_LOGIC_CALIBRATION_EFI_NAME, - &CIRRUS_LOGIC_CALIBRATION_EFI_GUID, + status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name, + cs_amp_lib_cal_efivars[i].guid, &data_size, data); if (status != EFI_SUCCESS) { ret = -EINVAL; From e5b4ad2183f7ab18aaf7c73a120d17241ee58e97 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 9 Sep 2025 12:30:39 +0100 Subject: [PATCH 6/6] ASoC: cs-amp-lib-test: Add test for getting cal data from HP EFI Add a test case that cs_amp_get_efi_calibration_data() returns data when it is in the HP-specific EFI variable. This uses redirected implementation of cs_amp_get_efi_variable() that only returns data if the passes name and GUID match the HP EFI variable. A simple test case installs this function hook and then checks that calibration data is returned. Signed-off-by: Richard Fitzgerald Message-ID: <20250909113039.922065-7-rf@opensource.cirrus.com> Signed-off-by: Mark Brown --- sound/soc/codecs/cs-amp-lib-test.c | 51 ++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/sound/soc/codecs/cs-amp-lib-test.c b/sound/soc/codecs/cs-amp-lib-test.c index c090498cbf78..2fde84309338 100644 --- a/sound/soc/codecs/cs-amp-lib-test.c +++ b/sound/soc/codecs/cs-amp-lib-test.c @@ -220,6 +220,56 @@ static efi_status_t cs_amp_lib_test_get_efi_variable(efi_char16_t *name, return EFI_SUCCESS; } +static efi_status_t cs_amp_lib_test_get_hp_cal_efi_variable(efi_char16_t *name, + efi_guid_t *guid, + unsigned long *size, + void *buf) +{ + static const efi_char16_t expected_name[] = L"SmartAmpCalibrationData"; + static const efi_guid_t expected_guid = + EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93); + struct kunit *test = kunit_get_current_test(); + struct cs_amp_lib_test_priv *priv = test->priv; + + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, name); + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, guid); + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, size); + + if (memcmp(name, expected_name, sizeof(expected_name)) || + efi_guidcmp(*guid, expected_guid)) + return -EFI_NOT_FOUND; + + if (!buf) { + *size = priv->cal_blob->size; + return EFI_BUFFER_TOO_SMALL; + } + + KUNIT_ASSERT_GE_MSG(test, ksize(buf), priv->cal_blob->size, "Buffer to small"); + + memcpy(buf, priv->cal_blob, priv->cal_blob->size); + + return EFI_SUCCESS; +} + +/* Get cal data block from HP variable. */ +static void cs_amp_lib_test_get_hp_efi_cal(struct kunit *test) +{ + struct cs_amp_lib_test_priv *priv = test->priv; + struct cirrus_amp_cal_data result_data; + int ret; + + cs_amp_lib_test_init_dummy_cal_blob(test, 2); + + kunit_activate_static_stub(test, + cs_amp_test_hooks->get_efi_variable, + cs_amp_lib_test_get_hp_cal_efi_variable); + + ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data); + KUNIT_EXPECT_EQ(test, ret, 0); + + KUNIT_EXPECT_MEMEQ(test, &result_data, &priv->cal_blob->data[0], sizeof(result_data)); +} + /* Get cal data block for a given amp, matched by target UID. */ static void cs_amp_lib_test_get_efi_cal_by_uid_test(struct kunit *test) { @@ -910,6 +960,7 @@ static struct kunit_case cs_amp_lib_test_cases[] = { KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test), KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_no_index_test), KUNIT_CASE(cs_amp_lib_test_get_efi_cal_zero_not_matched_test), + KUNIT_CASE(cs_amp_lib_test_get_hp_efi_cal), KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_uid_test, cs_amp_lib_test_get_cal_gen_params), KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_unchecked_test,