mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 00:22:00 +02:00
ASoC: cs35l56: Add support for new Dell laptops
Merge series from Richard Fitzgerald <rf@opensource.cirrus.com>: On new Dell models the driver must read a UEFI variable to get a variant identifier for the audio hardware. Without this, the driver cannot know which firmware file to load to the amps.
This commit is contained in:
commit
be9d8d9664
|
|
@ -58,6 +58,9 @@ int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_
|
|||
int cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps,
|
||||
const struct cirrus_amp_cal_data *in_data);
|
||||
int cs_amp_get_vendor_spkid(struct device *dev);
|
||||
const char *cs_amp_devm_get_vendor_specific_variant_id(struct device *dev,
|
||||
int ssid_vendor,
|
||||
int ssid_device);
|
||||
struct dentry *cs_amp_create_debugfs(struct device *dev);
|
||||
|
||||
static inline u64 cs_amp_cal_target_u64(const struct cirrus_amp_cal_data *data)
|
||||
|
|
|
|||
|
|
@ -786,9 +786,14 @@ config SND_SOC_CROS_EC_CODEC
|
|||
config SND_SOC_CS_AMP_LIB
|
||||
tristate
|
||||
|
||||
config SND_SOC_CS_AMP_LIB_TEST_HOOKS
|
||||
bool
|
||||
depends on SND_SOC_CS_AMP_LIB
|
||||
|
||||
config SND_SOC_CS_AMP_LIB_TEST
|
||||
tristate "KUnit test for Cirrus Logic cs-amp-lib" if !KUNIT_ALL_TESTS
|
||||
depends on SND_SOC_CS_AMP_LIB && KUNIT
|
||||
select SND_SOC_CS_AMP_LIB_TEST_HOOKS
|
||||
default KUNIT_ALL_TESTS
|
||||
help
|
||||
This builds KUnit tests for the Cirrus Logic common
|
||||
|
|
@ -928,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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/pci_ids.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/random.h>
|
||||
#include <sound/cs-amp-lib.h>
|
||||
|
|
@ -56,6 +57,8 @@ struct cs_amp_lib_test_ctl_write_entry {
|
|||
struct cs_amp_lib_test_param {
|
||||
int num_amps;
|
||||
int amp_index;
|
||||
char *vendor_sysid;
|
||||
char *expected_sysid;
|
||||
};
|
||||
|
||||
static struct cirrus_amp_efi_data *cs_amp_lib_test_cal_blob_dup(struct kunit *test)
|
||||
|
|
@ -2305,6 +2308,98 @@ static void cs_amp_lib_test_spkid_hp_31(struct kunit *test)
|
|||
KUNIT_EXPECT_EQ(test, 1, cs_amp_get_vendor_spkid(dev));
|
||||
}
|
||||
|
||||
static efi_status_t cs_amp_lib_test_get_efi_vendor_sysid(efi_char16_t *name,
|
||||
efi_guid_t *guid,
|
||||
u32 *returned_attr,
|
||||
unsigned long *size,
|
||||
void *buf)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
const struct cs_amp_lib_test_param *param = test->param_value;
|
||||
unsigned int len;
|
||||
|
||||
KUNIT_ASSERT_NOT_NULL(test, param->vendor_sysid);
|
||||
len = strlen(param->vendor_sysid);
|
||||
|
||||
if (*size < len) {
|
||||
*size = len;
|
||||
return EFI_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
KUNIT_ASSERT_NOT_NULL(test, buf);
|
||||
memcpy(buf, param->vendor_sysid, len);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/* Fetch SSIDExV2 string from UEFI */
|
||||
static void cs_amp_lib_test_ssidexv2_fetch(struct kunit *test)
|
||||
{
|
||||
const struct cs_amp_lib_test_param *param = test->param_value;
|
||||
struct cs_amp_lib_test_priv *priv = test->priv;
|
||||
struct device *dev = &priv->amp_dev->dev;
|
||||
const char *got;
|
||||
|
||||
kunit_activate_static_stub(test,
|
||||
cs_amp_test_hooks->get_efi_variable,
|
||||
cs_amp_lib_test_get_efi_vendor_sysid);
|
||||
|
||||
got = cs_amp_devm_get_vendor_specific_variant_id(dev, PCI_VENDOR_ID_DELL, 0xabcd);
|
||||
KUNIT_EXPECT_NOT_ERR_OR_NULL(test, got);
|
||||
KUNIT_EXPECT_STREQ(test, got, param->expected_sysid);
|
||||
}
|
||||
|
||||
/* Invalid SSIDExV2 string should be ignored */
|
||||
static void cs_amp_lib_test_ssidexv2_fetch_invalid(struct kunit *test)
|
||||
{
|
||||
struct cs_amp_lib_test_priv *priv = test->priv;
|
||||
struct device *dev = &priv->amp_dev->dev;
|
||||
const char *got;
|
||||
|
||||
kunit_activate_static_stub(test,
|
||||
cs_amp_test_hooks->get_efi_variable,
|
||||
cs_amp_lib_test_get_efi_vendor_sysid);
|
||||
|
||||
got = cs_amp_devm_get_vendor_specific_variant_id(dev, PCI_VENDOR_ID_DELL, 0xabcd);
|
||||
KUNIT_EXPECT_NOT_NULL(test, got);
|
||||
KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(got), -ENOENT);
|
||||
}
|
||||
|
||||
static void cs_amp_lib_test_ssidexv2_not_dell(struct kunit *test)
|
||||
{
|
||||
struct cs_amp_lib_test_priv *priv = test->priv;
|
||||
struct device *dev = &priv->amp_dev->dev;
|
||||
const char *got;
|
||||
|
||||
kunit_activate_static_stub(test,
|
||||
cs_amp_test_hooks->get_efi_variable,
|
||||
cs_amp_lib_test_get_efi_vendor_sysid);
|
||||
|
||||
/* Not returned if SSID vendor is not Dell */
|
||||
got = cs_amp_devm_get_vendor_specific_variant_id(dev, PCI_VENDOR_ID_CIRRUS, 0xabcd);
|
||||
KUNIT_EXPECT_NOT_NULL(test, got);
|
||||
KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(got), -ENOENT);
|
||||
}
|
||||
|
||||
static void cs_amp_lib_test_vendor_variant_id_not_found(struct kunit *test)
|
||||
{
|
||||
struct cs_amp_lib_test_priv *priv = test->priv;
|
||||
struct device *dev = &priv->amp_dev->dev;
|
||||
const char *got;
|
||||
|
||||
kunit_activate_static_stub(test,
|
||||
cs_amp_test_hooks->get_efi_variable,
|
||||
cs_amp_lib_test_get_efi_variable_none);
|
||||
|
||||
got = cs_amp_devm_get_vendor_specific_variant_id(dev, PCI_VENDOR_ID_DELL, 0xabcd);
|
||||
KUNIT_EXPECT_NOT_NULL(test, got);
|
||||
KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(got), -ENOENT);
|
||||
|
||||
got = cs_amp_devm_get_vendor_specific_variant_id(dev, -1, -1);
|
||||
KUNIT_EXPECT_NOT_NULL(test, got);
|
||||
KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(got), -ENOENT);
|
||||
}
|
||||
|
||||
static int cs_amp_lib_test_case_init(struct kunit *test)
|
||||
{
|
||||
struct cs_amp_lib_test_priv *priv;
|
||||
|
|
@ -2375,6 +2470,71 @@ static void cs_amp_lib_test_get_cal_param_desc(const struct cs_amp_lib_test_para
|
|||
KUNIT_ARRAY_PARAM(cs_amp_lib_test_get_cal, cs_amp_lib_test_get_cal_param_cases,
|
||||
cs_amp_lib_test_get_cal_param_desc);
|
||||
|
||||
static const struct cs_amp_lib_test_param cs_amp_lib_test_ssidexv2_param_cases[] = {
|
||||
{ .vendor_sysid = "abcd_00", .expected_sysid = "00" },
|
||||
{ .vendor_sysid = "abcd_01", .expected_sysid = "01" },
|
||||
{ .vendor_sysid = "abcd_XY", .expected_sysid = "XY" },
|
||||
|
||||
{ .vendor_sysid = "1028abcd_00", .expected_sysid = "00" },
|
||||
{ .vendor_sysid = "1028abcd_01", .expected_sysid = "01" },
|
||||
{ .vendor_sysid = "1028abcd_XY", .expected_sysid = "XY" },
|
||||
|
||||
{ .vendor_sysid = "abcd_00_WF", .expected_sysid = "00" },
|
||||
{ .vendor_sysid = "abcd_01_WF", .expected_sysid = "01" },
|
||||
{ .vendor_sysid = "abcd_XY_WF", .expected_sysid = "XY" },
|
||||
|
||||
{ .vendor_sysid = "1028abcd_00_WF", .expected_sysid = "00" },
|
||||
{ .vendor_sysid = "1028abcd_01_WF", .expected_sysid = "01" },
|
||||
{ .vendor_sysid = "1028abcd_XY_WF", .expected_sysid = "XY" },
|
||||
|
||||
{ .vendor_sysid = "abcd_00_AA_BB", .expected_sysid = "00" },
|
||||
{ .vendor_sysid = "abcd_01_AA_BB", .expected_sysid = "01" },
|
||||
{ .vendor_sysid = "abcd_XY_AA_BB", .expected_sysid = "XY" },
|
||||
|
||||
{ .vendor_sysid = "1028abcd_00_AA_BB", .expected_sysid = "00" },
|
||||
{ .vendor_sysid = "1028abcd_01_AA_BB", .expected_sysid = "01" },
|
||||
{ .vendor_sysid = "1028abcd_XY_A_BB", .expected_sysid = "XY" },
|
||||
};
|
||||
|
||||
static void cs_amp_lib_test_ssidexv2_param_desc(const struct cs_amp_lib_test_param *param,
|
||||
char *desc)
|
||||
{
|
||||
snprintf(desc, KUNIT_PARAM_DESC_SIZE, "vendor_sysid:'%s' expected_sysid:'%s'",
|
||||
param->vendor_sysid, param->expected_sysid);
|
||||
}
|
||||
|
||||
KUNIT_ARRAY_PARAM(cs_amp_lib_test_ssidexv2, cs_amp_lib_test_ssidexv2_param_cases,
|
||||
cs_amp_lib_test_ssidexv2_param_desc);
|
||||
|
||||
static const struct cs_amp_lib_test_param cs_amp_lib_test_ssidexv2_invalid_param_cases[] = {
|
||||
{ .vendor_sysid = "abcd" },
|
||||
{ .vendor_sysid = "abcd_0" },
|
||||
{ .vendor_sysid = "abcd_1" },
|
||||
{ .vendor_sysid = "abcd_0_1" },
|
||||
{ .vendor_sysid = "abcd_1_1" },
|
||||
{ .vendor_sysid = "abcd_1_X" },
|
||||
{ .vendor_sysid = "abcd_1_X" },
|
||||
{ .vendor_sysid = "abcd_000" },
|
||||
{ .vendor_sysid = "abcd_010" },
|
||||
{ .vendor_sysid = "abcd_000_01" },
|
||||
{ .vendor_sysid = "abcd_000_01" },
|
||||
|
||||
{ .vendor_sysid = "1234abcd" },
|
||||
{ .vendor_sysid = "1234abcd_0" },
|
||||
{ .vendor_sysid = "1234abcd_1" },
|
||||
{ .vendor_sysid = "1234abcd_0_1" },
|
||||
{ .vendor_sysid = "1234abcd_1_1" },
|
||||
{ .vendor_sysid = "1234abcd_1_X" },
|
||||
{ .vendor_sysid = "1234abcd_1_X" },
|
||||
{ .vendor_sysid = "1234abcd_000" },
|
||||
{ .vendor_sysid = "1234abcd_010" },
|
||||
{ .vendor_sysid = "1234abcd_000_01" },
|
||||
{ .vendor_sysid = "1234abcd_000_01" },
|
||||
};
|
||||
|
||||
KUNIT_ARRAY_PARAM(cs_amp_lib_test_ssidexv2_invalid, cs_amp_lib_test_ssidexv2_invalid_param_cases,
|
||||
cs_amp_lib_test_ssidexv2_param_desc);
|
||||
|
||||
static struct kunit_case cs_amp_lib_test_cases[] = {
|
||||
/* Tests for getting calibration data from EFI */
|
||||
KUNIT_CASE(cs_amp_lib_test_cal_data_too_short_test),
|
||||
|
|
@ -2434,6 +2594,15 @@ static struct kunit_case cs_amp_lib_test_cases[] = {
|
|||
KUNIT_CASE(cs_amp_lib_test_spkid_hp_30),
|
||||
KUNIT_CASE(cs_amp_lib_test_spkid_hp_31),
|
||||
|
||||
/* Test cases for SSIDExV2 */
|
||||
KUNIT_CASE_PARAM(cs_amp_lib_test_ssidexv2_fetch,
|
||||
cs_amp_lib_test_ssidexv2_gen_params),
|
||||
KUNIT_CASE_PARAM(cs_amp_lib_test_ssidexv2_fetch_invalid,
|
||||
cs_amp_lib_test_ssidexv2_invalid_gen_params),
|
||||
KUNIT_CASE_PARAM(cs_amp_lib_test_ssidexv2_not_dell,
|
||||
cs_amp_lib_test_ssidexv2_gen_params),
|
||||
KUNIT_CASE(cs_amp_lib_test_vendor_variant_id_not_found),
|
||||
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/pci_ids.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/timekeeping.h>
|
||||
#include <linux/types.h>
|
||||
|
|
@ -36,6 +37,10 @@
|
|||
#define HP_CALIBRATION_EFI_GUID \
|
||||
EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93)
|
||||
|
||||
#define DELL_SSIDEXV2_EFI_NAME L"SSIDexV2Data"
|
||||
#define DELL_SSIDEXV2_EFI_GUID \
|
||||
EFI_GUID(0x6a5f35df, 0x1432, 0x4656, 0x85, 0x97, 0x31, 0x04, 0xd5, 0xbf, 0x3a, 0xb0)
|
||||
|
||||
static const struct cs_amp_lib_cal_efivar {
|
||||
efi_char16_t *name;
|
||||
efi_guid_t *guid;
|
||||
|
|
@ -206,7 +211,7 @@ int cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
|
|||
const struct cirrus_amp_cal_controls *controls,
|
||||
const struct cirrus_amp_cal_data *data)
|
||||
{
|
||||
if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
|
||||
if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
|
||||
return _cs_amp_write_cal_coeffs(dsp, controls, data);
|
||||
else
|
||||
return -ENODEV;
|
||||
|
|
@ -225,7 +230,7 @@ int cs_amp_read_cal_coeffs(struct cs_dsp *dsp,
|
|||
const struct cirrus_amp_cal_controls *controls,
|
||||
struct cirrus_amp_cal_data *data)
|
||||
{
|
||||
if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
|
||||
if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
|
||||
return _cs_amp_read_cal_coeffs(dsp, controls, data);
|
||||
else
|
||||
return -ENODEV;
|
||||
|
|
@ -244,10 +249,7 @@ int cs_amp_write_ambient_temp(struct cs_dsp *dsp,
|
|||
const struct cirrus_amp_cal_controls *controls,
|
||||
u32 temp)
|
||||
{
|
||||
if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
|
||||
return cs_amp_write_cal_coeff(dsp, controls, controls->ambient, temp);
|
||||
else
|
||||
return -ENODEV;
|
||||
return cs_amp_write_cal_coeff(dsp, controls, controls->ambient, temp);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_amp_write_ambient_temp, "SND_SOC_CS_AMP_LIB");
|
||||
|
||||
|
|
@ -304,6 +306,29 @@ static int cs_amp_convert_efi_status(efi_status_t status)
|
|||
}
|
||||
}
|
||||
|
||||
static void *cs_amp_alloc_get_efi_variable(efi_char16_t *name,
|
||||
efi_guid_t *guid,
|
||||
u32 *returned_attr)
|
||||
{
|
||||
efi_status_t status;
|
||||
unsigned long size = 0;
|
||||
|
||||
status = cs_amp_get_efi_variable(name, guid, NULL, &size, NULL);
|
||||
if (status != EFI_BUFFER_TOO_SMALL)
|
||||
return ERR_PTR(cs_amp_convert_efi_status(status));
|
||||
|
||||
/* Over-alloc to ensure strings are always NUL-terminated */
|
||||
void *buf __free(kfree) = kzalloc(size + 1, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
status = cs_amp_get_efi_variable(name, guid, returned_attr, &size, buf);
|
||||
if (status != EFI_SUCCESS)
|
||||
return ERR_PTR(cs_amp_convert_efi_status(status));
|
||||
|
||||
return_ptr(buf);
|
||||
}
|
||||
|
||||
static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev,
|
||||
efi_char16_t **name,
|
||||
efi_guid_t **guid,
|
||||
|
|
@ -583,7 +608,7 @@ static int _cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, i
|
|||
int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
|
||||
struct cirrus_amp_cal_data *out_data)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
|
||||
if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
|
||||
return _cs_amp_get_efi_calibration_data(dev, target_uid, amp_index, out_data);
|
||||
else
|
||||
return -ENOENT;
|
||||
|
|
@ -619,7 +644,7 @@ EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, "SND_SOC_CS_AMP_LIB");
|
|||
int cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps,
|
||||
const struct cirrus_amp_cal_data *in_data)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) {
|
||||
if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) {
|
||||
scoped_guard(mutex, &cs_amp_efi_cal_write_lock) {
|
||||
return _cs_amp_set_efi_calibration_data(dev, amp_index,
|
||||
num_amps, in_data);
|
||||
|
|
@ -692,7 +717,7 @@ 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))
|
||||
!IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
|
||||
return -ENOENT;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cs_amp_spkid_byte_types); i++) {
|
||||
|
|
@ -705,6 +730,92 @@ int cs_amp_get_vendor_spkid(struct device *dev)
|
|||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_amp_get_vendor_spkid, "SND_SOC_CS_AMP_LIB");
|
||||
|
||||
static const char *cs_amp_devm_get_dell_ssidex(struct device *dev,
|
||||
int ssid_vendor, int ssid_device)
|
||||
{
|
||||
unsigned int hex_prefix;
|
||||
char audio_id[4];
|
||||
char delim;
|
||||
char *p;
|
||||
int ret;
|
||||
|
||||
if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) &&
|
||||
!IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
char *ssidex_buf __free(kfree) = cs_amp_alloc_get_efi_variable(DELL_SSIDEXV2_EFI_NAME,
|
||||
&DELL_SSIDEXV2_EFI_GUID,
|
||||
NULL);
|
||||
ret = PTR_ERR_OR_ZERO(ssidex_buf);
|
||||
if (ret == -ENOENT)
|
||||
return ERR_PTR(-ENOENT);
|
||||
else if (ret < 0)
|
||||
return ssidex_buf;
|
||||
|
||||
/*
|
||||
* SSIDExV2 string is a series of underscore delimited fields.
|
||||
* First field is all or part of the SSID. Second field should be
|
||||
* a 2-character audio hardware id, followed by other identifiers.
|
||||
* Older models did not have the 2-character audio id, so reject
|
||||
* the string if the second field is not 2 characters.
|
||||
*/
|
||||
ret = sscanf(ssidex_buf, "%8x_%2s%c", &hex_prefix, audio_id, &delim);
|
||||
if (ret < 2)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
if ((ret == 3) && (delim != '_'))
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
if (strlen(audio_id) != 2)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
p = devm_kstrdup(dev, audio_id, GFP_KERNEL);
|
||||
if (!p)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* cs_amp_devm_get_vendor_specific_variant_id - get variant ID string
|
||||
* @dev: pointer to struct device
|
||||
* @ssid_vendor: PCI Subsystem Vendor (-1 if unknown)
|
||||
* @ssid_device: PCI Subsystem Device (-1 if unknown)
|
||||
*
|
||||
* Known vendor-specific hardware identifiers are checked and if one is
|
||||
* found its content is returned as a NUL-terminated string. The returned
|
||||
* string is devm-managed.
|
||||
*
|
||||
* The returned string is not guaranteed to be globally unique.
|
||||
* Generally it should be combined with some other qualifier, such as
|
||||
* PCI SSID, to create a globally unique ID.
|
||||
*
|
||||
* If the caller has a PCI SSID it should pass it in @ssid_vendor and
|
||||
* @ssid_device. If the vendor-spefic ID contains this SSID it will be
|
||||
* stripped from the returned string to prevent duplication.
|
||||
*
|
||||
* If the caller does not have a PCI SSID, pass -1 for @ssid_vendor and
|
||||
* @ssid_device.
|
||||
*
|
||||
* Return:
|
||||
* * a pointer to a devm-managed string
|
||||
* * ERR_PTR(-ENOENT) if no vendor-specific qualifier
|
||||
* * ERR_PTR error value
|
||||
*/
|
||||
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);
|
||||
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_amp_devm_get_vendor_specific_variant_id, "SND_SOC_CS_AMP_LIB");
|
||||
|
||||
/**
|
||||
* cs_amp_create_debugfs - create a debugfs directory for a device
|
||||
*
|
||||
|
|
@ -738,7 +849,7 @@ static const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = {
|
|||
};
|
||||
|
||||
const struct cs_amp_test_hooks * const cs_amp_test_hooks =
|
||||
PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST), &cs_amp_test_hook_ptrs);
|
||||
PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS), &cs_amp_test_hook_ptrs);
|
||||
EXPORT_SYMBOL_NS_GPL(cs_amp_test_hooks, "SND_SOC_CS_AMP_LIB");
|
||||
|
||||
MODULE_DESCRIPTION("Cirrus Logic amplifier library");
|
||||
|
|
|
|||
365
sound/soc/codecs/cs35l56-test.c
Normal file
365
sound/soc/codecs/cs35l56-test.c
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
// 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");
|
||||
|
|
@ -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,33 +1109,75 @@ 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;
|
||||
int ret;
|
||||
|
||||
if (cs35l56->dsp.fwf_suffix)
|
||||
return 0;
|
||||
|
||||
if (!cs35l56->sdw_peripheral)
|
||||
return 0;
|
||||
if (cs35l56->sdw_peripheral) {
|
||||
cs35l56->dsp.fwf_suffix = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL,
|
||||
"l%uu%u",
|
||||
cs35l56->sdw_link_num,
|
||||
cs35l56->sdw_unique_id);
|
||||
if (!cs35l56->dsp.fwf_suffix)
|
||||
return -ENOMEM;
|
||||
|
||||
cs35l56->dsp.fwf_suffix = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL,
|
||||
"l%uu%u",
|
||||
cs35l56->sdw_link_num,
|
||||
cs35l56->sdw_unique_id);
|
||||
if (!cs35l56->dsp.fwf_suffix)
|
||||
return -ENOMEM;
|
||||
/*
|
||||
* There are published firmware files for L56 B0 silicon using
|
||||
* the ALSA prefix as the filename suffix. Default to trying these
|
||||
* first, with the new SoundWire suffix as a fallback.
|
||||
* None of these older systems use a vendor-specific ID.
|
||||
*/
|
||||
if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev == 0xb0)) {
|
||||
cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix;
|
||||
cs35l56->dsp.fwf_suffix = cs35l56->component->name_prefix;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* There are published firmware files for L56 B0 silicon using
|
||||
* the ALSA prefix as the filename suffix. Default to trying these
|
||||
* first, with the new name as an alternate.
|
||||
* Some manufacturers use the same SSID on multiple products and have
|
||||
* a vendor-specific qualifier to distinguish different models.
|
||||
* Models with the same SSID but different qualifier might require
|
||||
* different audio firmware, or they might all have the same audio
|
||||
* firmware.
|
||||
* Try searching for a firmware with this qualifier first, else
|
||||
* fallback to standard naming.
|
||||
*/
|
||||
if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev == 0xb0)) {
|
||||
cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix;
|
||||
cs35l56->dsp.fwf_suffix = cs35l56->component->name_prefix;
|
||||
if (snd_soc_card_get_pci_ssid(cs35l56->component->card, &vendor, &device) < 0) {
|
||||
vendor_id = cs_amp_devm_get_vendor_specific_variant_id(cs35l56->base.dev, -1, -1);
|
||||
} else {
|
||||
vendor_id = cs_amp_devm_get_vendor_specific_variant_id(cs35l56->base.dev,
|
||||
vendor, device);
|
||||
}
|
||||
ret = PTR_ERR_OR_ZERO(vendor_id);
|
||||
if (ret == -ENOENT)
|
||||
return 0;
|
||||
else if (ret)
|
||||
return ret;
|
||||
|
||||
if (vendor_id) {
|
||||
if (cs35l56->dsp.fwf_suffix)
|
||||
cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix;
|
||||
else
|
||||
cs35l56->fallback_fw_suffix = cs35l56->component->name_prefix;
|
||||
|
||||
cs35l56->dsp.fwf_suffix = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL,
|
||||
"%s-%s",
|
||||
vendor_id,
|
||||
cs35l56->fallback_fw_suffix);
|
||||
if (!cs35l56->dsp.fwf_suffix)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(cs35l56_set_fw_suffix);
|
||||
|
||||
static int cs35l56_component_probe(struct snd_soc_component *component)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -74,4 +74,8 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56);
|
|||
int cs35l56_init(struct cs35l56_private *cs35l56);
|
||||
void cs35l56_remove(struct cs35l56_private *cs35l56);
|
||||
|
||||
#if IS_ENABLED(CONFIG_KUNIT)
|
||||
int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56);
|
||||
#endif
|
||||
|
||||
#endif /* ifndef CS35L56_H */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user